[AWS] コンテナイメージをGitHub ActionsでLambdaへ自動デプロイ

# 概要
2020年12月、AWS Lambdaがコンテナイメージのデプロイをサポートしました。
そこで、Lambdaの流行に乗り遅れてはいけないと感じ、コンテナイメージを自動でLambdaへデプロイできるような設定についてを記述しました。
本記事が、LambdaのCI/CDを達成するための助けとなれば幸いです。

## 必要なもの
* AWSアカウントとECRリポジトリ
* Lambda関数
   * 本記事はLambda関数は作成されていることを前提としています。
* GitHubアカウントとリポジトリ

# 手順
まず、以下の権限が付与されているIAMユーザーを作成します。
以下の変数を代入します。

| 変数名 | 変数の詳細
| --- | ---
| `<AWS Region>` | AWSリージョン
| `<AWS Account ID>` | AWSアカウントID
| `<AWS ECR Repository Name>` | AWS ECRリポジトリ名
| `<AWS Lambda Function Name>` | AWS Lambda関数名

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "GetAuthorizationToken",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
            "Sid": "ManageRepositoryContents",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:BatchGetImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage"
            ],
            "Resource": "arn:aws:<AWS Region>:<AWS Account ID>:repository/<AWS ECR Repository Name>"
        },
        {
            "Sid": "LambdaAccess",
            "Effect": "Allow",
            "Action": "lambda:*",
            "Resource": "arn:aws:lambda:<AWS Region>:<AWS Account ID>:function:<AWS Lambda Function Name>"
        }
    ]
}
```

IAMユーザーを作成すると、Access Key IDとSecret Access Keyを取得できます。これらの値は後ほど使用するため、控えておきます。

次に、GitHub Secretsに以下の変数を登録します。

| 変数名 | 変数の詳細
| --- | ---
| `AWS_ACCESS_KEY_ID` | AWS Access Key ID
| `AWS_SECRET_ACCESS_KEY` | AWS Secret Access Key
| `AWS_ACCOUNT_ID` | AWSアカウントID
| `AWS_ECR_REPOSITORY_NAME` | AWS ECRリポジトリ名
| `AWS_LAMBDA_FUNCTION_NAME` | AWS Lambda関数名
| `AWS_REGION` | AWSリージョン

次に、LambdaにデプロイするためのPythonスクリプトと、Dockerfileを用意します。

```python
def main(event, context):
    return 'Hello World!'
```

```Dockerfile
FROM public.ecr.aws/lambda/python:3.8

ADD main.py ${LAMBDA_TASK_ROOT}

CMD [ "main.main" ]
```

次に、リポジトリ直下に`.github/workflows/lambda-test.yml`を作成し、以下のように記述します（[参考](https://dev.classmethod.jp/articles/github-action-ecr-push/)）。

```yml
name: ECR Push & Lambda Update

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 3

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: docker build & docker push & lambda update
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
          aws lambda update-function-code --function-name $AWS_LAMBDA_FUNCTION_NAME --image-uri $ECR_REGISTRY/$ECR_REPOSITORY:latest
        env:
          AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
          AWS_LAMBDA_FUNCTION_NAME: ${{ secrets.AWS_LAMBDA_FUNCTION_NAME }}
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPOSITORY_NAME }}
```

上記のワークフローを説明すると、

```yml
      - name: docker build & docker push & lambda update
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
          aws lambda update-function-code --function-name $AWS_LAMBDA_FUNCTION_NAME --image-uri $ECR_REGISTRY/$ECR_REPOSITORY:latest
```
という部分で、Dockerfileをビルドし、ビルドしたイメージをECRにプッシュ、そしてプッシュしたイメージをLambdaに更新という形でデプロイしています。

以上の準備を終えたあと、mainブランチにプッシュしてください。Lambdaのスクリプトが更新されているかと思います。

# 終わりに
GitHub Actionsを用いてLambdaを自動デプロイする設定を記述しました。
CloudFormationを用いた自動デプロイ方法についての記事もいつか書きたいですね。

## 参考文献
* [クラスメソッドさんの記事](https://dev.classmethod.jp/articles/github-action-ecr-push/)
  * ECRへのプッシュについてはこちらを参考にさせていただきました。