はじめに

前回の記事の中でAWS Summit 2026のCNS449というセッションでAWS Lambda Web Adapterの話があったことを軽くまとめましたが、以前案件でAWS Lambda Web Adapterを実際に使用した際、日本語の情報がまだ少ないと感じていたので今回こちらで私のセッション受講メモを交えて改めて詳しく紹介しようと思います。

既存のWebフレームワークで書かれたアプリケーションを、Lambda向けにハンドラーを書き換えることなくそのままLambda上で動かせる、というのがコンセプトです。

本記事では以下を使って検証しています。

  • Webフレームワーク: FastAPI
  • IaC: Terraform
  • デプロイ方式: コンテナイメージ(Lambdaのコンテナサポート)+ Function URL

今回は私が最近よく使用している構成ですがExpress/Flask/DjangoやSAM/CDKでももちろん問題ないです!

Lambda Web Adapterとは

Lambda Web Adapterは、Lambda Extensionとして動作するプロキシです。Lambda Runtime APIから受け取ったイベント(API Gateway/ALB/Function URLからのHTTPリクエスト相当)を、コンテナ内でリッスンしている通常のHTTPサーバー(今回はUvicorn上のFastAPI)へのHTTPリクエストに変換し、レスポンスを再びLambdaのレスポンス形式に変換して返します。

つまりアプリケーション側は「Lambdaで動いている」ことを意識する必要がなく、lambda_handlerのようなLambda専用のエントリポイントを書く必要がありません。ローカルではuvicorn main:appで動かし、Lambda上ではAdapterが同じアプリをそのままプロキシする、という構成になります。

従来方式との違い

従来のLambda対応 Lambda Web Adapter
アプリコードの変更 ASGI用のアダプタ層を書く必要あり 基本的に不要
ローカル実行との差異 Lambda用ラッパーとローカル起動用コードが分岐しやすい 同じ起動コマンドをそのまま使える
対応言語/フレームワーク 各言語のライブラリに依存 HTTPサーバーであれば言語を問わない
デプロイ形態 ZIPパッケージが主 コンテナイメージ/ZIP(Lambda Layer)いずれも可

セッションで語られていた設計思想: AI活用時代のLambda

CNS449では機能紹介だけでなく、「なぜ今Lambda Web Adapterが必要とされているか」という文脈でAWSの方が語っていた設計思想が印象的だったので、聴講メモをもとに紹介します(発言の要約・再構成であり、一次資料そのままではない点はご留意ください)。

AIにLambda固有の制約を意識させない

生成AIにコードを書かせる、レビューさせる機会が増える中で、Lambdaのハンドラー形式やイベント変換処理のようなLambda固有のコードは、AIにとっても人間にとっても「本質的でない」複雑さになります。AIにコードを生成・修正させるとき、Lambdaの制約(イベント形式、コンテキストオブジェクト、レスポンス形式など)まで意識させる必要があると、生成されるコードの質やテストの書きやすさに影響してしまいます。

Lambda Web Adapterを使うと、AIは「Lambda上で動く」ことを意識する必要がなくなり、通常のWebサーバー(FastAPIなど)としてコードを生成・修正すればよくなります。これはそのままテスト戦略にも直結します。

シンプルで薄いテストを書く

Lambda固有のコードが増えるほど、テストもLambda環境をモックする必要が出てきて重くなります。LWAによって「薄いLambda」を保つ(Lambda側にはAdapterの起動設定程度しか置かず、ロジックは通常のHTTPサーバーとして実装する)ことで、テストはFastAPIのTestClientのような通常のWebアプリと同じ書き方で済み、AIにテストを書かせる場合もLambda特有の知識が不要になります。

Lambdaにどれくらいのコード量を乗せるか

一方で「薄く保つ」ことと「1つのLambda関数にどこまで機能をまとめるか」はトレードオフになります。1つのLambda(コンテナ)に多くのエンドポイントを持たせるほど、アプリケーションコードはシンプルな1枚岩のWebアプリとして扱いやすくなりますが、次に述べる権限の問題が出てきます。

LWAはIAM権限が広くなりやすい

複数のエンドポイント・複数のAWSリソースへのアクセスを1つのLambda関数(=1つの実行ロール)に集約すると、そのLambda関数のIAMロールが必要以上に広い権限を持ってしまいがちだという指摘がありました。Lambdaは元々「1関数1責務」を前提に最小権限を設計しやすい仕組みですが、LWAで既存のWebアプリをそのまま乗せると、権限の粒度がアプリ側の構造(=既存のモノリシックなWebアプリの構造)に引き戻されてしまう点は注意が必要です。

対策として、機能やリソースアクセスパターンごとに適切な単位でLambda関数(≒コンテナ)を分割し、それぞれに最小権限のロールを与える、という設計指針が語られていました。

モノレポとの相性の良さ

この分割方針は、モノレポ構成のプロジェクトと相性が良いとのことです。1つのリポジトリの中に複数のサービス(Lambda関数単位)を持ちつつ、各サービスは通常のWebアプリとして開発・テストし、デプロイ時にLWA経由でLambda化する、という構成が取りやすくなります。

HTTPだけでなくAWSサービスイベントにも対応

またLambda Web AdapterはAPI Gateway/ALB/Function URLなどのHTTP系トリガーだけでなく、S3やSQS、EventBridgeなど他のAWSサービスからのイベントにも対応が進んでいるという話もありました。本記事の検証はHTTP経路のみですが、既存のWebアプリを移行する際にAWSサービスイベントも同じアプリ内で扱えるようになる点は、今後試してみたいポイントです。

