0
0
お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

AWS LambdaにGo言語のスクリプトをDockerイメージでデプロイする [Terraformを使用]

Last updated at Posted at 2024-07-16

要約

最終的なソースコード全体は以下のリンクからご確認いただけます。

関連記事

Go言語で記述したスクリプトを、AWSのLambdaにTerraformを用いてデプロイする機会があり、その過程の試行錯誤を整理して一連の記事にまとめました。

この記事はそのうちの2本目として、Dockerイメージを用いて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で実行するようにしています。

./docker/Dockerfile
################## ビルドステージ ##################
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にアップロードしたイメージを消しましょう。

./terraform/ecr.tf
locals {
  ecr_repository_name = "test-lambda"  # FIXME
}

resource "aws_ecr_repository" "test_lambda" {
  name = local.ecr_repository_name
}

変数の用意

変数は以下の様になっています。今回も、デプロイイメージのハッシュ値(ダイジェスト)を保持しておくためにS3を利用しているため、各自のS3のバケット名が必要になります。

terraform/lambda.tf(一部)
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へアップロードしています。

terraform/lambda.tf(一部)
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"としています。また、runtimehandlerを指定するとエラーになったため記述を省いています。

terraform/lambda.tf(一部)
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")
}
terraform/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イメージを使う方が好きかなーといった印象でした。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0