【AWS】API Gateway + Lambda(FastAPI) + RDSの構築手順

# 概要
API Gateway + Lambda + RDSの構築方法を紹介します。

### 前提
- FastAPIのアプリケーション（作成済）をLambdaで動かす
    - そのため後で設定するハンドラは`main.py`とします
- RDSとそのセキュリティグループは作成済み
- Lambdaのアーキテクチャは`arm64`、ランタイムは`Python3.12`で構築する

# 手順
- ①アプリケーションのZIP化
- ②ライブラリパッケージの作成
- ③SecurityGroup設定
- ④LambdaLayer作成
- ⑤Lambda関数作成
- ⑥API Gateway作成

## ①アプリケーションのZIP化
まずはローカルで良いのでアプリをZIPにします。

`Mangum`のようなアダプターを利用することで、LambdaでFastAPIを動かすことができます。
詳細は以下記事をご参考ください（これより下の手順も大いに参考にさせていただいています）。

https://qiita.com/Shinkijigyo_no_Hitsuji/items/cedd1825e5437663d3ce

## ②ライブラリパッケージの作成
- `requirements.txt`に任意のライブラリを記載の上、以下のスクリプトを実行してライブラリのzipを作成します

```sh
mkdir python

pip install \
--platform manylinux2014_aarch64 \
--target=python/python/lib/python3.12/site-packages/ \
--implementation cp \
--python-version 3.12 \
--only-binary=:all: \
--upgrade \
--no-deps \
-r requirements.txt

cd python

zip -r ../fastapi-layer.zip .

cd ..

rm -rf python
```

スクリプト参考：

https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-native-libraries

エラー「`No module named 'pydantic_core._pydantic_core'`」が出た場合アーキテクチャ違いの可能性があります。以下の別記事をご参考ください。

https://qiita.com/Ryo-0131/items/680a1ee53ff86728dfe7

## ③Lambda用のSecurityGroup作成・設定
- EC2 -> SecurityGroup -> 作成 -> 該当VPCを選択して作成。ルールは無しでOK
- RDSのセキュリティグループのインバウンドルールに上記で作成したセキュリティグループを追加
    - タイプ：「MYSQL/Aurora」
    - ポート範囲：3306


## ④LambdaLayer作成
- Lambda -> レイヤー -> 作成 -> ②で作成したzipをアップロード
    - アーキテクチャ：`arm64`
    - ランタイム：`Python3.12`

## ⑤Lambda関数作成
- Lambda -> 関数 -> 作成
    - アーキテクチャ：`arm64`
    - ランタイム：`Python3.12`
- 「コード」タブ -> 「コードソース」-> 「アップロード」-> ①で作成したアプリzipをアップロード
- 「コード」タブ -> 「ランタイム設定 」 -> ハンドラを「`main.handler`」に変更
    - 階層指定も可能です。`app.main.handler`みたいな感じです
- 「コード」タブ -> 「レイヤー 」 -> 「カスタムレイヤー」 -> ④で作成したLayerとバージョンを選択
- 「設定」タブ -> 「一般設定」-> （必要であれば）メモリやタイムアウトを任意に設定
- 「設定」タブ -> 「環境変数」-> シート「環境変数」にRDSの情報やアプリで使うENV設定を登録
    - これはLambda設定の「RDS データベース接続」でも可能と思います。今回はENVで使っているものをまとめて環境変数で登録しました
- 「設定」タブ -> 「VPC」 -> 該当VPCを選択
    - サブネットはRDSがあるサブネットを選択
    - セキュリティグループは上記③で作成したSGを選択
- 「テスト」タブ -> テスト
    - テンプレートは「`apigateway-aws-proxy`」を選択
    - イベントJSON内の`"path"`をアプリで使うパスに変更（`/test`など）
    - イベントJSON内の`"httpMethod"`を`GET`に変更
    - これで「テスト」を実行し、「実行結果」が成功することを確認します

## ⑥API Gateway作成
- API Gateway -> RESTAPI -> 構築
- API -> メソッドの作成 -> 以下を指定して作成
    - メソッドタイプ：「ANY」
    - 統合タイプ：「Lambda関数」
    - Lambdaプロキシ統合：「オン」にする
    - Lambda関数：⑤で作成した関数を選択
- API -> リソースを作成
    - プロキシのリソース：「オン」にする
    - リソースパス：`/`のまま
    - リソース名：「`{proxy+}`」
- API -> リソース -> 「/`{proxy+}` - ANY」を選択 -> 「統合を編集」を選択
    - 「統合タイプ」：「Lambda 関数」
    - Lambdaプロキシ統合：「オン」にする
    - Lambda関数：⑤で作成した関数を選択
- API -> リソース -> 「APIをデプロイ」選択
    - ステージ：「新しいステージ」選択
    - ステージ名：任意のステージ名を入力（`dev`や`prd`など）
- API -> ステージ -> 「URL を呼び出す」のURLをコピーしてアクセス

これでURLにアクセスできればOKです！

ちなみに上記の「Lambdaプロキシ統合」がオフになっている場合、

> The adapter was unable to infer a handler to use for the event.

というエラーになると思います。詳細は以下記事で紹介しました。

https://qiita.com/Ryo-0131/items/fc9aaef68e584ebac193


以上、構築手順でした。

## LambdaとRDSは相性が良くない？
調べるとLambdaとRDSは相性が悪いという記事が多いです。
一言で言うと「短時間で大量のデータベースコネクションが発生」する可能性があるからです。

> 理由はLambdaからRDSのDBコネクションを貼ると
リクエスト単位でコネクションを張ってしまうため
仕組み上、同時接続に耐えられません
（RDSのコネクション上限数が少ない）
https://qiita.com/teradonburi/items/86400ea82a65699672ad


> LambdaとRDSの相性の悪さはデータベースコネクション数の最大値にあります。 Lambdaはその特性上、リクエストに応じて大量にコンテナが実行されます。 このとき起動されるコンテナの数を制御できないため、大量のコンテナからデータベースへのコネクションが発生すると上限を超えてしまう事態が発生するようです。
https://spirits.appirits.com/role/engineer/17388/

RDS Proxyを使うことで上記問題を極力解決に近づけることはできるのですが、上記記事にあるように代わりに若干平均応答時間が落ちるという現象が見られました。別途記事にします。

と言うことで、RDSを使う場合は、上記問題を認識した上で許容できるか確認の上、検討されてください。