Serverless Operations, inc

>_cd /blog/id_v2x79gmkf

title

AWS Lambda Web Adapter × SnapStartでコールドスタートを改善する

今日は以下の続きをAWS Lambda Web Adapterでやってみた備忘録を書きます。

AWS Lambda がコンテナベース関数で SnapStart をサポートしたのでやってみた

以前、このブログで次の2本の記事を書きました。

前者では、Express や Flask のような使い慣れた Web フレームワークを、コードを書き換えずにそのまま Lambda 上で動かせる AWS Lambda Web Adapter(以下 LWA) を紹介しました。ただし基盤はあくまで Lambda です。HTTP リクエストを受け付けて初めて関数が起動するため、コールドスタートが発生しうる、という点が最後に残った注意点でした。

後者では、初期化済みの実行環境をスナップショットとして保存し、呼び出し時にそこから復元することで、そのコールドスタートを数秒からサブ秒級まで削れる SnapStart を見てきました。そして今回のアップデートで SnapStart がコンテナイメージにも対応したことで、LWA と組み合わせるという使い方ができるようになりました。

そこで本記事では、この2つを合流させます。ひとことで言えば、「LWA で動かす Web アプリを、SnapStart 対応のコンテナイメージに載せて、コールドスタートを改善する」という第二弾です。

これからやること

LWA は、Lambda の拡張(エクステンション)としてイメージに組み込まれ、関数の初期化フェーズで Web サーバーを起動し、レディネスチェック(起動確認)を通すという動きをします。

SnapStart は、この初期化がすべて終わったあとの状態をスナップショットとして取得します。つまり LWA と組み合わせた場合、「Web サーバーが起動し終わった状態」がまるごとスナップショットに収められ、呼び出し時にはそこから復元されることになります。毎回ゼロから Web サーバーを立ち上げ直す必要がなくなるわけで、両者は相性の良い組み合わせだと言えます。

これまで SnapStart はコンテナイメージに対応していなかったため、LWA アプリをコンテナとしてパッケージすると SnapStart は使えませんでした。今回のアップデートでそれが可能になった形です

さっそくやってみる

まずは作業用ディレクトリを作成します。

mkdir lwa-snapstart && cd lwa-snapstart

次に、 app.pyでFastAPI の Web アプリ本体を作成します。

cat > app.py << 'EOF'
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {"message": "Hello from Lambda Web Adapter + SnapStart!"}


@app.get("/health")
def health():
    return {"status": "ok"}
EOF

requirements.txt を作成し、FastAPI と、それを動かす ASGI サーバーの Uvicorn を指定します。

cat > requirements.txt << 'EOF'
fastapi==0.115.6
uvicorn==0.34.0
EOF

次に Dockerfile を作成します。

cat > Dockerfile << 'EOF'
FROM public.ecr.aws/lambda/python:3.12

# Lambda Web Adapter を組み込む
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter

# LWA にアプリのポートを教える
ENV AWS_LWA_PORT=8080
ENV PORT=8080

# 依存関係をインストール
COPY requirements.txt ${LAMBDA_TASK_ROOT}
RUN pip install -r requirements.txt

# アプリ本体をコピー
COPY app.py ${LAMBDA_TASK_ROOT}

# AWS ベースイメージ既定の ENTRYPOINT(RIC) を上書きし、Web サーバーを直接起動する
ENTRYPOINT []
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
EOF

イメージをビルドします。Lambda 実行環境との互換性のため、プラットフォームを明示します。

docker buildx build --platform linux/amd64 --provenance=false -t lwa-snapstart:test .

まずECR にログインします。取得した認証トークンで docker をログインさせます。

aws ecr get-login-password --region ap-northeast-1 \
  | docker login --username AWS --password-stdin 917561075114.dkr.ecr.ap-northeast-1.amazonaws.com

次にイメージを格納するECR にリポジトリを作成します。関数と同じ東京リージョンに作ります。

aws ecr create-repository --repository-name lwa-snapstart --region ap-northeast-1 \
  --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:917561075114:repository/lwa-snapstart",
        "registryId": "917561075114",
        "repositoryName": "lwa-snapstart",
        "repositoryUri": "917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart",
        "createdAt": "2026-09-13T16:34:38.492000+09:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": true
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

ローカルのイメージに、ECR にプッシュするためのタグを付けます。

docker tag lwa-snapstart:test 917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart:latest

ECR にイメージを push します。

docker push 917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart:latest

以下の様にPush中の画面が表示されますので少し待ちます。

