AWS Lambda と Lambda Web Adapter を活用した FastAPI 開発
はじめに
Lambda Web Adapter は、AWS Lambda 上でのアプリケーション開発を一変させるツールです。多くの開発者が馴染みのある Docker を活用してバックエンドサービスを構築できるようになります。
この記事では、FastAPI を用いて Lambda Web Adapter を利用した API バックエンドの開発手順を解説します。サンプルコードは私の GitHub リポジトリ からクローンできます。
概要
Lambda Web Adapter のメリット
Lambda Web Adapter は従来の AWS Lambda に比べて以下のような利点を提供します。
比較項目 | Lambda Web Adapter | 従来の Lambda |
---|---|---|
ローカルテストの容易さ | 簡単 | 難しい |
他サービス (例: Fargate) への移行の容易さ | 簡単 | 難しい |
AWS リソースの数 | 少ない1 | 多い |
コスト | 低い | 非常に低い |
1 Lambda Web Adapter を使用する場合、必要なのは 1 つの Lambda 関数と 1 つの API Gateway ルートのみです。
AWS の設計
従来の設計
API Gateway と複数の Lambda 関数を利用した従来の AWS 設計では、多くの API を管理する際に煩雑になります。
Lambda Web Adapter を利用した設計
Lambda Web Adapter を利用することで、シンプルなアーキテクチャを実現できます。必要なのは 1 つの API Gateway ルートと 1 つの Lambda 関数だけです。
プロジェクト構成
以下のプロジェクト構成を作成します。
/
|-- .venv/
|-- cdk/
| |-- bin/
| | `-- cdk.ts
| |-- lib/
| | `-- cdk-stack.ts
| |-- node_modules/
| |-- test/
| | `-- cdk.test.ts
| |-- .gitignore
| |-- .npmignore
| |-- biome.json
| |-- cdk.json
| |-- jest.config.js
| |-- package.json
| |-- package-lock.json
| |-- README.md
| `-- tsconfig.json
|-- docker/
| |-- Dockerfile-web
| `-- compose.yaml
|-- node_modules/
|-- src/
| |-- main.py
| `-- requirements.txt
|-- .gitignore
|-- package.json
`-- package-lock.json
はじめに
AWS CDK 環境のブートストラップ
まず、AWS CDK 環境をブートストラップします。
AWS CDK ガイド に従って、ローカルに AWS CDK をインストールします。
npm i -D aws-cdk
npx cdk bootstrap aws://<AWS_ACCOUNT_ID>/<AWS_REGION>
CDK プロジェクトの初期化
以下のコマンドで CDK プロジェクトを初期化します。
mkdir cdk && cd cdk
npx cdk init app --language typescript
FastAPI のセットアップ
以下のコマンドで FastAPI をインストールします。
python -m venv .venv
source .venv/bin/activate
pip install "fastapi[standard]"
mkdir src
pip freeze > ./src/requirements.txt
FastAPI を用いたバックエンド API の構築
API の作成
以下は ./src/main.py
に保存する FastAPI アプリケーションの例です。
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
API のテスト
FastAPI サーバーを起動します。
fastapi dev ./src/main.py
API を以下のようにテストします。
curl "http://127.0.0.1:8000/"
{"Hello":"World"}
curl "http://127.0.0.1:8000/items/1?q=keyword"
{"item_id":1,"q":"keyword"}
Docker を利用したコンテナ化
Dockerfile の作成
FastAPI バックエンドをコンテナ化するために、以下のコードを作成し ./docker/Dockerfile-web
に保存してください。この Dockerfile は開発環境と本番環境の両方に対応できるように設計されています。
主なポイント:
- ベースイメージ: 軽量かつ安全なベースイメージとして
public.ecr.aws/docker/library/python:3.12-alpine
を使用します (2 行目)。 - Lambda Web Adapter: AWS Lambda とのシームレスな統合を実現するために Lambda Web Adapter を追加します (22 行目)。
- ポート設定: 本番環境ではデフォルトで ポート 8080 をリッスンし、Lambda Web Adapter の設定と一致させます (25 行目)。
Lambda Web Adapter の使用方法については、公式ドキュメント をご参照ください。
# Base image: Python 3.12 Alpine
FROM public.ecr.aws/docker/library/python:3.12-alpine AS base
ENV APP_ROOT=/code
# Copy requirements and install dependencies
COPY ./src/requirements.txt $APP_ROOT/
RUN pip install --no-cache-dir --upgrade -r $APP_ROOT/requirements.txt
# Development stage
FROM base AS dev
ENV ENV=dev
EXPOSE 8000
CMD ["sh", "-c", "fastapi run $APP_ROOT/main.py --port 8000"]
# Production stage
FROM base
ENV ENV=prod
EXPOSE 8080
COPY ./src $APP_ROOT
# Copy Lambda Web Adapter
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
# Run FastAPI backend on port 8080 for Lambda Web Adapter
CMD ["sh", "-c", "fastapi run $APP_ROOT/main.py --port 8080"]
次のステップでは、ローカル開発とテストを効率化するために Docker Compose を設定します。
Docker Compose を利用したローカル開発
この記事ではデータベースの使用を含んでいませんが、実際のプロジェクトでは DynamoDB や MySQL などのデータベースを扱うことが一般的です。ローカル開発とテストを効率化するために Docker Compose を設定します。この設定により、API バックエンドを分離された環境でスムーズに動作させることができます。
./docker/compose.yaml
ファイルを作成し、以下の内容を記述してください。
services:
api:
build:
context: ../
dockerfile: ./docker/Dockerfile-web
target: dev
ports:
- "8000:8000"
volumes:
- ../src:/code
主な設定ポイント
- ビルドコンテキスト: プロジェクトのルート (
../
) を指定し、Dockerfile の開発環境用ステージ (target: dev
) を使用します。 - ポートのマッピング: コンテナのポート
8000
をホストのポート8000
にマッピングし、ローカルでアクセスできるようにします。 - ボリューム: ローカルの
src
ディレクトリをコンテナの/code
ディレクトリにマウントし、開発中にコードのホットリロードを有効にします。
バックエンドサービスの起動
以下のコマンドで Docker Compose を利用してローカル環境でバックエンドサービスを起動します。
cd docker
docker compose up
サービスが正常に起動すると、次のようなログが表示されます。
api-1 | INFO: Started server process [1]
api-1 | INFO: Waiting for application startup.
api-1 | INFO: Application startup complete.
api-1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
API は http://127.0.0.1:8000 でアクセス可能になります。curl
や Postman、またはブラウザを使用して通常通りテストできます。
次は AWS CDK を使用して、FastAPI アプリケーションを AWS Lambda にデプロイする方法について説明します。
AWS へのデプロイ
AWS CDK を使用したリソースの定義
FastAPI バックエンドを AWS にデプロイするために、AWS CDK を使用して必要なリソースを定義します。CDK を使用することで、インフラをコードで定義し、リソース作成が簡素化されます。
ステップ 1: エントリーポイントの設定
以下のコードを cdk/bin/cdk.ts
に貼り付けてください。このファイルは AWS CDK アプリケーションのエントリーポイントです。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkStack } from '../lib/cdk-stack';
const app = new cdk.App();
new CdkStack(app, 'App');
ステップ 2: インフラスタックの定義
cdk/lib/cdk-stack.ts
に AWS Lambda 関数と API Gateway を定義します。以下のコードは、Docker 化された Lambda 関数を作成し、それを API Gateway REST API に統合します。
import * as cdk from 'aws-cdk-lib';
import type { Construct } from 'constructs';
import { LambdaRestApi } from 'aws-cdk-lib/aws-apigateway';
import {
DockerImageCode,
DockerImageFunction,
LoggingFormat,
} from 'aws-cdk-lib/aws-lambda';
import * as path from 'node:path';
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Dockerized Lambda Function
const lambda = new DockerImageFunction(this, 'function', {
functionName: 'fast-api-app-function',
loggingFormat: LoggingFormat.JSON,
memorySize: 512, // タイムアウト回避のためにメモリを増加
code: DockerImageCode.fromImageAsset(path.join(__dirname, '..', '..'), {
file: path.join('docker', 'Dockerfile-web'),
platform: Platform.LINUX_AMD64, // Apple Silicon ユーザーは必須
exclude: ['*', '!src', '!docker'],
}),
});
// API Gateway REST API
new LambdaRestApi(this, 'api', {
handler: lambda,
deploy: true,
});
}
}
主なポイント
- メモリサイズ:
memorySize
を 512 MB またはそれ以上に設定します (20 行目)。これにより、メモリ不足によるタイムアウトを防ぎます。 - プラットフォーム設定: Apple Silicon を使用している場合は
Platform.LINUX_AMD64
を指定します (23 行目)。これを指定しないと、Error: fork/exec /opt/extensions/lambda-adapter: exec format error Extension.LaunchError
のエラーが発生する可能性があります。
これらのファイルが揃ったら、リソースのデプロイ準備が整います。
ステップ 3: リソースのデプロイ
CDK プロジェクトディレクトリに移動し、以下のコマンドを実行してデプロイを開始します。
cd cdk
npx cdk deploy
デプロイ中にリソースの作成を確認するプロンプトが表示される場合があります。その際は y
を入力して続行します。
Do you wish to deploy these changes (y/n)? y
App: deploying... [1/1]
App: creating CloudFormation changeset...
✅ App
✨ Deployment time: 52.67s
Outputs:
App.apiEndpoint9349E63C = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/
Stack ARN:
arn:aws:cloudformation:<AWS_REGION>:<AWS_ACCOUNT_ID>:stack/App/<UUID>
✨ Total time: 55.42s
デプロイが完了すると、API Gateway エンドポイント が出力されます。このエンドポイントを使用して API をテストできます。
アプリケーションのテスト
デプロイした API をテストします。
curl "https://<API_GATEWAY_ENDPOINT>/prod/"
{"Hello":"World"}
curl "https://<API_GATEWAY_ENDPOINT>/prod/items/1?q=keyword"
{"item_id":1,"q":"keyword"}
まとめ
Lambda Web Adapter は、AWS Lambda 上での API 開発を大幅に簡素化し、複雑さを削減しつつローカルテストを容易にします。AWS アーキテクチャの効率化を実現するこのツールは、モダンな Web 開発において欠かせない存在です。
Happy Coding! 🚀