【FastAPIサーバーレス】Github更新でLambdaを自動更新させるテンプレート

# 初めに

FastAPIを使いつつサーバーレスをAWSで構築します。
Githubの更新にActionsを利用して自動でLambdaへの反映を行うテンプレートを作成しました。

テンプレートレポジトリ: https://github.com/foasho/fastapi-lambda

## 動作手順

- レポジトリをダウンロード
- Local上でのAPIテストとPyTestの実行
- githubにPushし、Actionsを確認する
- IAMでAPIキーを取得し、GithubのRepository Secretsに設定
- Lambda作成とS3を作成後、main.ymlの編集
- APIGateway作成
- 接続確認

## ディレクトリ構成

```commandline
<your_project>
    |- .github/workflows/main.yml
    |- pytests
        |- __init__.py
         -  test_main.py

    |- .gitignore
    |- main.py
    |- localrun.py
    |- requirements.txt
```

# さっそく実装

## 1. レポジトリをダウンロード

git依存は自身のレポジトリでやるためにZIPでダウンロードするのが良いと思います。
リンク先: https://github.com/foasho/fastapi-lambda

コマンドでやる場合は以下
```commandline
git clone https://github.com/foasho/fastapi-lambda.git
```

プロジェクト直下に置いた際には、自身のレポジトリ（プライベート）を作成してください。

## 2. ローカルホストで実行とテスト実行
ローカルホストで実行
```commandline
pip install -r requirements.txt
python localrun.py

>> INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

http://127.0.0.1:8000
につなぎ、"Success"と表示されればをOKです。

テスト実行
```commandline
pytest -s
```

## 3. GithubにPushし、Actionsを確認

自身のレポジトリ欄Actionsの”CI/CD Pipeline”を確認し、
最新のコミット名のものをクリックすると、Actions時のビルドの失敗成功およびエラーログを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/3b83a143-2436-fb35-1ed4-f29fbb982228.png)


continous-integrationは成功し、continuous-deploymentは失敗するかと思います。
continuous-deploymentを成功させるには、
IAMでAPIキー作成後、AWS側でLambdaの作成と、S3のバケット作成をしなければなりません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/c3f676c0-8cd1-720f-ab60-92fda5b180a1.png)


## 4. IAMでAPIキーを取得し、GithubのRepository Secretsに設定
※今回のリージョンはオハイオ(us-east-2)を使用を想定で記述しています。



1. AWSにアクセスし、IAMページに遷移
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/5cdd2e1c-49dd-194d-7b3d-60a43e024370.png)


2. 新しいユーザーを作成し、ポリシーを追加。
ここでは新しいユーザーをgithub-action-lambdaとしています。
「ポリシーを直接アタッチする」より"AmazonS3FullAccess"と"AWSLambda_FullAccess"を選択し、『次へ』。
※FullAccessはそこそこ大きい権限なので、気になる方は絞った方がいいかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/e984e8fc-18c4-3a83-4027-78ce622e9c0c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/3406815e-2b9a-7292-e12b-3e98d4d7c846.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/a6bf8768-8804-7972-885f-5fdf8e5c006d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/134ec2ea-9602-4633-26b0-87a0169eb817.png)


作成後、IAM -> ユーザー -> github-action-lambda(ユーザー名) -> 「セキュリティ認証情報」からアクセスキーを生成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/374cbf74-45b7-4152-f4bc-a14aa366040b.png)

作成後、アクセスキーとシークレットキーが画面に表示されるので、
どこかにコピーしてメモしておきましょう。
※絶対に誰かに見られないように厳重に管理してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/85afa1bb-dd8a-eb6b-0361-292c62328995.png)

Githubの自分のレポジトリに戻り、「Settings」からSecurityの"Secrets and variables"のActionsより、
New repositiory secretで先ほど入手したアクセスキーをAWS_ACCESS_KEY_IDとして、
シークレットキーをAWS_SECRET_ACCESS_KEYとして入力してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/9b8925ea-9df0-4731-01a4-4423131bfd19.png)


リージョンは, オハイオの場合はus-east-2なので、
AWS_DEFAULT_REGIONにus-east-2を入力してください。


## 5. S3の作成とLambdaの作成

S3の作成
S3にアクセスし、「バケットを作成」
任意のバケットを選択し、作成ボタンを押すだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/ed7da91b-1d70-b5a7-c23c-c5302b73b4aa.png)



Lambdaの作成
Lambdaにアクセスし、「関数を作成」から、
任意の関数名を作成し、ランタイムはPython3.8を選択しましょう。
アーキテクチャはx86_64のままでOKです。あとはそのまま作成するだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/3b8bca08-021e-bd23-1588-2e4e70e2cdff.png)


作成後、ランタイム設定の編集ボタンを押し、ハンドラを「main.handler」に設定して保存ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/5b03d0cc-ec3b-b14e-baf8-0a5bfa8cd787.png)


## 6. main.ymlの編集

main.ymlの87行目と95行目を
自分の作成した関数名およびバケット名に変更して、プッシュしましょう。

```yaml
name: CI/CD Pipeline

