コンテナイメージを使用したLambdaのTerraformテンプレートを考えてみた

コンテナイメージを使用したLambdaのTerraformテンプレートを考えてみた

Clock Icon2025.03.31

こんにちは!コンサルティング部のくろすけです!
コンテナイメージを使用したLambdaについて、Terraformでスマートに構築できないかなと以前から思っておりました。
現状で思い至ったTerraformテンプレートについて、記事にしたいと思います。

経緯

実はあまり覚えてないんですが、下記のような経緯だったと思います。

  • なぜTerraform?
    • IaCでLambda周りのインフラを再利用したい
    • IaCにおいて個人的に好きなTerraformで
  • なぜコンテナイメージを使用したLambda?
    • zipを使用したLambda(以降zipLambda)の250MB制限厳しいな...
    • ECSでコンテナをすでに使ってるし、Lambdaもコンテナにしたら保守のキャパが空くかな...

今回のメインではないですが、下記についても考慮しています。

  • ECRリポジトリも同一Terraformテンプレートで管理
    • ただし、下記を満たすためにコンテナLambdaの初期イメージにはダミーのECRリポジトリを指定
      • コンテナLambdaの構築には、イメージURLの指定が必須
      • 同一Terraformテンプレートで作成したECRには作成直後はイメージが空
  • CI/CDの追加拡張も想定(というよりほぼ前提(今回はスコープ外))
    • アプリのデプロイは基本的にCI/CDで行う
    • アプリの更新による差分ではTerraformは更新しない
  • TerraformでのイメージのBuild・Pushはしない
    • local-exec Provisionarを使用することで、Terraformテンプレート内でイメージのBuild・Pushが可能
    • が、Terraformコマンド実行の際にDockerコマンド等に依存することになるため不採用

前提

使用バージョン

バージョン
Terraform 1.10.4
Terraform AWS Provider v5.87.0

構成

構築のスコープは下記の通りになります。

  • Lambda
  • ECR
  • IAM Role
  • CloudWatch Logs

krsk-terraform-lambda-container-20250327-1.png

経緯にも記載しましたが、今回のポイントは下記です。

  • ECRリポジトリも同一Terraformテンプレートで管理
  • CI/CDの追加拡張も想定(というよりほぼ前提(今回はスコープ外))
  • TerraformでのイメージのBuild・Pushはしない

やってみる

ディレクトリ構成

├── services
│   └── lambda
│        └── ${ENV}
│             ├── main.tf
│             ├── provider.tf
│             ├── terraform.tfvars
│             └── variables.tf
└── modules
    └── lambda
        ├── main.tf
        ├── outputs.tf
        └── variables.tf

Terraformテンプレート

services/lambda/main.tf
module "lambda" {
  source                    = "../../../modules/lambda"
  ecr_repository_name       = "${var.sys_name}-${var.env}-repository"
  dummy_ecr_repository_name = "dummy-${var.env}-repository"
  dummy_ecr_repository_tag  = "latest"
  lambda_function_name      = "${var.sys_name}-${var.env}-lambda"
  lambda_memory_size        = 128
  lambda_timeout            = 3
  lambda_environment = {
    ENV = var.env
  }
  lambda_alias_name  = var.env
  lambda_role_name   = "${var.sys_name}-${var.env}-lambda-role"
  lambda_policy_name = "${var.sys_name}-${var.env}-lambda-policy"
  env                = var.env
}
services/lambda/variables.tf
variable "sys_name" {
  description = "The name of the System"
  type        = string
}

variable "env" {
  description = "The environment for the VPC"
  type        = string
  default     = "dev"
}
modules/lambda/main.tf
################################################################################
# Common                                                                       #
################################################################################
data "aws_caller_identity" "self" {}
data "aws_region" "self" {}

################################################################################
# ECR                                                                          #
################################################################################
resource "aws_ecr_repository" "lambda" {
  name                 = var.ecr_repository_name
  image_tag_mutability = "IMMUTABLE"

  tags = {
    Name = var.ecr_repository_name
  }
}  # アプリケーション用のECRを作成する(こちらはダミーではない)

