GitHub Actionsを使ってコンテナ版AWS Lambdaにデプロイしてみた

GitHub Actionsを使い、コンテナイメージをAmazon ECRにプッシュし、AWS Lambdaにデプロイする方法を紹介します。 GitHubからAWSへの認証では、OpenID Connect(OIDC)を利用します。
2021.12.09

本ブログでは、GitHub Actionsを使い、main ブランチへの push をトリガーにコンテナイメージをビルドしてコンテナレジストリのAmazon ECRにプッシュし、AWS Lambdaにコンテナイメージをデプロイする方法を紹介します。

実質的にやっていることは、GitHub の ECS 向けドキュメントをベースに、以下の変更を加えています。

  • GitHub から AWS への認証に、IAMアクセスキーの代わりに OpenID Connect(OIDC)を利用
  • デプロイ先をAmazon ECSからAWS Lambdaへ変更

大前提として、デプロイのゴールはLambda関数のコンテナイメージを更新することにフォーカスしており、Lambda関数の作成、設定変更、Lambda関数を呼び出すリソースのデプロイは本記事のスコープ外です。

0. GitHub レポジトリにアプリケーションを用意

以下のような階層のLambda関数用レポジトリを用意します。

$ tree -a -I '.git'
.
├── .github
│   └── workflows
│       └── aws-lambda-deploy.yml
├── Dockerfile
└── app.py

app.py

import sys
def handler(event, context):
    return 'Hello from AWS Lambda using Python' + sys.version + '!'

Dockerfile

FROM public.ecr.aws/lambda/python:3.9
COPY app.py ${LAMBDA_TASK_ROOT}
CMD [ "app.handler" ]

.github/workflows/aws-lambda-deploy.yml が GitHub Actionsのワークフロー用設定ファイルです。 具体的な内容は ステップ4 で解説しています。

1. Amazon ECR レポジトリの作成

コンテナレジストリ Amazon ECR にレポジトリを作成します。

2. AWS Lambda 関数の作成

ウォークスルー手順簡易化のため、コンテナ版AWS Lambda関数を事前に作成します。

さらに、コンテナイメージURIには既存の URI を指定しておきます。

3. AWS側で OIDC 設定

GitHubからAWSリソースを操作する際に、OIDCで取得した一時トークンを利用します。

AWS側では

  • アイデンティティプロバイダー
  • GitHubのOIDCがAssumeするIAMロール

を作成します。

これらリソースは各AWSアカウントごとに一つ用意します。

参考 : Configuring OpenID Connect in Amazon Web Services - GitHub Docs

アイデンティティプロバイダー

IAM→アイデンティティプロバイダーから作成します。

以下を指定します。

  • Provider type : OpenID Connect
  • Provider URL : https://token.actions.githubusercontent.com
  • Audience : sts.amazonaws.com

作成されたリソースの ARN を控えます。

IAM ロール

GitHub アクション aws-actions/configure-aws-credentials の GitHub ページに、IAM ロールを作成するCloudFormationテンプレートが用意されています。

このテンプレートを流して、リソース作成します。

スタック作成時に以下を指定します。

  • GitHub 組織名
  • 先程作成したOIDCプロバイダーのARN
  • GitHub レポジトリ名

なお、現時点の CloudFormation テンプレートは以下の通りです。

Parameters:
  GitHubOrg:
    Type: String
  RepositoryName:
    Type: String
  OIDCProviderArn:
    Description: Arn for the GitHub OIDC Provider.
    Default: ""
    Type: String

Conditions:
  CreateOIDCProvider: !Equals 
    - !Ref OIDCProviderArn
    - ""

Resources:
  Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !If 
                - CreateOIDCProvider
                - !Ref GithubOidc
                - !Ref OIDCProviderArn
            Condition:
              StringLike:
                token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*

  GithubOidc:
    Type: AWS::IAM::OIDCProvider
    Condition: CreateOIDCProvider
    Properties:
      Url: https://token.actions.githubusercontent.com
      ClientIdList: 
        - sts.amazonaws.com
      ThumbprintList:
        - a031c46782e6e6c662c2c87c76da9aa62ccabd8e

Outputs:
  Role:
    Value: !GetAtt Role.Arn

作成された IAMロールには、ポリシーを追加します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ECR",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Lambda",
            "Effect": "Allow",
            "Action": "lambda:UpdateFunctionCode",
            "Resource": "*"
        }
    ]
}

許可するActionは要件に合わせて調整してください。

4. GitHub Actions ワークフローを作成

Lambda関数のレポジトリに Lambda デプロイ用のワークフローを追加します。

ファイル冒頭の env ブロックで定義している変数を環境に合わせて調整してください。

.github/workflows/aws-lambda-deploy.yml

name: Deploy container image to AWS Lambda
on:
  push:
    branches:
     - main
env:
  AWS_ACCOUNT_ID: MY_ACCOUNT_ID
  AWS_ROLE_NAME: MY_ROLE_NAME
  AWS_ROLE_SESSION_NAME: MY_ROLE_SESSION_NAME
  AWS_REGION: us-west-1
  ECR_REPOSITORY: MY_REPOSITORY_NAME
  LAMBDA_FUNCTION_NAME: MY_FUNCTION_NAME

permissions:
  id-token: write
  contents: read
jobs:
  aws-deploy:
    name: Push to ECR
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@master
        with:
          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.AWS_ROLE_NAME }}
          role-session-name: ${{ env.AWS_ROLE_SESSION_NAME }}
          aws-region: ${{ env.AWS_REGION }}

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

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to Lambda.
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          #  Update Lambda configuration
          aws lambda update-function-code --function-name $LAMBDA_FUNCTION_NAME --image-uri $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

ポイントは以下です。

  • アクション aws-actions/configure-aws-credentials でOIDCトークンを取得
  • アクション aws-actions/amazon-ecr-login で ECR にログイン
  • Gitのコミットハッシュ値をイメージタグ(IMAGE_TAG)に利用

最後のステップでは API aws lambda update-function-code を直接呼び出してLambda関数のイメージだけを更新しています。

AWS CDK 等を利用すると、Lambda 関数を使ったサービス全般のデプロイが可能です。

5. 動作確認

GitHub の main ブランチに更新が走ると、今回登録したワークフローが実行されることを確認してください。

GitHub 側でワークフローの実行が成功し、AWS 側で以下を確認できればOKです。

  • ECR に新しいイメージのPUSHされている
  • Lambda 関数のコンテナイメージが最新のものに変更されている

最後に

AWS Lambdaのコンテナイメージ対応により、CI/CDパイプラインを構築しやすくなりました。

本ブログでは、その一例として、GitHub ActionsとOpenID Connect(OIDC)を利用し、mainブランチへの更新をトリガーに、コンテナLambdaへデプロイする方法を紹介しました。

お使いのLambdaにピッタリなCI/CD環境を構築し、Lambdaの開発サイクルを加速させちゃいましょう!

参考