AWS Lambda のコンテナイメージデプロイ入門(ハンズオン)
概要
AWS Lambda は ZIP パッケージだけでなく、コンテナイメージ(最大 10GB)でもデプロイできます。従来の ZIP 形式(展開後 250MB 上限)では収まらない大きな依存関係や、OS ネイティブなバイナリを含むランタイムを扱いたいときに有効な選択肢です。
本記事では Python を例に、次の流れをハンズオンで通します。
- Lambda 用コンテナイメージを Dockerfile で作る
- Amazon ECR にプッシュする
- そのイメージから Lambda 関数を作成・更新する
- ローカルで動作確認する(Runtime Interface Emulator)
- IaC(AWS CDK / Terraform)での定義例
ZIP とコンテナイメージの使い分け
| 観点 | ZIP パッケージ | コンテナイメージ |
|---|---|---|
| サイズ上限 | 展開後 250MB | 10GB |
| ネイティブ依存 | Lambda Layer で対応 | Dockerfile 内で完結 |
| ローカル再現 | しづらい | RIE でしやすい |
| コールドスタート | 一般に軽い | 大きいイメージほど不利 |
「依存が重い」「OS パッケージを入れたい」「ビルド環境と実行環境を揃えたい」ケースでコンテナイメージが活きます。逆に軽量な関数なら ZIP の方が取り回しが楽です。
環境
- AWS CLI v2
- Docker(24 系で確認)
- Python 3.12(ベースイメージ
public.ecr.aws/lambda/python:3.12) - リージョンは
ap-northeast-1を例に記載
ディレクトリ構成とハンドラ
lambda-container-demo/ ├── Dockerfile ├── requirements.txt └── app.py
# app.py
import json
def handler(event, context):
name = event.get("name", "world")
return {
"statusCode": 200,
"body": json.dumps({"message": f"hello, {name}"}),
}
# requirements.txt requests==2.32.3
Dockerfile
AWS が提供する Lambda ベースイメージを使うのが最も簡単です。ベースイメージには Runtime Interface Client が同梱されており、CMD にハンドラを指定するだけで動きます。
FROM public.ecr.aws/lambda/python:3.12
COPY requirements.txt ${LAMBDA_TASK_ROOT}/
RUN pip install --no-cache-dir -r ${LAMBDA_TASK_ROOT}/requirements.txt
COPY app.py ${LAMBDA_TASK_ROOT}/
# "ファイル名.関数名" を指定
CMD ["app.handler"]
ポイントは CMD に モジュール.関数 を渡すこと。ここが ZIP デプロイでいう「ハンドラ設定」に相当します。
ビルド
Lambda は x86_64 と arm64(Graviton)を選べます。ビルドマシンと実行アーキが違う場合は --platform を明示します。
docker build --platform linux/amd64 -t lambda-container-demo:latest .
ローカルで動作確認(Runtime Interface Emulator)
AWS のベースイメージには Runtime Interface Emulator(RIE)が含まれているため、AWS へ上げる前にローカルで叩けます。
docker run --rm -p 9000:8080 lambda-container-demo:latest
curl -s "http://localhost:9000/2015-03-31/functions/function/invocations" \
-d '{"name":"lambda"}'
# => {"statusCode": 200, "body": "{\"message\": \"hello, lambda\"}"}
期待どおりのレスポンスが返れば、ハンドラ指定とビルドは正しくできています。
ECR へプッシュ
export AWS_REGION=ap-northeast-1
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export REPO=lambda-container-demo
# リポジトリ作成
aws ecr create-repository \
--repository-name "$REPO" \
--image-scanning-configuration scanOnPush=true \
--region "$AWS_REGION"
# ECR ログイン
aws ecr get-login-password --region "$AWS_REGION" \
| docker login --username AWS \
--password-stdin "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
# タグ付けして push
export IMAGE_URI="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO}:latest"
docker tag lambda-container-demo:latest "$IMAGE_URI"
docker push "$IMAGE_URI"
実行ロールの用意
cat > trust-policy.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole" }
]
}
JSON
aws iam create-role \
--role-name lambda-container-demo-role \
--assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy \
--role-name lambda-container-demo-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Lambda 関数を作成
--package-type Image と --code ImageUri=... を指定するのがコンテナデプロイの肝です。
export ROLE_ARN=$(aws iam get-role \ --role-name lambda-container-demo-role \ --query 'Role.Arn' --output text) aws lambda create-function \ --function-name lambda-container-demo \ --package-type Image \ --code ImageUri="$IMAGE_URI" \ --role "$ROLE_ARN" \ --architectures x86_64 \ --timeout 30 \ --memory-size 512 \ --region "$AWS_REGION"
動作確認(AWS 上)
aws lambda invoke \
--function-name lambda-container-demo \
--payload '{"name":"aws"}' \
--cli-binary-format raw-in-base64-out \
response.json \
--region "$AWS_REGION"
cat response.json
--cli-binary-format raw-in-base64-out を付けないと、AWS CLI v2 ではペイロードが Base64 として解釈されずエラーになる点に注意してください。
イメージを更新したとき
docker build --platform linux/amd64 -t lambda-container-demo:latest . docker tag lambda-container-demo:latest "$IMAGE_URI" docker push "$IMAGE_URI" aws lambda update-function-code \ --function-name lambda-container-demo \ --image-uri "$IMAGE_URI" \ --region "$AWS_REGION"
同じタグを使い回しても、Lambda へは明示的に新しいイメージを指す更新が必要です。実運用では :latest ではなく Git のコミットハッシュやビルド番号をタグにすると追跡しやすくなります。
IaC での定義例
AWS CDK(TypeScript)
import { Stack, StackProps, Duration } from "aws-cdk-lib";
import { Construct } from "constructs";
import { DockerImageFunction, DockerImageCode, Architecture } from "aws-cdk-lib/aws-lambda";
export class LambdaContainerStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new DockerImageFunction(this, "DemoFn", {
code: DockerImageCode.fromImageAsset("./lambda-container-demo"),
architecture: Architecture.X86_64,
memorySize: 512,
timeout: Duration.seconds(30),
});
}
}
fromImageAsset に Dockerfile のあるディレクトリを渡すだけで、cdk deploy 時に自動でビルドして CDK 管理の ECR へ上げてくれます。手動の ECR 操作が不要になるのが大きな利点です。
Terraform
resource "aws_lambda_function" "demo" {
function_name = "lambda-container-demo"
role = aws_iam_role.lambda.arn
package_type = "Image"
image_uri = "${aws_ecr_repository.demo.repository_url}:latest"
architectures = ["x86_64"]
timeout = 30
memory_size = 512
}
注意点
- アーキテクチャの一致: イメージのアーキ(amd64 / arm64)と関数の
--architecturesを必ず揃える。 - ベースイメージのパッチ: 定期的に再ビルドしてランタイムの脆弱性対応を取り込む。
- イメージサイズとコールドスタート: 大きいイメージはコールドスタートが伸びやすい。マルチステージビルドで不要物を削る。
- ECR のライフサイクルポリシー: 古いイメージが溜まるため世代管理する。
- タグ運用:
:latest固定は追跡性が落ちる。コミットハッシュ等を推奨。
まとめ
Lambda のコンテナイメージデプロイは、「依存が重い」「ビルド環境を揃えたい」「ローカルで再現したい」という課題に素直に答えてくれる方式です。単発なら ECR push と create/update-function、継続運用なら CDK の DockerImageFunction でビルドからデプロイまで自動化するのが扱いやすいでしょう。まずは RIE でローカル確認を挟むことで、クラウドに上げる前に多くのミスを潰せます。