The push refers to repository [917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart]
166393af100f: Pushed
3e767dc276bd: Pushed
5fc6fa3a2c6e: Pushed
5892b917b948: Pushing [==================================>                ]  102.8MB/147.3MB
b285d48dc4c8: Pushed
eedfa781985c: Pushed
40057257fd74: Pushed
202b864d06aa: Pushed
74f2eb4cb8f6: Pushed
da928407f440: Pushed

ではLambda関数の実行に必要な権限を持つIAMロールを作成します。

cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
aws iam create-role --role-name lambda-ex \
  --assume-role-policy-document file://trust-policy.json && \
aws iam attach-role-policy --role-name lambda-ex \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

ではいよいよLambda関数を作成します。

aws lambda create-function \
  --function-name lwa-snapstart \
  --package-type Image \
  --code ImageUri=917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart:latest \
  --role arn:aws:iam::917561075114:role/lambda-ex \
  --snap-start ApplyOn=PublishedVersions \
  --timeout 30 \
  --memory-size 512 \
  --region ap-northeast-1

関数の作成が完了するまで数分待ちます。SnapStartを使う場合、バージョン指定が必須となりますのでバージョンをまず発行します。ここで初期化 → スナップショット取得が走ります。LWA 構成では、この初期化フェーズで Web サーバー(Uvicorn)が起動し、レディネスチェックを通った状態がスナップショットに収められます。

aws lambda publish-version --function-name lwa-snapstart --region ap-northeast-1

Web としてアクセスできるように、発行したバージョンに Function URL を作成します。

aws lambda create-alias \
  --function-name lwa-snapstart \
  --name snapstart \
  --function-version 1 \
  --region ap-northeast-1

aws lambda create-function-url-config \
  --function-name lwa-snapstart \
  --qualifier 1 \
  --auth-type NONE \
  --region ap-northeast-1

このタイミングで以下のエラーが出てしまいます

aws: [ERROR]: An error occurred (ResourceConflictException) when calling the CreateAlias operation: The operation cannot be performed at this time. The resource arn:aws:lambda:ap-northeast-1:917561075114:function:lwa-snapstart:1 is currently in the following state: 'Failed'. StateReasonCode: 'FunctionError'

Additional error details:
Type: User

aws: [ERROR]: An error occurred (ValidationException) when calling the CreateFunctionUrlConfig operation: 1 validation error detected: Value '1' at 'qualifier' failed to satisfy constraint: Member must satisfy regular expression pattern: ((?!^\d+$)^[0-9a-zA-Z-_]+$)

CloudWatch Logs を見てみると以下の様になっています。

 aws logs tail /aws/lambda/lwa-snapstart --since 15m --region ap-northeast-1
2026-09-17T05:51:39.586000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO app is not ready after 2000ms url=http://127.0.0.1:8080/
2026-09-17T05:51:41.590000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO app is not ready after 4000ms url=http://127.0.0.1:8080/
2026-09-17T05:51:41.692000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO:     Started server process [5]
2026-09-17T05:51:41.692000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO:     Waiting for application startup.
2026-09-17T05:51:41.692000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO:     Application startup complete.
2026-09-17T05:51:41.692000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
2026-09-17T05:51:41.763000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INFO:     127.0.0.1:60582 - "GET / HTTP/1.1" 200 OK
2026-09-17T05:51:41.764000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb thread 'main' (2) panicked at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lambda_runtime-1.1.1/src/types.rs:169:10:
2026-09-17T05:51:41.764000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb missing lambda-runtime-aws-request-id header
2026-09-17T05:51:41.764000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
2026-09-17T05:51:41.766000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb EXTENSION       Name: lambda-adapter   State: Ready     Events: []
2026-09-17T05:51:41.766000+00:00 2026/09/17/[1]885758101e0e4f6f92c0f2c354db82bb INIT_REPORT Init Duration: 4203.20 ms  Phase: init      Status: error   Error Type: Extension.Crash

実は aws lambda publish-version --function-name lwa-snapstart --region ap-northeast-1 の時点で本来SnapStartが起動しスナップショットが作成されるのですが、その時点ですでにエラーとなっていました。このためそれ以降のコマンドが失敗するようになっています。

まず Uvicorn runningGET / HTTP/1.1" 200 OK から、Web アプリ(FastAPI)は正常に起動し、LWA のレディネスチェックも通過していることが分かります。つまりアプリ側には何の問題もありません。

