【Terraform】Lambdaをコンテナイメージでデプロイする

## 概要
コンテナが主流になりつつある現在！

Lambdaを使用する際もコンテナで動かすことで、メリットがあるのではないかと
個人的に考えるようになりました。

今回はメリット・デメリットを整理しつつ、
Terraformを使ったハンズオン手順も紹介します。

## 1. Lambdaをコンテナイメージでデプロイするメリット
### ランタイム制限からの解放
Lambdaの公式サポートする言語・バージョンに依存せず、
独自のランタイムやライブラリを使える。

:::note info
 #### 例えば・・
 最新のPythonやNode.js、Goのバージョンを自由に選べたりで
 IaCとも相性が良かったりします。
:::

### IaC（Infrastructure as Code）との親和性
ソースコードをTerraformやCloudFormationに直接埋め込むのではなく、
ECRイメージURIを指定するだけでよい！

:::note info
大量のコードをIaCツールで管理する煩雑さが減る
:::

### 開発環境の再現性が高い
Dockerコンテナでローカル動作確認ができ、本番環境と同じ環境で動作させられる。

:::note info
「ローカルで動くのに本番で動かない」問題が減る
:::

### 依存関係管理の簡素化
ライブラリやバイナリの依存関係をイメージにまとめてしまうので、
Lambdaレイヤーなどの管理が不要。

## 2. Lambdaをコンテナイメージでデプロイするデメリット
### イメージサイズの増加
コンテナイメージは通常のZIPパッケージより大きくなりがちです。
起動時間（コールドスタート）に影響が出る場合もあります。

### イメージ管理コスト
ECRにイメージをPush・管理する手間やストレージコストが増える。

### Lambdaのコード更新がやや複雑
ソースコード変更 → イメージ再ビルド → ECR Push → Lambda更新 のフローになります。

### Lambda特有の簡易さが薄れる
ZIP方式に比べて準備が増えるため、シンプルな関数向けでは過剰な場合も。。

## 3. ハンズオン手順（Terraform＋Docker）
:::note warn
#### 事前準備
- AWS CLI設定済み
- Terraformインストール済み
- Dockerインストール済み
:::


### 3-1. pythonでLambda関数を実装

```app.py
def handler(event, context):
    return {
        'statusCode': 200,
        'body': 'Hello from container!'
    }
```

### 3-2. Dockerfileの作成

dockerfileを作成します。
※app.py はLambdaハンドラ関数を定義したPythonファイルです。

```dockerfile
FROM public.ecr.aws/lambda/python:3.12

# 必要なファイルをコピー
COPY app.py ${LAMBDA_TASK_ROOT}

# ハンドラを指定
CMD ["app.lambda_handler"]
```

#### ローカルでビルド・テスト例
ローカルで動かしたい場合はこんな感じでどうぞ！

```bash
# ビルド
docker build -t lambda-container:latest .

# ローカルで実行テスト
docker run -p 9000:8080 lambda-container:latest

# 別ターミナルから
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
```

### 3-3. Dockerイメージのビルド＆ECRにPush

AWS ECR を使用して、作成した Docker イメージをアップロードします。

#### ECR にリポジトリを作成

AWSマネジメントコンソールにログインし、ECRにリポジトリを作成しましょう。
名称は任意で大丈夫です！

![スクリーンショット 2025-04-29 15.22.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/e23b8a53-6ee0-4bab-b597-277c5e1c5052.png)


作成したら、画面右上に「プッシュコマンドを表示」ボタンがあります。
これを押下すると、何とECRにPushするところまで方法を案内してくれます！

![スクリーンショット 2025-04-29 15.22.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/945342/d62754b4-87b6-421f-8940-0ebd968f0f6b.png)

### 3-4. Terraform設定例（main.tf）

```css
.
├── main.tf
├── variables.tf
```

```main.tf
provider "aws" {
  region = "ap-northeast-1"
}

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

resource "aws_lambda_function" "container_lambda" {
  function_name = "lambda-container-example"
  package_type  = "Image"
  image_uri     = var.image_uri
  role          = aws_iam_role.lambda_exec_role.arn
  timeout       = 10
}

resource "aws_iam_role_policy_attachment" "lambda_logs" {
  role       = aws_iam_role.lambda_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
```

```variables.tf
variable "image_uri" {
  description = "ECR Image URI"
  type        = string
}
```

:::note info
main.tfの「image_uri」は環境変数として用意いただいてもいいですし、
「terraform.tfvars」ファイルを用意してもいいです。

例：
```
image_uri = "<your-ecr-url>/lambda-container-example:latest"
```
:::

### 3-5. Terraform実行例
```bash
terraform init
terraform apply
```

デプロイ完了後、マネジメントコンソールやAWS CLIからテストできます：

```bash
aws lambda invoke --function-name lambda-container-example response.json
cat response.json
```

## よくあるトラブル
個人的に引っかかったり、よくありそうなエラー(LambdaやTerraform)を
チェックリストで記載しておきますので参考にどうぞ！

### 1. IAM Role関連
- IAM Roleがすでに存在して重複エラーになっていないか？
- assume_role_policy のJSONに誤字（例: Statemanet → Statement）はないか？
- 実行ロールにCloudWatch LogsやECR関連の権限を付与しているか？

### 2. ECRイメージ関連
- ECRに正しくイメージがpushされているか？タグは間違っていないか？
- image_uriに正しいURI（例: account-id.dkr.ecr.region.amazonaws.com/repo:tag）がセットされているか？
- Lambdaのimage_uriに環境変数（TF_VAR_image_uri）を使う場合、terraform apply時に変数が正しく渡されているか？

### 3. Terraform設定関連
- 変数参照の書き方（var.image_uriなど）に誤りはないか？
- terraform.tfvarsファイルの値と環境変数設定の整合性は取れているか？
- tfファイルのsyntaxやリソース構成が正しいか？
- 設定変更後にterraform planで内容を必ず確認しているか？

### 4. Lambdaイメージの中身関連
- DockerfileでLambda用エントリポイントが正しく設定されているか？（ENTRYPOINT ["/lambda-entrypoint.sh"]など）
 - /lambda-entrypoint.shに実行権限（chmod +x）があるか？
 - イメージのベースはAWSが推奨しているものか？（amazon/aws-lambda-python:3.12など）
 - イメージが大きすぎて起動に時間がかかりすぎていないか？

### 5. Lambdaデプロイのトラブル
- Lambda作成時にcode.s3Bucketやs3Keyが空になっていないか？（コンテナイメージ指定時はこれら不要）
- Lambdaのログ（CloudWatch Logs）を必ずチェックし、エラー内容を把握しているか？
- LambdaとECRのリージョンが一致しているか？

### 6. その他運用面
- TerraformのStateファイルは適切に管理しているか？
- 環境変数やSecretsはGitHub ActionsのSecretsなど安全な方法で管理しているか？
- CI/CDパイプラインの中で terraform init → terraform plan → terraform apply の流れが確立されているか？

## まとめ
Lambdaコンテナイメージは運用面と開発の自由度が大きく向上します。
特に大規模プロジェクトや複雑な依存関係の管理に効果的です。

ただし初期設定やCI/CDパイプライン構築の手間が増えるため、
小規模・単純な関数では従来のZIPデプロイも検討するのもありかと！