📑
FastAPI,mangumをコンテナ化してAPIにする
過去ログ
1回目
2回目
やりたいこと
前回やれたこと
構成図
2回目の記事でFastAPI,mangumを利用して、swagger-ui上でのAPI管理ができるようになりました。これらをコンテナ化した上で、Lambda+API Gatewayを作ってテストを行います。
SAMのDeployではなくCLIでのデプロイとしています。SAMは次回にでも。
ファイル/フォルダ構成
sam-app-1
- events
|- event.json
- hello_world
|- Dockerfile <- 新規作成
|- app.py
|- __init__.py
|- requirements.txt
- tests
|- __init__.py
|- integration
|- __init__.py
|- test_api_gateway.py
|- requirements.txt
|- unit
|- __init__.py
|- test_api_gateway.py
- __init__.py
- README.md
- response.json
- samconfig.toml
- template.yaml
ファイル準備
Dockerfile
./sam-app-1/hello_world/Dockerfile
を作成します。
基本的な記載方法はAWS公式に従いますが、pythonのVersionは3.9を利用します。
またCMDはapp.pyに記載されたハンドラーの名前となります
FROM public.ecr.aws/lambda/python:3.9
# Install the function's dependencies using file requirements.txt
# from your project folder.
COPY requirements.txt .
RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
# Copy function code
COPY app.py ${LAMBDA_TASK_ROOT}
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.lambda_handler" ]
app.py
変更点は無いですが、再度掲載
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
@app.get("/hello")
def root():
return {"message": "Hello World"}
lambda_handler = Mangum(app)
requirements.txt
変更点は無いですが、再度掲載
requests
fastapi
mangum
環境構築
ECRリポジトリ準備
aws ecr create-repository --repository-name hello_world
Docker準備
# AWS情報取得
REGION=$(aws configure get region)
ACCOUNTID=$(aws sts get-caller-identity --output text --query Account)
# build
docker build -t hello_world .
# tag設定
docker tag hello_world:latest ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/hello_world:latest
# ECRログイン
aws ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com
# ECRへpush
docker push ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/hello_world:latest
IAM準備
権限の問題でCloud9ではエラーとなるので、ここはCloudShellで実行下さい
# Role作成
aws iam create-role --role-name lambda-hello_world \
--assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
Lambda準備
# Role変数設定
ROLE_ARN=arn:aws:iam::${ACCOUNTID}:role/lambda-hello_world
# DIGEST取得
DIGEST=$(aws ecr list-images --repository-name hello_world --out text --query 'imageIds[?imageTag==`latest`].imageDigest')
# Lambda関数作成
aws lambda create-function \
--function-name hello_world \
--package-type Image \
--code ImageUri=${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/hello_world@${DIGEST} \
--role ${ROLE_ARN}
Lambdaテスト
実行結果の"body"が{"message":"Hello World"}であることを確認できます
# payload準備
cat << EOF > payload.json
{
"resource": "/",
"path": "/hello",
"httpMethod": "GET",
"isBase64Encoded": true,
"queryStringParameters": {
"foo": "bar"
},
"requestContext": {
"httpMethod": "GET"
}
}
EOF
# Lambda実行
aws lambda invoke \
--function-name hello_world \
--payload file://payload.json \
--cli-binary-format raw-in-base64-out \
response.json
# レスポンス確認
cat response.json
# ゴミ削除
rm payload.json response.json
API Gateway作成
# REST APIの作成
aws apigateway create-rest-api \
--name 'api_hello_world' \
--description 'api_hello_world' \
--endpoint-configuration types=REGIONAL
# API IDの取得
apigateway_id=$(aws apigateway get-rest-apis \
--query 'items[?name==`api_hello_world`].id' \
--output text);echo ${apigateway_id}
# リソース IDの取得
resource_id=$(aws apigateway get-resources \
--rest-api-id ${apigateway_id} \
--query 'items[?path==`/`].id' \
--output text);echo ${resource_id}
# リソースの作成
aws apigateway create-resource \
--rest-api-id ${apigateway_id} \
--parent-id ${resource_id} \
--path-part '{proxy+}'
# リソース IDの取得
resource_id_proxy=$(aws apigateway get-resources \
--rest-api-id ${apigateway_id} \
--query 'items[?path==`/{proxy+}`].id' \
--output text);echo ${resource_id_proxy}
# メソッドの作成
aws apigateway put-method \
--rest-api-id ${apigateway_id} \
--resource-id ${resource_id_proxy} \
--http-method GET \
--authorization-type "NONE" \
--no-api-key-required
# インテグレーションの設定
aws apigateway put-integration \
--rest-api-id ${apigateway_id} \
--resource-id ${resource_id_proxy} \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri "arn:aws:apigateway:${REGION}:lambda:path/2015-03-31/functions/arn:aws:lambda:${REGION}:${ACCOUNTID}:function:hello_world/invocations"
# Lambda関数の実行権限付与
aws lambda add-permission \
--function-name hello_world \
--statement-id hello_world \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:${REGION}:${ACCOUNTID}:${apigateway_id}/*/*/*"
# APIのデプロイ
aws apigateway create-deployment \
--rest-api-id ${apigateway_id} \
--stage-name default \
--stage-description default \
--description default
APIテスト
実行(成功例)
URI="https://${apigateway_id}.execute-api.${REGION}.amazonaws.com/default/hello"
curl -H "Content-Type: application/json" -X GET ${URI}
結果(成功例)
{"message":"Hello World"}
実行(失敗例)
URI="https://${apigateway_id}.execute-api.${REGION}.amazonaws.com/default/hello-test"
curl -H "Content-Type: application/json" -X GET ${URI}
結果(失敗例)
{"detail":"Not Found"}
まとめ
FastAPI,mangumでswagger-uiでAPIを管理しつつ、コンテナ化まで出来ました。
参考
Discussion