AWS APIGateway+Lambda+FastAPIにてCSVファイルの処理APIを作ってみた
はじめに
今回はLambda+FastAPI+PandasでのCSVファイル800行の処理速度を検証するためにAPIを作成してみた。
これは、私が作っているポートフォリオ用のアプリのための検証である。
検証手順
- Lambda+FastAPI をZipファイルでuploadしてLambdaが動くか検証
- LambdaをCSVアップロードの方式に書き換えて、実際にリクエストを送る
1.Lambda+FastAPI をZipファイルでuploadしてLambdaが動くか検証
lumbda_function.py
from fastapi import FastAPI, Request
from mangum import Mangum
import pandas as pd
from pydantic import BaseModel
from typing import List, Dict
app = FastAPI()
# 入力の定義(例:リストの辞書形式)
class InputData(BaseModel):
data: List[Dict[str, float]] # 例: [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
@app.post("/describe")
async def describe_data(input: InputData):
try:
# DataFrameに変換
df = pd.DataFrame(input.data)
# describe()をJSONに変換
result = df.describe().to_dict()
return {"summary": result}
except Exception as e:
return {"error": str(e)}
# Lambdaハンドラー
handler = Mangum(app)
リクエストBodyから辞書型をリストとして格納して、describe()メソッドでサマリーを表示するようになっています。
Mangumとは、LambdaからAPIGatewayに渡される特殊なリクエスト形式をFastAPIが処理できるASGIに変換するlibraryです。
build.ps1最終形態
# build.ps1
Write-Host "▶ Cleaning up..."
Remove-Item -Recurse -Force package, function.zip -ErrorAction SilentlyContinue
Write-Host "▶ Installing dependencies with Docker (Amazon Linux 2023)..."
docker run --rm -v "${PWD}:/app" -w /app public.ecr.aws/amazonlinux/amazonlinux:2023 `
bash -c "
yum update -y && \
yum install -y python3 python3-pip python3-devel gcc zip git make && \
alternatives --install /usr/bin/python python /usr/bin/python3 1 && \
alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 && \
python -m pip install --upgrade pip && \
mkdir -p /app/package && \
pip install -r /app/requirements.txt -t /app/package && \
cd /app/package && \
find . -name '*.pyc' -delete && \
find . -name '__pycache__' -type d -exec rm -r {} +
"
Write-Host "▶ Creating ZIP file..."
Copy-Item -Path .\lambda_function.py -Destination .\package\
Compress-Archive -Path .\package\* -DestinationPath function.zip
Write-Host "✅ 完了しました! → function.zip を Lambda にアップロードできます。"
requirements.txt最終形態
fastapi
mangum
numpy
pandas
pydantic==1.10.13
上記のBuild.ps1ファイルを実行して、Zipファイルを作成し、Lambdaにアップロードしました。
テストコードを実行です!
が・・・・
ここで少し嵌りました。何度テストしても、このZipファイルがLambda上でエラーになり、pydantic_coreが見つからないと怒られます。調べたらWindows上でDockerなしでBuildしたファイルとUbuntu上でBuildしたファイルでは、セットが異なるようで、Docker(AmazonLinux2023)上でBuildして、中身をZipファイルにつめないといけないとのことで、以下の文を追加しました。
mkdir -p /app/package && \
pip install -r /app/requirements.txt -t /app/package && \
cd /app/package && \
find . -name '*.pyc' -delete && \
find . -name '__pycache__' -type d -exec rm -r {} +
成功!と思いきや、またエラー。
嵌りました。
Pythonの実行VersionがLambda上とローカル(Docker上)でずれていたため、以下のエラーと格闘しました。
{
"errorMessage": "Unable to import module 'lambda_function': Unable to import required dependencies:\nnumpy: Error importing numpy: you should not try to import numpy from\n its source directory; please exit the numpy source tree, and relaunch\n your python interpreter from there.",
"errorType": "Runtime.ImportModuleError",
"requestId": "",
"stackTrace": []
}
.soファイルというnumpyのbuild済みのファイルが含まれているか確認しにいきました。
あるのになぜ??と思ったら、LambdaのランタイムのVersionとずれていました。
pydantic_coreのバージョンを指定したり、いろいろやったのですが、結局バージョンの違いがポイントだったようです。
上記エラーが解消!今度こそ!!と思ったのですが・・・・
またもや、エラーです。
リクエストの形式がHTTP形式になっていないからInternalSeverErrorが出ていたようで、sourceIPを追記することで、テストが正しく実行できました!
テストコード
{
"version": "2.0",
"routeKey": "POST /describe",
"rawPath": "/describe",
"rawQueryString": "",
"requestContext": {
"http": {
"method": "POST",
"path": "/describe",
"sourceIp": "127.0.0.1",
"userAgent": "test-client"
}
},
"headers": {
"content-type": "application/json"
},
"isBase64Encoded": false,
"body": "{\"data\": [{\"a\": 1, \"b\": 2}, {\"a\": 3, \"b\": 4}]}"
}
やった~!!!
2. LambdaをCSVアップロードの方式に書き換えて、実際にリクエストを送る
from fastapi import FastAPI, UploadFile, File
from mangum import Mangum
import pandas as pd
app = FastAPI()
@app.post("/describe")
async def describe_csv(file: UploadFile = File(...)):
try:
# CSVファイルを読み込み
contents = await file.read()
df = pd.read_csv(pd.io.common.BytesIO(contents))
# describe()をJSONに変換
result = df.describe().to_dict()
return {"summary": result}
except Exception as e:
return {"error": str(e)}
# Lambdaハンドラー
handler = Mangum(app)
これは、テストがLambda単体では、できない形式となっているので、APIGateway経由で実際にリクエストしてテストします!!
/describeでリクエストを受ける仕様なので、APIGatewayの/describeにPOSTメソッドで、プロキシ統合をOnにして、Lambdaをトリガーに設定する。
プロキシ統合をOnにしないと、受け取ったリクエストが、Mangumの期待する生のデータでなくなるようです。
APIをデプロイで新しいステージにデプロイする。
curlコマンドで、実際にCSVを渡してみて、反応をみた。
curl -X POST https://youraddress.execute-api.ap-northeast-1.amazonaws.com/dev/describe \
-H "Content-Type: multipart/form-data" \
-F "file=@./simulated_data.csv"
失敗・・・
libraryの中にpython-multipartモジュールが足りないようなので、
追記して、zipファイルを再度アップロード
requirements.txt
fastapi
mangum
numpy
pandas
pydantic==1.10.13
python-multipart
無事成功!!
良かった!!
Lambdaがコールドスタートのせいで、最初の処理がやや遅いです。
でも、できた!!!