1.はじめに
1.1.1.本記事の目的
前回AWS Lambda から ECS on Fargate コンテナ移行ハンズオンで、簡単なアプリケーションをECS用に作成しました。
今度はこちらのECSで作成したイメージをLambda Web Adapter
を利用することで、Lambda環境でも同じイメージが利用できるよう、その具体的な手順をハンズオン形式で記載していきます。
前回ブログのGitHub:GitHub
1.1.2.Lambda Web Adapterとは?
- Lambda Web Adapter(LWA)は、VM やコンテナ向けに開発されたウェブアプリケーションを、ほぼコード修正なしでAWS Lambdaで実行可能にするツールです。
Lambda実行環境内で拡張機能として動作し、LambdaイベントをHTTPリクエストに変換することで、通常のWebサーバーとして実装されたアプリケーションがそのままLambda上で動作します。
従来の方法ではLambda関数ごとに特有のハンドラを実装する必要がありましたが、Lambda Web Adapterを利用することで、Express.js、Flask、FastAPIなどの既存ウェブフレームワークを大きな変更なしにLambda上で実行でき、開発効率と再利用性を大幅に向上させることができます。
■ Lambda Web Adapterの動作概要
参考図:Lambda Web Adapter でウェブアプリを (ほぼ) そのままサーバーレス化する
2.ハンズオン
2.1.前提
2.1.1.実行環境
- 本記事執筆時点のバージョンを以下に記載する
環境 | 設定 |
---|---|
構築 | AWS CloudShell |
リージョン | バージニア北部(us-east-1) |
AWS CLI | 2.27.8 |
Python | 3.9.21 |
2.1.3.本構成の説明
- `aws lambda invoke{ で渡されたJSONペイロードが、Lambda Web Adapterによって「翻訳」され、あたかも通常のHTTPクライアントからWebアプリケーションにリクエストが送られたかのように振る舞う。
これにより、Webフレームワークで書かれた既存のアプリケーションコードをほとんど変更せずにLambdaで実行が可能となる。
項番 | 実施項目 | 項目内容 |
---|---|---|
1 | aws lambda invoke 実行 | ・図の「テスト」の位置から aws lambda invoke コマンドを実行。 ・このコマンドには、呼び出すLambda関数名 ($FUNCTIONNAME) と、入力データとなるペイロード (test-event.json の内容) が指定される。 |
2 | AWS Lambdaがイベントを受信 | ・AWS Lambdaサービスのエンドポイントにリクエストを送信し、Lambdaサービスは指定された関数 ($FUNCTIONNAME) の実行を準備。 |
3 | Lambda実行環境の起動とコンテナの実行 | ・関数の設定(コンテナイメージを使用)に基づいて、ECRからコンテナイメージをプルして実行。 ・コンテナが起動すると、Dockerfileの CMD で指定されたコマンド(この場合は python app.py)が実行され、Webアプリケーションがコンテナ内でリッスン状態となる(例: ポート8080) ・同時に、Lambda拡張機能として組み込まれているLambda Web Adapter (LWA) も起動する。 |
4 | LWAがLambdaランタイムからイベントペイロードを受信 | ・Lambdaランタイムは、invoke コマンドで渡されたペイロード (test-event.json の内容) をLWAに渡す。 |
5 | LWAがイベントペイロードをHTTPリクエストに変換 | ・LWAは、受け取ったJSON形式のイベントペイロードを、コンテナ内のWebアプリケーションが理解できるHTTPリクエストに変換する。 |
6 | WebアプリケーションがHTTPリクエストを処理 | ・コンテナ内で動作しているWebアプリケーション(app.py のFlaskアプリなど)は、LWAから送信されたHTTPリクエストを通常のWebリクエストとして受け取る。 ・例えば、Flaskアプリの場合、@app.route(‘/invoke’, methods=[‘POST’]) のようなルートでリクエストは処理される。 |
7 | LWAがHTTPレスポンスをLambdaのレスポンス形式に変換 | Webアプリケーションが返したHTTPレスポンス(ステータスコード、ヘッダー、ボディ)をLWAが受け取り、LWAは、このHTTPレスポンスをLambda関数が返すべきJSON形式のレスポンスに変換します。 |
8 | Lambda関数がレスポンスを返し、invoke コマンドが出力 | 変換されたレスポンスがLambda関数の実行結果として返される。 |
■本ブログの構成
2.2.事前準備(Git Clone)
# フォルダ作成
mkdir 20250511_webadapter && cd 20250511_webadapter
# リポジトリをクローンしてカレントディレクトリにリポジトリの内容を展開
git clone https://github.com/tetutetu214/20250505_ecs_container.git .
# フォルダ確認
20250511_webadapter $ ll
total 24
-rw-r--r--. 1 cloudshell-user cloudshell-user 1286 May 11 04:37 app.py
-rw-r--r--. 1 cloudshell-user cloudshell-user 582 May 11 04:37 Dockerfile
-rw-r--r--. 1 cloudshell-user cloudshell-user 757 May 11 04:37 lambda-to-ecs-task-definition.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 357 May 11 04:37 requirements.txt
-rw-r--r--. 1 cloudshell-user cloudshell-user 229 May 11 04:37 response.json
-rw-r--r--. 1 cloudshell-user cloudshell-user 46 May 11 04:37 test-event.json
2.3.Dockerfile 修正
2.3.1.概要
- Lambda Web Adapterを利用するように、ファイルの修正を実施する。
DockerfileにLambda Web Adapter
を導入するための数行の変更だけで、ECSで利用しているイメージをLambdaでも利用することが可能となる。
2.3.2.ECS と Lambda Web Adapter の Dockerfile比較
クリック で コード表示
凡例
■:共通の記述(一部追加・変更有)
●:Lambda Web Adapter だけにしか存在しない記載
★:同内容の記載
ECS:
|
Lambda Web Adapter:
|
2.3.3.Dockerfile修正
HEALTHCHECK
に関しては ECSでのヘルスチェックで利用するため、Lambdaでは利用されてない。今回の目的として(ほぼ)同じファイルで利用できるという趣旨のため記載は残置する。
cat > Dockerfile << EOF
# 汎用的なPythonイメージを使用
FROM public.ecr.aws/docker/library/python:3.11-slim
# Lambda Web Adapterを/opt/extensions/フォルダにコピー
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter
# 作業ディレクトリを設定
WORKDIR /app
# アプリケーションをコピー
COPY app.py requirements.txt ./
# 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt
# コンテナ起動時に実行するコマンド
CMD ["python", "app.py"]
# ECSヘルスチェック用
HEALTHCHECK --interval=30s --timeout=10s CMD python -c "import requests; requests.get('http://localhost:8080/health').raise_for_status()"
EOF
2.4.ECRリポジトリ作成とログイン
# 変数設定
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REPO_NAME="lambda-webadapter"
REGION=us-east-1
# ECRリポジトリ作成
aws ecr create-repository --repository-name $REPO_NAME
# ECRにログイン
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com
2.5.Dockerイメージのビルドとプッシュ
# 変数設定
ECR_IMAGE_URI=$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO_NAME
# Dockerイメージビルド
docker build -t $ECR_IMAGE_URI:latest .
# ECRにプッシュ
docker push $ECR_IMAGE_URI:latest
2.6.Lambda実行ロールと関数デプロイ
# 変数設定
FUNCTIONNAME="lambda-webadapter-function"
LAMBDAROLE="lambda-container-role"
# IAMロール作成
aws iam create-role --role-name $LAMBDAROLE --assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}'
# IAMポリシーをアタッチ
aws iam attach-role-policy \
--role-name $LAMBDAROLE \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# IAMロールARNを取得
ROLE_ARN=$(aws iam get-role --role-name $LAMBDAROLE --query 'Role.Arn' --output text)
# Lambda関数を作成
aws lambda create-function \
--function-name $FUNCTIONNAME \
--role $ROLE_ARN \
--code ImageUri=$ECR_IMAGE_URI:latest \
--description "Python Lambda Container from AWS CloudShell" \
--timeout 30 \
--memory-size 256 \
--package-type Image \
--region $REGION
2.7.挙動確認
- 今回利用している
app.py
は/invoke
というエンドポイントでイベントを処理するが、Lambda Web Adapter
では、デフォルト設定に従い/events
というエンドポイントに送信される。 - 上記理由より環境変数を設定することで、イベントの処理先を設定。
2.7.1.環境変数の設定
# Lambda関数の環境変数を設定
aws lambda update-function-configuration \
--function-name $FUNCTIONNAME \
--environment "Variables={AWS_LWA_PASS_THROUGH_PATH=/invoke}"
# 上記コマンド実施後のレスポンス(関係する部分を抜粋)
# Lambda Web Adapterがデフォルトでイベントを送るパスが/invoke と設定されている。
"Environment": {
"Variables": {
"AWS_LWA_PASS_THROUGH_PATH": "/invoke"
}
},
2.7.2.コマンド実行
"StatusCode": 200
が返ってくることを確認
# テストイベントを作成
echo '{"key": "value", "message": "Lambda Container from AWS Cloud"}' > test-event.json
# Lambda関数の呼出し
aws lambda invoke \
--function-name $FUNCTIONNAME \
--payload fileb://test-event.json \
response.json
# レスポンスの確認
cat response.json
2.7.2.実行結果
2.7.2.1.コンソールの出力
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
2.7.2.2.response.jsonの内容
- 適宜改行して記載
"server_type": "ECS"
に関しては、app.py
の返信をハードコーディングして、今回修正していないためECS
というレスポンスが返却されている。
# ファイルを開く
cat response.json
{"body":
{"display_time":"2025-05-11 14:43:53 JST",
"event":{
"key":"value",
"message":"Lambda Container from AWS Cloud"
},
"greeting":"Hello from Lambda Container from AWS Cloud!",
"requested_at":"2025-05-11T14:43:53.060190+09:00",
"server_type":"ECS"
},
"statusCode":200
}
2.8.クリーンアップ
- 以下コマンドで、本ハンズオンで作成したリソースを削除します。
不要な課金を避けるために、ハンズオン終了後は実行することを推奨します。
# Lambda関数の削除
aws lambda delete-function --function-name $FUNCTIONNAME
# ECRリポジトリの削除(必要に応じて)
aws ecr delete-repository --repository-name $REPO_NAME --force
# IAMロールの削除
aws iam detach-role-policy \
--role-name lambda-container-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name $LAMBDAROLE
3.おわりに
3.1.得られた知見
- ECSで動いていたコンテナアプリを、Dockerfile内にLambda Web Adapterを追加するだけでLambdaでも実行可能となる
- 同じアプリケーションコードを活用できるため、環境ごとの管理コストが低減する
3.2.今後の課題
- ECSとLambdaの両方に対応したデプロイパイプラインの構築方法