################################################################################
# Lambda                                                                       #
################################################################################
resource "aws_lambda_function" "lambda" {
  function_name = var.lambda_function_name
  architectures = ["arm64"]
  package_type  = "Image"
  image_uri = (
    "${data.aws_caller_identity.self.account_id}.dkr.ecr.${data.aws_region.self.name
    }.amazonaws.com/${var.dummy_ecr_repository_name}:${var.dummy_ecr_repository_tag}"
  )  # ダミーのイメージURIを指定
  role    = aws_iam_role.lambda.arn
  publish = true

  memory_size = var.lambda_memory_size
  timeout     = var.lambda_timeout

  lifecycle {
    ignore_changes = [
      image_uri, last_modified
    ]  # アプリケーションの更新は無視する(イメージURI等の変更を無視する)
  }

  environment {
    variables = var.lambda_environment
  }

  tracing_config {
    mode = "Active"
  }

  depends_on = [
    aws_cloudwatch_log_group.lambda
  ]

  tags = {
    Name = var.lambda_function_name
  }
}

resource "aws_lambda_alias" "lambda" {
  name             = var.env
  function_name    = aws_lambda_function.lambda.arn
  function_version = aws_lambda_function.lambda.version

  lifecycle {
    ignore_changes = [
      function_version
    ]  # アプリケーションの更新は無視する(イメージURI等の変更を無視する)
  }
}

################################################################################
# IAM Role for Lambda                                                          #
################################################################################
resource "aws_iam_role" "lambda" {
  name               = var.lambda_role_name
  assume_role_policy = data.aws_iam_policy_document.lambda_principal.json

  tags = {
    Name = var.lambda_role_name
  }
}

data "aws_iam_policy_document" "lambda_principal" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "lambda.amazonaws.com",
      ]
    }
  }
}

resource "aws_iam_role_policy_attachment" "lambda" {
  role       = aws_iam_role.lambda.name
  policy_arn = aws_iam_policy.lambda.arn
}

resource "aws_iam_policy" "lambda" {
  name   = var.lambda_policy_name
  policy = jsonencode(var.lambda_policy_document)

  tags = {
    Name = var.lambda_policy_name
  }
}

################################################################################
# CloudWatch Logs                                                              #
################################################################################
resource "aws_cloudwatch_log_group" "lambda" {
  name              = "/aws/lambda/${var.lambda_function_name}"
  retention_in_days = var.cloudwatch_loggrp_retention_in_days

  tags = {
    Name = "/aws/lambda/${var.lambda_function_name}"
  }
}

modules/lambda/variables.tf
variable "ecr_repository_name" {
  description = "The name of the ECR repository"
  type        = string
}

variable "dummy_ecr_repository_name" {
  description = "The name of the dummy ECR repository"
  type        = string
}

variable "dummy_ecr_repository_tag" {
  description = "The tag for the ECR dummy repository"
  type        = string
  default     = "latest"
}

variable "lambda_function_name" {
  description = "The name of the Lambda function"
  type        = string
}

variable "lambda_memory_size" {
  description = "The amount of memory for the Lambda function"
  type        = number
}

variable "lambda_timeout" {
  description = "The timeout for the Lambda function"
  type        = number
}

variable "lambda_environment" {
  description = "The environment variables for the Lambda function"
  type        = object({})
  default = {
    ENV = "pvt1"
  }
}

variable "lambda_alias_name" {
  description = "The name of the Lambda function alias"
  type        = string
}

variable "lambda_role_name" {
  description = "The name of the IAM role for the Lambda function"
  type        = string
}

variable "lambda_policy_name" {
  description = "The name of the IAM policy for the Lambda function"
  type        = string
}

variable "lambda_policy_document" {
  description = "The IAM policy document for the Lambda function"
  type = object({
    Version = string
    Statement = list(object({
      Effect   = string
      Action   = list(string)
      Resource = list(string)
    }))
  })
  default = {
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
        ],
        Resource = [
          "arn:aws:logs:*:*:*",
        ]
      }
    ]
  }
}

variable "cloudwatch_loggrp_retention_in_days" {
  description = "The retention period for the CloudWatch log group"
  type        = number
  default     = 3
}

variable "env" {
  description = "The environment"
  type        = string
}

結果

Warningが出ていますが、意図する挙動に対するものなので無視して問題ありません。
CleanShot 2025-03-31 at 16.58.13@2x.png

あとがき

あとはCI/CDなどでイメージURIを更新するようにしてあげれば、OKかなと思います!
(これはこれで後でAWS CodePipelineを用いて記事にしようと思います。)

CI/CDもないと全体最適化できているか判定できないですが、明日の自分がやってくれる!はず!

心残りはvariables.tfを作り込んでいないので、Lambdaの柔軟性が低いかなと思っております。(これも明日の自分に任せよう。)

ということでやりたいことは尽きないですが、ひとまず自分なりに考えたコンテナLambdaのTerraformテンプレートの概要は以上になります!それでは!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.