🐥

API をローカル環境と AWS Lambda 環境で同一コードベースで運用する方法(FastAPI)

2025/03/18に公開

はじめに

FastAPI で開発したバックエンドアプリケーションを、ローカルでは Docker Compose を使い、本番環境では AWS Lambda にデプロイしますが、環境ごとにコードを変更せずに運用する方法を紹介します。

環境情報

  • Python: 3.10
  • FastAPI: 最新版
  • Docker: 最新版
  • AWS Lambda: Python 3.10 ランタイム
  • AWS API Gateway: REST API
  • OS: macOS 24.3.0

アーキテクチャ概要

ローカル環境

  • Docker Compose を使用して、フロントエンド、バックエンド、テスト環境を構築
  • FastAPI アプリケーションを Uvicorn で直接実行
  • ホットリロード機能を活用した迅速な開発

本番環境(AWS Lambda)

  • AWS Lambda を使用してサーバーレスアーキテクチャを実現
  • API Gateway を通じて HTTP リクエストを処理
  • Mangum アダプターを使用して FastAPI アプリケーションを Lambda ハンドラーとして実行

同一コードベースでの環境切り替えの仕組み

1. マルチステージ Docker ビルド

バックエンドの Dockerfile では、マルチステージビルドを使用して、ローカル環境と本番環境の両方に対応しています。

# ローカル開発環境用ステージ
FROM python:3.10 as local

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

ENV ENVIRONMENT=local
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

# 本番環境(AWS Lambda)用ステージ
FROM public.ecr.aws/lambda/python:3.10 as production

# 依存関係のインストール
COPY requirements.txt ${LAMBDA_TASK_ROOT}/
RUN pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

# アプリケーションコードのコピー
COPY app ${LAMBDA_TASK_ROOT}/app
COPY app/main.py ${LAMBDA_TASK_ROOT}/

# 環境変数の設定
ENV ENVIRONMENT=production \
    AWS_LAMBDA_FUNCTION_NAME=sample-api

CMD ["app.main.handler"]

# デフォルトは本番環境用のステージを使用
FROM production

最後の FROM production 命令は、明示的に環境を指定しない場合のデフォルトステージを設定しています。この行により、単に docker build を実行した場合は本番環境用のイメージが生成されます。一方、docker build --target local のように特定のターゲットを指定すると、そのステージのイメージが生成されます。

ローカル環境では、Docker Compose で明示的に target: local を指定することで、ローカル開発用のステージを使用します。

api:
  build:
    context: ./api
    target: local
  # 他の設定...

2. 環境検出と Mangum アダプター

FastAPI アプリケーションのエントリーポイント(main.py)では、環境変数を使用して実行環境を検出し、AWS Lambda 環境の場合のみ Mangum ハンドラーを作成します。

import os
from fastapi import FastAPI
from mangum import Mangum

# 環境変数の取得
ENVIRONMENT = os.getenv("ENVIRONMENT", "local")

app = FastAPI(title="Sample API")

# エンドポイントの定義...

# AWS Lambda環境の場合のみMangumハンドラーを作成
if os.getenv("AWS_LAMBDA_FUNCTION_NAME"):
    handler = Mangum(app, lifespan="off", api_gateway_base_path=None)

ここで重要なのは、AWS_LAMBDA_FUNCTION_NAME 環境変数はユーザーが明示的に設定する必要がないという点です。この環境変数は AWS Lambda ランタイムによって自動的に設定されるため、Lambda 環境では常に利用可能です。そのため、この変数の存在を確認するだけで、コードが Lambda 環境で実行されているかどうかを簡単に判断できます。

これにより、同じコードベースでありながら、ローカル環境では Uvicorn による直接実行、AWS Lambda 環境では Mangum ハンドラーを通じた実行が可能になります。

環境変数の管理

ローカル環境(Docker Compose)

ローカル環境では、Docker Compose のenvironmentセクションで環境変数を設定します。

api:
  # 他の設定...
  environment:
    - PYTHONPATH=/app
    - ENVIRONMENT=local
    - API_KEY=sample_api_key_123
    - DEBUG=True
    - LOG_LEVEL=DEBUG

本番環境(AWS Lambda)

AWS Lambda 環境では、以下の環境変数を設定する必要があります:

  1. 必須環境変数

    • ENVIRONMENT: productionに設定
    • API_KEY: API キー(セキュアな値を設定)
  2. オプション環境変数

    • DEBUG: デバッグモード(本番ではFalseに設定)
    • LOG_LEVEL: ログレベル(本番ではINFOまたはERRORに設定)

AWS Lambda コンソールまたはインフラストラクチャコード(Terraform、CloudFormation など)を通じて、これらの環境変数を設定します。

環境に応じた動的な設定

コード内では、環境変数に基づいて動的に設定を変更することができます。例えば、CORS 設定は環境によって異なる値を使用します:

# CORS設定
ALLOWED_ORIGINS = (
    ["https://sample-app.example.com"] if ENVIRONMENT == "production" else ["http://localhost:3000"]
)

デプロイフロー

ローカル環境での開発

  1. Docker Compose を使用して環境を起動:
docker compose up
  1. ローカル環境でのテスト:
# APIエンドポイントのテスト
curl http://localhost:8000/health

# サンプル機能のテスト
curl -X POST http://localhost:8000/process -H "Content-Type: application/json" -d '{"data": "test"}'

AWS Lambda 環境へのデプロイ

  1. 本番用の Docker イメージをビルド:
docker build -t sample-api:production ./api
  1. コンテナイメージを AWS ECR にプッシュ(または直接 Lambda 関数としてデプロイ)

  2. AWS Lambda の環境変数を設定:

    • AWS Lambda コンソールで環境変数を設定
    • または、インフラストラクチャコードを通じて設定
  3. デプロイ後のテスト:

    • API Gateway エンドポイントを使用して API をテスト

まとめ

この記事では、FastAPI アプリケーションをローカル環境と AWS Lambda 環境の両方で同じコードベースを使用して運用する方法について説明しました。主なポイントは以下の通りです:

  1. マルチステージ Docker ビルドを使用して環境ごとの違いを吸収
  2. 環境変数を使用して実行環境を検出
  3. Mangum アダプターを使用して FastAPI アプリケーションを Lambda ハンドラーとして実行
  4. 環境変数を通じて各環境に適した設定を動的に適用

この方法を使用することで、開発からテスト、本番環境までのシームレスなデプロイが可能になり、環境間の一貫性を保ちながら効率的な開発を行うことができます。

実装例

上記の解決策を実装した実際のサンプルコードは以下のリポジトリで公開しています:
https://github.com/keishimizu26629/fastapi-lambda-sample

参考 URL

Discussion