on:
  push:
    # "master"は自身のブランチを選択して入力
    branches: [ master ]

jobs:
  continous-integration:
    runs-on: ubuntu-latest

    steps:
      # Step1
      - uses: actions/checkout@v2

      # Step2 Pythonのセットアップ (Pythonは3.8)
      - name: Python Setup
        uses: actions/setup-python@v2
        with:
          python-version: 3.8
          architecture: x64

      # Step3　venv環境のインストール
      - name: Install Python Virtual Env
        run: pip3 install virtualenv

      # Step4
      - name: Setup Virtual env
        uses: actions/cache@v2
        id: cache-venv
        with:
          path: venv
          key: ${{ runner.os }}-venv-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-venv-

      # Step5 venvに依存ライブラリのインストール
      - name: Activate and Install Depencies into Virtual env
        run: python -m venv venv && source venv/bin/activate && pip3 install -r requirements.txt
        if: steps.cache-venv.outputs.cache-hit != 'true'

      # Step6 作成した実行環境でPytestを行う
      - name: Activate venv and Run Test
        run: . venv/bin/activate && pytest

      # Step7　依存関係をZIPファイル化
      - name: Create Zipfile archive of Dependencies
        run: |
          cd ./venv/lib/python3.8/site-packages
          zip -r9 ../../../../temp.zip .

      # Step8 アプリケーションをZipファイル化
      - name: Add App to Zip file
        run: unzip temp.zip && zip -g api.zip -r .

      # Step9 GithubActionのストレージにapi.zipとしてアップロード
      - name: Upload zip file artifact
        uses: actions/upload-artifact@v2
        with:
          name: api
          path: api.zip

  # api.zipをs3にPutする
  continuous-deployment:
    runs-on: ubuntu-latest
    needs: [continous-integration]
    # refs/heads/masterの"master"部分は自身のブランチを選択して変更
    if: github.ref == 'refs/heads/master'
    steps:
      # Step 1
      - name: Install AWS CLI
        uses: unfor19/install-aws-cli-action@v1
        with:
          version: 1
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
      # Step 2
      - name: Download Lambda api.zip
        uses: actions/download-artifact@v2
        with:
          name: api
      # Step 3
      - name: Upload to S3
        # <YourS3Bucket>の部分は自身のS3のバケット名を入れる
        run: aws s3 cp api.zip s3://<YourS3Bucket>/api.zip
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
      # Step 4
      - name: Deploy new Lambda
        # # <YourLambdaName>の部分は自身の作成したLambda名を入れる, <YourS3Bucket>の部分は自身のS3のバケット名を入れる
        run: aws lambda update-function-code --function-name <YourLambdaName> --s3-bucket <YourS3Bucket> --s3-key api.zip
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
```

プッシュ後、デプロイが完了してるか確認できます。
完了していれば、S3に指定したバケットの直下にapi.zipが作成され、
Lambdaにも反映がされているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/2e02031d-a083-49d1-d519-739326d90ba7.png)


## 6. APIGatewayの作成
API Gatewayのサービスページにアクセスし、
APIタイプ選択から、RESTAPIで「構築」をします。

新しいAPIを選択し、自由にAPI名を入れましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/d2455740-99eb-92d8-9178-7b1687800dad.png)



上部の「アクション」から「メソッドの作成」
→Anyを選択

ANYセットアップを以下のように行ってください
- Lambda関数を選択
- Lambdaプロキシ統合の使用にチェック
- Lambda関数に関数名を記入
- 右下部の「保存」ボタンをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/63995994-d388-649a-47d8-2f5066ec5286.png)


次に、アクションより「リソースの作成」を選択
最上部のチェックボックスにチェックを入れると、
自動でproxy設定を入力してくれるので、そのまま「リソースの作成」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/7e8d5dab-980c-9726-af21-836e0874b1c4.png)


最後に、アクションから、「APIのデプロイ」を選択し、以下を設定してください
- 「新しいステージ」
- ステージ名：任意（例: main）
「デプロイ」で完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493888/54d561ff-1eac-6cc3-c63c-bc8008e1e14b.png)


ステージページからURLが表示されるので、そのURLにアクセスし、
"Success!"と自分のFastAPIのResponseがかえってこれば成功です。


お疲れ様でした。