サイトアイコン 協栄情報ブログ

ECSコンテナ から AWS Lambda へ Lambda Web Adapter を利用したハンズオン

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の動作概要

参考図: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.本構成の説明

項番 実施項目 項目内容
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.概要

2.3.2.ECS と Lambda Web Adapter の Dockerfile比較

クリック で コード表示

凡例
■:共通の記述(一部追加・変更有)
●:Lambda Web Adapter だけにしか存在しない記載
★:同内容の記載

ECS:

=== ★汎用的なPythonイメージを使用 ===
FROM public.ecr.aws/docker/library/python:3.11-slim
 
=== ★作業ディレクトリを設定 ===
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()"
 
  
Lambda Web Adapter:

=== ★汎用的なPythonイメージを使用 ===
FROM public.ecr.aws/docker/library/python:3.11-slim
 
=== ● Lambda Web Adapterをコピー ===
# 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()"
 

  

2.3.3.Dockerfile修正

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.挙動確認

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.コマンド実行

# テストイベントを作成
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の内容
# ファイルを開く
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.得られた知見

モバイルバージョンを終了