はじめに
コマンドラインツールをインストールして Lambda で実行するため、ECR のコンテナイメージで動作する AWS Lambda を実装し、GitHub Actions からデプロイする機会がありましたので、その手順を記載します。
GitHub Actions から AWS SAM を実行して、AWS Lambda のデプロイを行います。
やること
AWS Lambda へデプロイするプログラムをローカルでテストする環境を構築
準備
準備するファイルとフォルダ構成
VSCode Dev Container を使ってローカルでテストする環境を作成します。
|--.devcontainer | |--devcontainer.json |--.vscode | |--extensions.json | |--launch.json | |--settings.json |--Dockerfile |--lambda | |--app.py | |--requirements.txt
.devcontainer/devcontainer.json
コンテナ内で Python のデバッグができる様に VSCode の拡張機能をインストールする指定を入れています。
Lambda 実行プログラムの配置先である「/var/task」をカウントしています。
{ "name": "lambda_ecr", "build": { "dockerfile": "${localWorkspaceFolder}/Dockerfile", "args": {} }, "runArgs": [ "--name=lambda_ecr", "--volume=${localWorkspaceFolder}/lambda:/var/task", "--volume=${localWorkspaceFolder}/.vscode:/var/task/.vscode" ], "workspaceFolder": "/var/task", "customizations": { "vscode": { "extensions": [ "ms-python.autopep8", "ms-python.python", "ms-python.vscode-pylance", ] } } }
.vscode/extensions.json
コンテナ外で使用する VSCode の拡張機能を定義しています。
{ "recommendations": [ "ms-python.autopep8", "ms-python.python", "ms-python.vscode-pylance", "ms-vscode-remote.remote-containers" ] }
.vscode/launch.json
Docker 内でデバッグ起動する際に使用します。
{ "version": "0.2.0", "configurations": [ { "name": "Python: Lambda", "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/app.py", "console": "integratedTerminal", "args": [] } ] }
.vscode/settings.json
フォーマット定義
{ "editor.tabSize": 4, "editor.renderWhitespace": "all", "terminal.integrated.scrollback": 10000, "extensions.ignoreRecommendations": false, "editor.formatOnSave": true, "editor.formatOnPaste": true, "": { "editor.defaultFormatter": "ms-python.autopep8", "editor.formatOnSave": true }, }
lambda/app.py
Lambdaメイン処理
「python app.py」で実行できる様に、if __name__ == "__main__":
を定義しています。
import json def lambda_handler(event, context): """ Lambdaメイン処理 """ print(json.dumps(event, ensure_ascii=False)) return { 'statusCode': 200, 'body': 'Hello World!', } if __name__ == "__main__": event = {} context = {} print(lambda_handler(event, context))
lambda/requirements.txt
ライブラリインポート用
空のファイルを準備します。
Dockerfile
tar と gzip は、コンテナ内で VSCode の拡張機能をインストールするために必要です。
FROM public.ecr.aws/lambda/python:3.12 RUN set -ex \ && dnf -y update \ && dnf install -y tar gzip COPY lambda/ ${LAMBDA_TASK_ROOT} RUN pip3 install -r requirements.txt ENV \ TZ='Asia/Tokyo' CMD [ "app.lambda_handler" ]
Dev Container起動
左下「><」アイコンをクリックして、「コンテナを再度開く」を選択します。
左メニューの拡張機能アイコンをクリック、「@recommended 」を入力して推奨される拡張機能の一覧を表示して、「ワークスペースのおすすめの拡張機能をインストール」を実行してインストールします。
デバッグ起動
左メニューのデバッグアイコンをクリック、ブレークポイントを設定してデバッグ起動するとブレークポイントで止まることを確認します。
Github Actions からデプロイする設定
GitHub Actions から AWS へアクセスするため GitHub ID プロバイダ作成
GitHub Actions で IAM ロールを利用して AWS へアクセスするために、ID プロバイダを登録します。一時的なクレデンシャルを利用して GitHub Actions から AWS へアクセスするためよりセキュアに接続することができます。
IAM から、アクセス管理 – プロバイダを追加を実行します。
プロバイダの URL:OpneID Connect
プロバイダーの URL: https://token.actions.githubusercontent.com
対象者: sts.amazonaws.com
GitHub Actions 用の IAM ポリシー、IAM ロール作成
IAM で、GitHub Actions 用のIAMポリシーを作成します。下記権限のポリシーをポリシー名「lambda-ecr-deploy-policy」で作成します。
- CloudFormation テンプレートファイルを S3 へ格納するため S3 の権限が必要
- CloudFormation を実行する権限が必要
- Lambda に対する権限が必要
- ECR プッシュする権限が必要
- (この例では必要なかったが)CloudFormation テンプレートファイルで IAM に関する定義がある場合は権限が必要
{ "Version": "2012-10-17", "Statement": [ { "Sid": "0", "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject" ], "Resource": "arn:aws:s3:::lambda-ecr-deploy-{AWSアカウントID}/*" }, { "Sid": "1", "Effect": "Allow", "Action": "cloudformation:*", "Resource": [ "arn:aws:cloudformation:ap-northeast-1:{AWSアカウントID}:stack/lambda-ecr-deploy-stack/*", "arn:aws:cloudformation:ap-northeast-1:aws:transform/Serverless-2016-10-31" ] }, { "Sid": "2", "Effect": "Allow", "Action": "lambda:*", "Resource": [ "arn:aws:lambda:ap-northeast-1:{AWSアカウントID}:function:lambda-ecr-deploy" ] }, { "Sid": "3", "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:GetRepositoryPolicy", "ecr:DescribeRepositories", "ecr:ListImages", "ecr:DescribeImages", "ecr:BatchGetImage", "ecr:GetLifecyclePolicy", "ecr:GetLifecyclePolicyPreview", "ecr:ListTagsForResource", "ecr:DescribeImageScanFindings" ], "Resource": [ "*" ] }, { "Sid": "4", "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload", "ecr:PutImage" ], "Resource": [ "arn:aws:ecr:ap-northeast-1:{AWSアカウントID}:repository/lambda-ecr-deploy-ecr" ] }, { "Sid": "5", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:DetachRolePolicy", "iam:AttachRolePolicy", "iam:GetRole", "iam:DeleteRole", "iam:PassRole" ], "Resource": [ "*" ] } ] }
IAM で、GitHub Actions 用の IAM ロールを作成します。下記権限のポリシーをポリシー名「lambda-ecr-deploy-role」で作成します。
信頼されたエンティティタイプ:カスタム信頼ポリシー
カスタム信頼ポリシー
- IDプロバイダーを使用すること宣言
- 接続するGitHubリポジトリを定義
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::{AWSアカウントID}:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { "token.actions.githubusercontent.com:sub": "repo:{GitHubリポジトリ}:*" } } } ] }
Lambda 用のIAMポリシー、IAMロール作成
IAM で、Lambda 用のIAMポリシーを作成します。下記権限のポリシーをポリシー名「lambda-ecr-deploy-run-policy」で作成します。
- LambdaからCloudWatchへログ出力するのに必要な権限のみ定義
{ "Version": "2012-10-17", "Statement": [ { "Sid": "0", "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" } ] }
IAM で、Lambda 用の IAM ロールを作成します。下記権限のポリシーをポリシー名「lambda-ecr-deploy-run-role」で作成します。
信頼されたエンティティタイプ:AWS のサービス
サービスまたはユースケース:Lambda
ECR リポジトリ作成
ECR 名「lambda-ecr-deploy-ecr」、プライベートな ECR 定義を作成します。
S3 バケット作成
バケット名「lambda-ecr-deploy-{AWSアカウントID}」で作成します。
Lambda 用の CloudFormation テンプレート作成
ファイルを追加して、Lambda 用の CloudFormation テンプレートを作成します。
|--.devcontainer | |--devcontainer.json |--.vscode | |--extensions.json | |--launch.json | |--settings.json |--Dockerfile |--lambda | |--app.py | |--requirements.txt |--lambda-stack.yaml ← ★追加★
lambda-stack.yaml
Lambda 本体の定義のみで、作成済みの IAM ロールと ECR リポジトリを指定しています。ImageConfig\Command で実行する Lambda 関数を指定しています。
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Parameters: Project: Type: String AccountId: Type: String Resources: EcrFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub ${Project} CodeUri: lambda/ Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${Project}-run-role PackageType: Image ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/${Project}-ecr:latest ImageConfig: Command: - "app.lambda_handler"
GitHub Actions 定義作成
ファイルを追加して、GitHub Actions 用のワークフローファイルを作成します。
|--.devcontainer | |--devcontainer.json |--.github | |--workflows | | |--lambda_deploy_dev.yaml ← ★追加★ |--.vscode | |--extensions.json | |--launch.json | |--settings.json |--Dockerfile |--lambda | |--app.py | |--requirements.txt |--lambda-stack.yaml
lambda_deploy_dev.yaml
処理の流れは
- GitHub 資源チェックアウト
- GitHub 用 IAM ロールのセッション受入
- ECR ログイン
- Docker ビルド
- ECR へDocker イメージプッシュ
- AWS SAM コマンドで Lambda デプロイ
- Lambda の新しいイメージをデプロイ実行(ECR へ Docker イメージプッシュしただけでは、Lambda の参照 ECR イメージは最新化されないため、最新化するコマンドを実行する)
name: Lambda Deploy Development on: workflow_dispatch: env: AWS_ACCOUNT_ID: {AWSアカウントID} AWS_IAM_ROLE_ARN: arn:aws:iam::{AWSアカウントID}:role/lambda-ecr-deploy-role AWS_DEFAULT_REGION: ap-northeast-1 PROJECT: lambda-ecr-deploy SAM_CLI_TELEMETRY: 1 jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ env.AWS_IAM_ROLE_ARN }} role-session-name: github-actions-${{ github.run_id }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - run: aws sts get-caller-identity - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Build Docker working-directory: ./ run: | docker build -t ${{ env.PROJECT }}-ecr -f ./Dockerfile . docker tag ${{ env.PROJECT }}-ecr:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${{ env.PROJECT }}-ecr:latest - name: Push Image to Amazon ECR working-directory: ./ run: | docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${{ env.PROJECT }}-ecr:latest - name: ECR Lambda Stack env: STACK_NAME: ${{ env.PROJECT }}-stack TEMPLATE_NAME: lambda-stack.yaml working-directory: ./ run: | sam build --use-container \ --template-file ${TEMPLATE_NAME} sam deploy --no-confirm-changeset \ --stack-name ${STACK_NAME} \ --template-file ${TEMPLATE_NAME} \ --s3-bucket ${{ env.PROJECT }}-${AWS_ACCOUNT_ID} \ --image-repository ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${{ env.PROJECT }}-ecr \ --parameter-overrides Project=${{ env.PROJECT }} AccountId=${AWS_ACCOUNT_ID} \ --capabilities CAPABILITY_IAM \ --no-fail-on-empty-changeset - name: Lambda Renew Image run: | aws lambda update-function-code \ --function-name ${{ env.PROJECT }} \ --image-uri ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${{ env.PROJECT }}-ecr:latest
GitHub Actions 実行
GiHub Actions のリポジトリで、「Actions」タブ押下、左メニューのワークフロー名押下、「Run workflow」押下、該当の Branch を選択して、「Run workflow」押下して、GitHub Actions を実行し、
GiHub Actions が正常終了することを確認します。
Lambda テスト実行
GiHub Actions 正常終了後、Lambda「lambda-ecr-deploy」がデプロイされていることを確認した後に、
イベント名:任意の値
イベントJSON:{}(任意のJSON値)
を入力して「テスト」ボタン押下してテスト実行し、正常に下記が出力されていることを確認します。
{ "statusCode": 200, "body": "Hello World!" }
ECR のコンテナイメージで動作する AWS Lambda を GitHub Actions からデプロイし、テスト実行できるまでできました!