要約
最終的なソースコード全体は以下のリンクからご確認いただけます。
関連記事
Go言語で記述したスクリプトを、AWSのLambdaにTerraformを用いてデプロイする機会があり、その過程の試行錯誤を整理して一連の記事にまとめました。
この記事はそのうちの2本目として、Dockerイメージを用いてLambdaにスクリプトをデプロイしてみたいと思います。
- zipファイルを用いてGo言語の処理をAWS Lambdaにデプロイする
- Dockerイメージを用いてGo言語の処理をAWS Lambdaにデプロイする(この記事)
- EventBridgeからAWS Lambdaを定期実行する処理をデプロイする
背景
以前の記事でAWS Lambdaの処理をzipファイルを用いてデプロイする方法を実施しました。その際に、Lambdaのデプロイ方法は他にDockerイメージを用いた方法もあるという話をしました。
私自身、普段の開発やECSへデプロイする場合などはDockerを使うことが多いので、LambdaのデプロイもDockerで済ませられたら便利だなと思います。そこで、Terraformを用いてDockerのイメージでLambdaをデプロイできるようにしたいと思います。
なお、注意点として、zipファイルをアップロードしてバイナリが直接実行場合と比べて、DockerでLambdaをデプロイした場合、多少実行時間が長くなるようです。これは、皆さんの求める要件に合わせてご検討ください。
実装
フォルダ構成は以下の様になっています。
zipファイルの時と比べて大きく変わったのは、Dockerfile
が追加されたのと、ecr.tf
が追加されています。逆にsrc/*
と、main.tf
は一才変わっていません。
.
├── .gitignore
├── docker
│ └── Dockerfile
├── image_digest.txt
├── src
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── terraform
├── .gitignore
├── .terraform.lock.hcl
├── ecr.tf
├── lambda-assume-role.json
├── lambda.tf
├── main.tf
└── terraform.tfstate
Dockerイメージの用意
AWSの公式のドキュメントを参考に、以下の様にDockerfile
を用意します。私の場合は./docker/Dockerfile
に配置しました。
中身としては標準的なマルチステージビルドの記述です。初めにビルドしてバイナリを用意し、AWSの用意しているLambda用のベースイメージprovided:al2023
で実行するようにしています。
################## ビルドステージ ##################
FROM golang:1.22 as build
WORKDIR /app
COPY ./src/go.mod ./src/go.sum ./
RUN go mod download
COPY ./src .
RUN go build -tags lambda.norpc -o main main.go
################## 実行ステージ ##################
FROM public.ecr.aws/lambda/provided:al2023
COPY --from=build /app/main ./main
ENTRYPOINT [ "./main" ]
DockerイメージをアップロードするECRの設定
DockerイメージでのLambdaへのデプロイは、事前にビルドしたDockerイメージをAWSのECRというリポジトリにアップロードし、そこのURIを指定することで行います。
今回はそのECRのリポジトリ自体も、Terraformによる設定でつくります。リポジトリ名は各自のものに変更してください。
ECRのストレージ容量に対する課金にはご注意ください。使わないときは、ECRにアップロードしたイメージを消しましょう。
locals {
ecr_repository_name = "test-lambda" # FIXME
}
resource "aws_ecr_repository" "test_lambda" {
name = local.ecr_repository_name
}
変数の用意
変数は以下の様になっています。今回も、デプロイイメージのハッシュ値(ダイジェスト)を保持しておくためにS3を利用しているため、各自のS3のバケット名が必要になります。
locals {
s3_bucket = "hoge" # FIXME
s3_key_prefix = "test-lambda"
s3_base_path = "${local.s3_bucket}/${local.s3_key_prefix}"
golang_codedir = "${path.module}/../src"
hash_file_name = "image_digest.txt"
}
イメージのビルドとアップロード
このブロックでは、zip化の時と同様にprovisioner "local-exec"
のブロックを用いてローカルでコマンドを実行し、Dockerイメージのビルド、ECRへのアップロード、ハッシュ値(ダイジェスト)の取得とS3へのアップロードといった処理を実施しています。
イメージのビルドとECRへのプッシュは、一般的なECRのユースケースとも同じであるため説明は省きます。Lambdaにおける実行リソースの置き換えをトリガーするためのハッシュ値には、コンテナのダイジェストを使用しています。
ダイジェストの取得はdocker inspect
コマンドを用いて行い、結果をテキストファイルに保存しS3へアップロードしています。
resource "null_resource" "lambda_build" {
depends_on = [ aws_ecr_repository.test_lambda ]
triggers = {
code_diff = sha256(join("", [
for file in fileset(local.golang_codedir, "*")
: filesha256("${local.golang_codedir}/${file}")
]))
}
# イメージのビルド
provisioner "local-exec" {
command = "cd ${path.module}/.. && docker build . -f docker/Dockerfile --platform linux/amd64 -t ${aws_ecr_repository.test_lambda.repository_url}:latest"
}
# イメージをECRへプッシュ
provisioner "local-exec" {
command = "aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${aws_ecr_repository.test_lambda.repository_url} && docker push ${aws_ecr_repository.test_lambda.repository_url}:latest"
}
# ハッシュの生成
provisioner "local-exec" {
command = "cd ${path.module}/.. && docker inspect --format='{{index .RepoDigests 0}}' ${aws_ecr_repository.test_lambda.repository_url}:latest > ${local.digest_file_name}"
}
# ハッシュのs3へのアップロード
provisioner "local-exec" {
command = "cd ${path.module}/.. && aws s3 cp ${local.digest_file_name} s3://${local.s3_base_path}/${local.digest_file_name} --content-type \"text/plain\""
}
}
Lambda本体の設定
最後にLambda本体の設定です。ロールポリシーなどはzipの時と変わっていません。resource "aws_lambda_function"
の中でimage_uri
にイメージのURIを指定し、package_type
も"Image"
としています。また、runtime
やhandler
を指定するとエラーになったため記述を省いています。
resource "aws_lambda_function" "test_lambda" {
function_name = "test-lambda"
package_type = "Image"
image_uri = "${aws_ecr_repository.test_lambda.repository_url}:latest"
role = aws_iam_role.lambda_role.arn
source_code_hash = base64sha256(data.aws_s3_object.image_hash.body)
}
resource "aws_iam_role" "lambda_role" {
name = "role-for-test_lambda"
assume_role_policy = file("lambda-assume-role.json")
}
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Sid": "",
"Effect": "Allow"
}
]
}
Lambdaのデプロイ
ここまできたら前回と同様にterraform apply
でデプロイします。前回と同じようにLambdaがデプロイされ、実行されているかと思います。
今回は、ECRのような課金がすぐ行われるリソースを使用しているため、使わないときは忘れずにterraform destroy
を実行しておきましょう。
ECRにイメージが残っている場合、terraform destroy
でもECRのリポジトリが削除されない場合があります。その際は手動でECRに残っているイメージを消してから、terraform destroy
を再度実行してください。
終わりに
zip化してデプロイする方法と比べて、ビルドなどのコマンドがDockerfileにまとめられているのは便利だなと感じました。処理の手順もシンプルで、個人的にはzipでデプロイするよりもDockerイメージを使う方が好きかなーといった印象でした。
参考