FastAPI (mangum) を AWS Lambda で動かす
対象
- PythonのFastAPIをAWS Lambdaで動かしたい人
- カスタムイメージを使ってビルドしたい人
- ローカルでの動作確認方法を知りたい人
- AWSのリソース作成の概略を知りたい人
利用するライブラリのバージョン
以下のライブラリを利用します。
fastapiは執筆時点での最新版、mangumは0.11.0より新しいバージョンだとエラーが発生するため、このバージョンとしてます。
pydanticやstarlette, typing-extensionsはfastapiやmangumの依存ライブラリです。
fastapi==0.103.1
pydantic==2.3.0
starlette==0.27.0
mangum==0.11.0
typing-extensions==4.7.1
#カスタムイメージを利用する場合
#awslambdaric==2.0.7
概要
FastAPIはAPIのエンドポイントを複数公開できますが、AWS Lambdaは1つだけしか公開できません。そこで、mangumというライブラリを使うことで、複数のエンドポイントを1つにまとめることができます。
mangumを利用する際はエンドポイントが1つになるため、リクエストBodyにパスやメソッドなどを設定することで、FastAPIの処理を分岐させます。
パラメーター名 | 概要 |
---|---|
path | エンドポイントのパス |
httpMethod | エンドポイントを叩く際のメソッド (GET, POST, DELETE) |
requestContext | API Gateway利用時に値が入る (AWSのアカウントIDなど) |
multiValueQueryStringParameters | POSTメソッドの利用時などで送りたいパラメーターを設定する |
サンプルコード
こちらで確認できます。
アプリ側のコード
使い方はすごく簡単です。以下のように、FastAPIのインスタンスを作成し、mangumの引数に渡すだけです。
ちなみに、ドキュメントを読む限りだと、mangumはデフォルトで8080ポートを使うようで、ポートの変更はできなさそうでした。
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
@app.get('/test1')
async def test1():
return {"message": "test"}
@app.post('/test2')
async def test():
return {'message': "test2"}
@app.post('/test3')
async def test1(message: str):
return {'message': message}
# export port 8080
handler = Mangum(app)
Dockerfile (AWSのイメージ利用する場合)
AWSが提供しているイメージを使う場合は以下のようになります。
requirements.txtに使用するPythonのライブラリのバージョンを記述してます。
特に引っかかる点はないと思うので、詳細はAWSのドキュメントやリポジトリを確認してみてください。
FROM public.ecr.aws/lambda/python:3.8
COPY app.py requirements.txt ./
RUN pip install -r ./requirements.txt
CMD ["app.handler"]
Dockerfile (カスタムイメージを利用する場合)
AWSが提供するイメージ (public.ecr.aws/lambda/python:3.8) が利用できない場合もあると思います。その場合は自身でカスタムイメージ (Pythonを入れた任意のDockerイメージ) にLambdaのランタイムを追加することになります。
まず、requirements.txtにLambdaのランタイムであるawslambdaricを追加します。
fastapi==0.103.1
pydantic==2.3.0
starlette==0.27.0
mangum==0.11.0
typing-extensions==4.7.1
awslambdaric==2.0.7
DockerfileではDocker起動時のデフォルトのディレクトリを/functionとし、アプリのコードを追加します。
最後にENTRYPOINTを設定し、awslambdaric経由でapp.handlerを動かします。
FROM python:3.11
WORKDIR /function
COPY app.py requirements.txt ./
RUN pip install -r ./requirements.txt
ENTRYPOINT [ "/usr/local/bin/python", "-m", "awslambdaric" ]
CMD ["app.handler"]
ビルド方法
AWS公式イメージ
AWSの公式イメージを利用する場合は、以下のようにDockerコンテナを起動します。
docker run -p 9000:8080 <docker image name>
カスタムイメージ
カスタムイメージを利用する場合はaws-lambda-rieをローカルにインストールします。
インストール方法はGitHubの方を参考にしてください。
Dockerの起動コマンドは以下のようになります。
docker run -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 --entrypoint /aws-lambda/aws-lambda-rie <docker image name> /usr/local/bin/python -m awslambdaric app.handler
ローカルでの動作確認
エンドポイントは http://localhost:9000/2015-03-31/functions/function/invocations
になります。
リクエストBodyには、path, httpMethod, requestContext, multiValueQueryStringParametersを設定します。
@app.get('/test1')
にアクセスしたい場合はpathを /test1, httpMethodをGETに設定し、リクエストします。
curl -X "POST" "http://localhost:9000/2015-03-31/functions/function/invocations" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"path": "/test1",
"requestContext": {},
"httpMethod": "GET",
"multiValueQueryStringParameters": {}
}'
@app.post('/test2')
にアクセスしたい場合は、httpMethodをPOSTにします。
curl -X "POST" "http://localhost:9000/2015-03-31/functions/function/invocations" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"path": "/test2",
"requestContext": {},
"httpMethod": "POST",
"multiValueQueryStringParameters": {}
}'
@app.post('/test3')
にアクセスしたい場合は、multiValueQueryStringParametersにmessageを追加します。
curl -X "POST" "http://localhost:9000/2015-03-31/functions/function/invocations" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"path": "/test3",
"requestContext": {},
"httpMethod": "POST",
"multiValueQueryStringParameters": {
"message": "hoge"
}
}'
AWSへのデプロイ
詳細は省きます。
まずローカルでイメージをビルドし、ECRにpushします。
次に、ECRにpushしたイメージを使ってLambdaにデプロイします。
ローカルではENTRYPOINTやCMDを指定していましたが、Lambdaでは指定する必要がないので、空欄にしておきます。
AWS Lambdaから直接URLを発行することもできますが、mangumが対応してないため、API Gatewayを経由させます。
POSTだけで良いので、リクエストをLambdaに送るように設定します。APIのタイプはREST APIです。
デプロイ後の動作確認はエンドポイントを変えるだけで、ローカルと同じです。
以上。
Discussion