1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS APIGateway+Lambda+FastAPIにてCSVファイルの処理APIを作ってみた(忘備録)

Posted at

AWS APIGateway+Lambda+FastAPIにてCSVファイルの処理APIを作ってみた

はじめに

今回はLambda+FastAPI+PandasでのCSVファイル800行の処理速度を検証するためにAPIを作成してみた。
これは、私が作っているポートフォリオ用のアプリのための検証である。

検証手順

  1. Lambda+FastAPI をZipファイルでuploadしてLambdaが動くか検証
  2. 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をデプロイで新しいステージにデプロイする。

image.png

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がコールドスタートのせいで、最初の処理がやや遅いです。
でも、できた!!!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?