今回は、AWS LambdaとCloud FunctionsにFastAPIをシームレスにデプロイする方法を紹介します。
 OSSの開発などでどちらでも動作する機能を同一コードで管理する時などに便利です。
作成する機能
SentryというApplication Monitoring PlatformのアラートをAWS Lambda, Cloud Functions経由でSlackに通知できる機能を作成します。
 Integration機能を使用してSlackに通知する事ができますが、Free planでは直接通知する事ができないため、Webhookを使用して通知するものです。
 OSSはこちらで公開しています。
使用するライブラリ
今回使用するライブラリはAgraffeというものです。
 AWS LambdaやCloud Functionsで使用できるEntrypointをFastAPIのアプリケーションを使用して簡単に作成する事ができます。
開発
パッケージのインストール
poetryを使用してパッケージ管理を行います。
 poetryをインストールし、初期化を行います。
pip install poetry
poetry init初期化が完了したら、今回使用するパッケージをインストールします。
poetry add fastapi
poetry add agraffe
poetry add requestsrequirements.txtの作成
パッケージのインストールが完了したら、requirements.txtを作成します。
 デプロイする際に必要になるため、パッケージを追加した際はこの作業を必ず実施する必要が有ります。
poetry export -f requirements.txt > requirements.txtコーディング
実際の処理を書いていきます。
 AWS LambdaとCloud Functionsの切り替えを環境変数で行うことで、コードを変えずに処理を変更しています。
import os
from typing import Dict
from agraffe import Agraffe, Service
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/printf")
async def printf(request: Request) -> Dict[str, str]:
    body = await request.json()
    print(body)
    return {}
platform = os.environ.get("PLATFORM", "GCP")
if platform == "GCP":
    entry_point = Agraffe.entry_point(app, Service.google_cloud_functions)
elif platform == "AWS":
    entry_point = Agraffe.entry_point(app, Service.aws_lambda)
else:
    Exception(f"Unsupported platform of {platform}")FastAPIのインスタンスをAgraffeに渡す事で柔軟にコード切り替えを行っています。
 環境変数はデプロイ時に設定し、その値を使用して切り替えを行っています。
- クラウド環境の切り替えコード抜粋
platform = os.environ.get("PLATFORM", "GCP")
if platform == "GCP":
    entry_point = Agraffe.entry_point(app, Service.google_cloud_functions)
elif platform == "AWS":
    entry_point = Agraffe.entry_point(app, Service.aws_lambda)
else:
    Exception(f"Unsupported platform of {platform}")デプロイ
Cloud Functionsへのデプロイ
gcloudコマンドを使用してデプロイします。
 gcloudコマンドのインストール方法はこちらを参照してください。
gcloud functions deploy sentry-alert-to-slack-with-functions \
    --runtime python37 \
    --trigger-http \
    --entry-point entry_point \
    --region {region} \
    --set-env-vars SLACK_ENDPOINT={SLACK_WEBHOOK_ENDPOINT} \
    --set-env-vars PLATFORM=GCPAWS Lambdaへのデプロイ
zipにパッケージ化した後、awsコマンドを使用してデプロイします。
 awsコマンドのインストール方法はこちらを参照してください。
- パッケージ化
mkdir dist \
    && cp main.py dist/ \
    && pip install -r requirements.txt -t dist/ \
    && cd dist \
    && zip -r ../dist.zip * \
    && cd ../ \
    && rm -rf dist- デプロイ
aws lambda create-function \
    --function-name sentry-alert-to-slack-with-functions \
    --runtime python3.7 \
    --role {LambdaExecuteRole} \
    --handler entry_point \
    --zip-file fileb://dist.zip \
    --environment "Variables={SLACK_ENDPOINT={SLACK_WEBHOOK_ENDPOINT},PLATFORM=AWS}"デプロイ後、Lambdaを開きAPI Gatewayの設定を行います。
 AgraffeはREST APIのみ対応しているので、REST APIを選択します。
おわりに
以上でAWS LambdaとCloud Functionsを同一コードでシームレスにデプロイする事ができます。
 今回は公式のデプロイツールを使用していますが、より便利なツールを使用すると保守/運用性が上がると思います。