問題はその直後です。thread 'main' panickedmissing lambda-runtime-aws-request-id header は、LWA(lambda-adapter 拡張)自身がクラッシュしたことを示しています。Error Type: Extension.Crash も、落ちたのがアプリではなく拡張であることを示しています。これは、LWA が Lambda の Runtime API を呼び出したときに、本来あるべき lambda-runtime-aws-request-id ヘッダーが存在せず、それを前提に動く LWA が異常終了した、という意味です。SnapStart のスナップショット取得フェーズは通常の呼び出しとは異なる特殊なライフサイクルであり、LWA の内部ランタイムクライアントがこのフェーズに対応していない(要求しているヘッダーが存在していない)ために発生しています。

コンテナイメージで LWA を利用するには、AWS ベースイメージ既定のエントリーポイントである RIC(Runtime Interface Client)を上書きし、LWA を自前のランタイムクライアントとして動かす必要があります。ところが、SnapStart のスナップショット取得・復元のライフサイクルを正しく処理できるのは、この上書きによって退けられた RIC の方となっています。その結果、SnapStart の司令塔が不在のまま LWA がスナップショットフェーズに直面し、対応できずにクラッシュする。記事執筆時点2026年9月17日では最新版のLWAでも正式対応はしていないようでした。

回避策

LWA公式レポジトリには以下の記載があります。

Non-AWS base images may be used since the Runtime Interface Client ships with the Lambda Web Adapter.

つまり LWA には RIC(Runtime Interface Client)が同梱されており、非AWSベースイメージであっても LWA 自身が RIC の役割を提供する、ため上記で発生したヘッダーの上書き問題を回避できます。このため非AWSベースイメージでコンテナを再度作成します。

Dockerfileを以下に修正します。

FROM public.ecr.aws/docker/library/python:3.12-slim

# ① SnapStart をこのイメージで許可する(カスタムベースイメージのため)
LABEL com.amazonaws.lambda.feature.snapstart="Allow"

# ② Lambda Web Adapter を組み込む(RIC も同梱されている)
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.0.1 /lambda-adapter /opt/extensions/lambda-adapter

ENV PORT=8080
WORKDIR /var/task

COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY app.py ./

# ③ 非AWSベースイメージなので ENTRYPOINT 上書きは不要。素直に Web サーバーを起動
CMD exec uvicorn app:app --host 0.0.0.0 --port 8080

再度イメージをビルドします。

docker buildx build --platform linux/amd64 --provenance=false -t lwa-snapstart-2nd:test .

イメージにタグをつけます。

docker tag lwa-snapstart-2nd:test 917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart-2nd:latest

出来上がったイメージをレポジトリにPushします。

docker push 917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart-2nd:latest

では再度Lambda関数を作成します。

aws lambda create-function \
  --function-name lwa-snapstart-2nd \
  --package-type Image \
  --code ImageUri=917561075114.dkr.ecr.ap-northeast-1.amazonaws.com/lwa-snapstart-2nd:latest \
  --role arn:aws:iam::917561075114:role/lambda-ex \
  --snap-start ApplyOn=PublishedVersions \
  --timeout 30 \
  --memory-size 512 \
  --region ap-northeast-1

関数が作成されたらSnapStartに必要なバージョンを発行いたします。

aws lambda publish-version --function-name lwa-snapstart-2nd --region ap-northeast-1

では先ほど失敗したFunctionURLの発行と権限付与を行います。

# エイリアス(バージョンを指す別名)を作成
aws lambda create-alias \
  --function-name lwa-snapstart-2nd \
  --name snapstart \
  --function-version 1 \
  --region ap-northeast-1

# Function URL はエイリアス名で作成(qualifier に数字は不可)
aws lambda create-function-url-config \
  --function-name lwa-snapstart-2nd \
  --qualifier snapstart \
  --auth-type NONE \
  --region ap-northeast-1

# 権限1: InvokeFunctionUrl(AuthType NONE 条件つき)
aws lambda add-permission \
  --function-name lwa-snapstart-2nd \
  --qualifier snapstart \
  --statement-id FunctionURLAllowPublicAccess \
  --action lambda:InvokeFunctionUrl \
  --principal '*' \
  --function-url-auth-type NONE \
  --region ap-northeast-1

# 権限2: InvokeFunction(こちらは --function-url-auth-type を付けない)
aws lambda add-permission \
  --function-name lwa-snapstart-2nd \
  --qualifier snapstart \
  --statement-id FunctionURLAllowPublicInvoke \
  --action lambda:InvokeFunction \
  --principal '*' \
  --region ap-northeast-1

いよいよテストです。

curl -i https://<あなたのURL>.lambda-url.ap-northeast-1.on.aws/
HTTP/1.1 200 OK
...
x-amzn-Remapped-server: uvicorn
{"message":"Hello from Lambda Web Adapter + SnapStart!"}

無事成功しました!

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-