0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

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

参考図: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関数の実行結果として返される。

■本ブログの構成
image.png

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:

=== ★汎用的な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修正

  • 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の両方に対応したデプロイパイプラインの構築方法
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?