想定ユースケース

  • 既存のコンテナ化されたWebアプリをサーバーレスに移行したい
  • ローカル開発環境と本番Lambda環境でアプリコードを完全に一致させたい
  • 特定言語・フレームワーク専用のLambdaアダプタライブラリに縛られたくない

ハンズオン

構成

  • FastAPIアプリをDockerコンテナ化
  • Lambda Web Adapterのバイナリをマルチステージビルドで取り込む
  • コンテナイメージとしてLambda関数をデプロイ
  • Function URLでHTTPエンドポイントを直接公開

アプリケーションコード

Lambda用の特別なコードは書いていません。通常のFastAPIアプリです。

app/main.py

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="Lambda Web Adapter Demo")


class Item(BaseModel):
    name: str
    price: float


@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI on Lambda Web Adapter"}


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


@app.post("/items")
def create_item(item: Item):
    return {"received": item}

Dockerfile

ポイントはマルチステージビルドでaws-lambda-adapterイメージからバイナリだけを取り出し、/opt/extensions/に配置するだけという点です。ベースイメージはLambda専用のものではなく、通常のpython:3.12-slimを使っています。

app/Dockerfile

# Lambda Web Adapterのバイナリを取得するためのステージ
FROM public.ecr.aws/awsguru/aws-lambda-adapter:0.9.0 AS lambda-adapter

# ベースイメージはLambda専用ではない、通常のPythonイメージでよい
FROM python:3.12-slim

# Adapterを拡張(Extension)として配置するだけでLambda対応になる
COPY --from=lambda-adapter /lambda-adapter /opt/extensions/lambda-adapter

WORKDIR /var/task

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY main.py .

# Adapterがヘルスチェック/プロキシ対象とするポート
ENV AWS_LWA_PORT=8080
EXPOSE 8080

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

AWS_LWA_PORTでAdapterがどのポートにプロキシするかを指定しています。他にもAWS_LWA_READINESS_CHECK_PATH(起動確認用のヘルスチェックパス)などの環境変数があり、必要に応じて調整できます。

Terraformでのデプロイ

Dockerイメージのビルド・ECRへのpush・Lambda関数の作成までTerraformで一貫して行っています。

terraform/ecr.tf

resource "aws_ecr_repository" "app" {
  name         = var.project_name
  force_delete = true

  image_scanning_configuration {
    scan_on_push = true
  }
}

resource "docker_image" "app" {
  name = "${aws_ecr_repository.app.repository_url}:latest"

  build {
    context  = "${path.module}/../app"
    platform = "linux/amd64"
  }
}

resource "docker_registry_image" "app" {
  name = docker_image.app.name
}

terraform/lambda.tf

resource "aws_lambda_function" "app" {
  function_name = var.project_name
  role          = aws_iam_role.lambda_exec.arn

  package_type = "Image"
  image_uri    = "${aws_ecr_repository.app.repository_url}@${docker_registry_image.app.sha256_digest}"

  memory_size = 512
  timeout     = 30

  environment {
    variables = {
      AWS_LWA_PORT = "8080"
    }
  }
}

resource "aws_lambda_function_url" "app" {
  function_name      = aws_lambda_function.app.function_name
  authorization_type = "NONE"
}

デプロイ

cd terraform
terraform init
terraform apply

適用後、Function URLが出力されます。

動作確認

Function URLに対して直接リクエストします。

curl "$(terraform output -raw function_url)"

curl -X POST "$(terraform output -raw function_url)items" \
-H "Content-Type: application/json" \
-d '{"name":"apple","price":100}'

以下は実行結果です。

コンソールでは以下のようになっています。コンソールを修正してテスト等を行う方は若干不便かもしれません。

CloudWatch Logsを見ると、Extensionとして起動したAdapterのログと、Uvicornの起動ログが両方出力されており、コンテナ内で2つのプロセスが協調して動いていることが確認できます。

詰まった点・Tips

  • 例: AWS_LWA_PORTとDockerfile内でExposeしているポート・Uvicornの起動ポートが不一致だとタイムアウトする
  • 例: レスポンスのストリーミングを使う場合はAWS_LWA_INVOKE_MODE=RESPONSE_STREAMとLambda側のInvoke modeの設定を合わせる必要がある
  • 例: 今回の検証では1つのLambda関数に全エンドポイントを詰め込んだが、実運用では前述の権限設計の指針の通り機能単位で関数を分割し、IAMロールを最小権限にすべき

既存手法との比較・使い分け

  • Lambda Web Adapterは言語・フレームワークを問わず「HTTPサーバーであること」だけを要求するため、既存コンテナ資産の移行がしやすい
  • 一方で、常時起動のコンテナ基盤(ECS/EKS)からの移行では、コールドスタートや同時実行数などLambda特有の制約は依然として残る点は注意が必要

まとめ

  • Lambda Web Adapterを使うと、既存のFastAPI(に限らずHTTPサーバー全般)アプリをLambda用のコード変更なしにコンテナイメージとしてデプロイできる
  • Terraformでの構築もECR pushからLambda関数作成まで一貫して自動化できる
  • 実運用に載せる際はストリーミング対応やペイロードサイズ制限など、Lambda特有の制約を踏まえた設計が必要

参考