なになれ

IT系のことを記録していきます

AWS Lambdaのコンテナイメージサポートを使用したCI/CD環境の構築

現在では、AWS Lambdaの実行環境にコンテナイメージが利用できます。

aws.amazon.com

これにより、ECSやEKSといったコンテナ系のサービスだけでなく、Lambdaでもコンテナネイティブな開発環境を作ることができます。
自分の環境では、ECSやEKSとLambdaを併用してシステムを構成することが多いので、開発環境を統一できることにはメリットがあります。
今回はLambdaの場合、どのような開発ワークフローとなるのか、CI/CD環境を構築して確認してみます。
あくまでCI/CDの一例ですので、参考にしてもらえればと思います。
なお、LambdaなどのAWSリソースの作成には、Terraformを利用します。

成果物はこちらです。
github.com

概要

今回の開発ワークフローの概要です。
GitHubリポジトリへのコミットをトリガーにして、CodeBuildを起動します。
CodeBuildでは、リポジトリ内のDockerfileの構成に従い、コンテナイメージを作成します。
コンテナイメージをECRにPushします。
Lambda関数の更新にECRのコンテナイメージを指定します。

CI/CDの構成

f:id:hi1280:20210615220545p:plain

環境構築

コンテナイメージの作成

初回にLambda関数を作成する際には、コンテナイメージが必要です。
そのため、以下の手順でコンテナイメージを用意します。

$ FUNCTION_NAME=aws-lambda-container-cicd-example
$ REGION=$(aws configure get region)
$ ACCOUNTID=$(aws sts get-caller-identity --output text --query Account)
$ docker build -t ${FUNCTION_NAME} .
$ aws ecr create-repository --repository-name ${FUNCTION_NAME}
$ docker tag ${FUNCTION_NAME}:latest ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/${FUNCTION_NAME}:latest
$ aws ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com
$ docker push ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/${FUNCTION_NAME}:latest

ECRにコンテナイメージをPushする際の標準的なやり方です。
一時的にイメージを作りたいだけなので、イメージタグにlatestを使うことを許容しています。

CodeBuildの作成

CI/CDの基盤となるCodeBuildを用意します。

Terraformのtfファイルを一部抜粋したものです。
詳細は成果物を確認ください。

...
resource "aws_codebuild_project" "aws_lambda_container_cicd_example" {
  name         = local.function_name
  service_role = aws_iam_role.iam_for_codebuild.arn
  artifacts {
    type = "NO_ARTIFACTS"
  }
  cache {
    modes = [
      "LOCAL_DOCKER_LAYER_CACHE",
    ]
    type = "LOCAL"
  }
  environment {
    compute_type    = "BUILD_GENERAL1_SMALL"
    image           = "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
    type            = "LINUX_CONTAINER"
    privileged_mode = true
  }
  source {
    git_clone_depth     = 1
    insecure_ssl        = false
    location            = "https://github.com/hi1280/${local.function_name}"
    report_build_status = false
    type                = "GITHUB"
    git_submodules_config {
      fetch_submodules = false
    }
  }
}
...

ビルドの処理内容を決めるbuildspec.yamlです。
./scripts_build.shに処理を委譲しています。

version: 0.2
env:
  variables:
    REPOSITORY_NAME: aws-lambda-container-cicd-example
phases:
  build:
    commands:
      - ./scripts/build.sh

./scripts/build.sh

#!/bin/sh

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${REPOSITORY_NAME}
IMAGE_TAG=$(echo "${CODEBUILD_RESOLVED_SOURCE_VERSION}" | cut -c 1-7)

# build
aws ecr get-login-password --region "${AWS_DEFAULT_REGION}" | docker login --username AWS --password-stdin "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
docker build -t "${REPOSITORY_URI}:${IMAGE_TAG}" .
docker tag "${REPOSITORY_URI}:${IMAGE_TAG}" "${REPOSITORY_URI}:latest"
docker push "${REPOSITORY_URI}:${IMAGE_TAG}"
docker push "${REPOSITORY_URI}:latest"

# deploy
aws lambda update-function-code --function-name "${REPOSITORY_NAME}" --image-uri "${REPOSITORY_URI}:${IMAGE_TAG}" --publish

CODEBUILD_RESOLVED_SOURCE_VERSIONには、GitのコミットIDが入ります。コミットIDの一部をイメージタグに指定します。
ビルド部分とデプロイ部分に分けています。
ビルド部分は、ECRにコンテナイメージをPushする際の標準的なやり方です。
デプロイ部分はAWS CLIでLambdaを更新する処理を行います。
--publishを指定することで、バージョンごとにLambdaの内容が履歴に残るように更新しています。

Lambdaの作成

以下のtfファイルでLambdaを作成します。

...
resource "aws_lambda_function" "aws_lambda_container_cicd_example" {
  function_name = local.function_name
  role          = aws_iam_role.iam_for_lambda.arn
  package_type  = "Image"
  image_uri     = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/${local.function_name}:latest"
  lifecycle {
    ignore_changes = [
      image_uri
    ]
  }
}

package_typeでImageを指定することで、コンテナイメージを使ってLambdaを作成することになります。
image_uriはLambdaの更新ごとに変更されるため、ignore_changesを指定し、Terraformのチェックからは除外します。

まとめ

Lambdaにコンテナイメージがサポートされる前までは、効率的にLambdaを開発するために各種ツールを駆使しながら各自が工夫して、開発フローを作っていたかと思います。
コンテナイメージがサポートされたことにより、個人的にはそのあたりが明確になり、開発フローが分かりやすくなったと思います。

参考

aws.amazon.com