Terraformを使ったLambda関数のコンテナイメージ構築

# はじめに

AWS Lambdaは、サーバーレスでイベント駆動型のコンピューティングサービスです。従来のLambda関数ではzipファイルでコードをアップロードしていましたが、コンテナイメージを使用することで、より柔軟で強力な開発・デプロイが可能になります。
本記事では、Infrastructure as CodeツールであるTerraformを用いて、Lambda関数として実行するコンテナイメージを構築し、AWS環境へデプロイする手順を解説します。

制作したレポジトリはこちらです。

https://github.com/sugiyama404/practice_lambda_docker

参考にしたサイト

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-image.html#python-image-instructions

# 目的

Lambda関数をコンテナイメージで実行することにより、柔軟性とパフォーマンスを向上させることを目指します。

# インフラ構成

![aws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/bf371686-55a4-771c-b691-22d38eb1f5f9.png)


以下のAWSサービスを用いてインフラを構築します。
+ **ECR**: Dockerイメージを保存するプライベートなレジストリ
+ **Lambda**: サーバーレスでコードを実行するサービス
+ **IAM**: AWSリソースへのアクセスを制御するサービス

# PythonファイルをLambdaにアップロードする場合との比較: メリットとデメリット

従来のzipファイルアップロードと比較した、コンテナイメージを使用するメリットとデメリットは以下の点が挙げられます。

## メリット

+ **一貫性と依存関係の管理**: コンテナイメージは依存関係や環境設定を含む一貫した実行環境を提供
+ **パフォーマンス**: コンテナイメージは迅速にデプロイおよびスケール可能
+ **柔軟性**: カスタムランタイムや特定のOSライブラリが必要な場合にも対応可能

## デメリット

+ **複雑さ**: コンテナのビルドと管理が複雑
+ **ビルド時間**: コンテナイメージのビルドに時間がかかることがある
+ **コスト**: コンテナレジストリの使用には追加コストが発生
+ **ファイルの確認**: Lambda管理画面で個別のファイルを確認できない

# 実装方法

# IAM

IAMロールはLambda関数がAWSリソースにアクセスするための許可を管理します。

```terraform:infra/modules/iam/aws_iam_role.tf
resource "aws_iam_role" "lambda_role" {
  name = "lambda_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "lambda.amazonaws.com",
        },
      },
    ],
  })

  tags = {
    Name = "${var.app_name}-lambda-iam-role"
  }
}
```

IAMポリシーはLambda関数が必要とする権限を指定します。

```terraform:infra/modules/iam/aws_iam_policy.tf
resource "aws_iam_policy" "lambda_policy" {
  name = "lambda_policy"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "ecr:*"
        ],
        Resource = "*",
        Effect   = "Allow"
      }
    ]
  })
}
```

IAMポリシーをIAMロールにアタッチして、ポリシーの権限をロールに委任します。

```terraform:infra/modules/iam/aws_iam_policy_attachment.tf
resource "aws_iam_policy_attachment" "lambda_role_attachment" {
  name       = "${var.app_name}-lambda-attach"
  roles      = ["${aws_iam_role.lambda_role.name}"]
  policy_arn = aws_iam_policy.lambda_policy.arn
}
```

# Lambda

Lambda関数をTerraformで定義する際には、以下のような設定を行います。

```terraform:infra/modules/lambda/aws_lambda_function.tf
resource "aws_lambda_function" "main" {
  function_name = var.lambda_function_name
  package_type  = "Image"
  image_uri     = "${var.lambda_repository_url}:latest"
  description   = "lambda_function"
  role          = var.lambda_iam_role
  publish       = true
  memory_size   = 128
  timeout       = 30
  depends_on    = [aws_cloudwatch_log_group.lambda]

  tags = {
    Name = "${var.app_name}-lamdba"
  }
}
```

Lambda関数のログを保存するために、CloudWatchロググループを設定します。

```terraform:infra/modules/lambda/aws_cloudwatch_log_group.tf
resource "aws_cloudwatch_log_group" "lambda" {
  name = "/aws/lambda/${var.lambda_function_name}"
}
```

## Docker for Lambda

Lambda関数を実行するためのDockerfileを以下のように作成します。

```Dockerfile:infra/modules/lambda/src/Dockerfile
FROM public.ecr.aws/lambda/python:3.12

# Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# Install the specified packages
RUN pip install --no-cache-dir -r requirements.txt

# Copy function code
COPY lambda_function.py ${LAMBDA_TASK_ROOT}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "lambda_function.handler" ]
```

pythonのライブラリーをインストールします。

```Dockerfile:infra/modules/lambda/src/requirements.txt
boto3
```

不要なファイルやディレクトリを除外する.dockerignoreファイルを設定します。

```text:infra/modules/lambda/src/.dockerignore
# 一般的な除外ルール
.DS_Store
.AppleDouble
.LSOverride
Icon
._*

# ファイルシステム関連
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# iCloud関連
*.icloud

# Xcode関連
*.xcodeproj
xcuserdata/
build/
DerivedData/

# その他
*.symlink
*.localized
*.vmx
*.vmdk

# Dockerとgitの除外ルール
.git
*Dockerfile*
*docker-compose*
```

実際のLambda関数のコードは以下のようになります。

```Python:infra/modules/lambda/src/lambda_function.py
import sys
def handler(event, context):
    return 'Hello from AWS Lambda using Python' + sys.version + '!'
```

この設定により、Dockerを使用してLambda関数のコンテナイメージをビルドし、AWS Lambdaで実行できる準備が整います。Lambda関数の依存関係や実装を容易に管理し、柔軟性とパフォーマンスを向上させることができます。

# 結果

以下のテスト結果になりました。

![log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/b2e5eaac-8a22-697a-4e32-72011f663947.png)


# まとめ

Terraformを使用してLambda関数のコンテナイメージを構築することで、AWS上でのアプリケーションの柔軟性と効率を大幅に向上させることができます。本記事で紹介した手順に従うことで、よりスケーラブルで高性能なサーバーレスアーキテクチャを実現できるでしょう。