【AWS】API Gateway + Lambda + RDSでProvisioned Concurrency(プロビジョニングされた同時実行)を設定する

# 概要
API Gateway + Lambda + RDSでProvisioned Concurrency(プロビジョニングされた同時実行)を設定する方法を紹介します。

RDSの構築までは以下の記事で紹介しましたので、この記事ではそこからProvisioned Concurrencyを設定する手順を紹介します。

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

### 前提
Lambdaではリクエストがある度に実行環境を作成します。一度起動したものはずっと保持しているのではなく、少し経つと破棄されてしまいます（明確に決まっているわけではなさそうですが体感では5分くらい。不特定期間らしい？）。

そうすると「Lambda関数インスタンスの生成」がオーバーヘッドになり、初期リクエストの性能が劣化するという「コールドスタート問題」が発生します。
※逆に、環境が落ちる前にリクエストが来れば同じ実行環境を再利用できるので、性能の劣化は起きません

この問題を解決すべく2019年から登場したのが、Provisioned Concurrency。（それより以前は CloudWatch Eventsで定期的にキックするという手法が使われていたらしいです）

これにより、Lambdaの同時実行環境（関数インスタンス）を、あらかじめプロビジョニングされた状態にしておくことができます。プロビジョニング（予約）された状態、と言うのは、常に実行環境が待機してくれているわけなので、「ウォームスタート」の状態、と言う意味になります。

ただし、プロビジョニングされている時間に対して課金されるので、そこは要注意です（あまりやりすぎると普通にEC2の方が割安になったりする？）。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/provisioned-concurrency.html


# 手順
- ①Lambdaバージョン発行/エイリアス作成
- ②API GatewayのARNを取得
- ③リソースベースのポリシーステートメントを設定する
- ④プロビジョニングされた同時実行を設定する
- ⑤統合リクエストの関数に`${stageVariables.alias}`を指定する
- ⑥ステージ変数を登録する
- ⑦APIをデプロイ

## ①Lambdaバージョン発行/エイリアス作成
（「プロビジョニングされた同時実行の設定」では必ずエイリアスかバージョンの選択が必要になるため）

- Lambda関数 -> 「バージョン」タブ -> 「新規バージョンを発行」
- Lambda関数 -> 「エイリアス」タブ -> 「新規エイリアスの作成」
    - 名前：`dev`など命名する
    - バージョン：`$LATEST`以外を選択する

※`$LATEST`を選択するとProvisionすることができませんので、それ以外を選びます。よくあるのは`dev`（=`version1`）と`prod`（=`$LATEST`）でエイリアスを切って、`dev`をAPI Gatewayと紐づける、と言う形かと思います。

> $LATEST を指すエイリアスにプロビジョニングされた同時実行を割り当てることはできません。関数のバージョンを指すエイリアスを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/16dfbf13-2873-1b86-8f00-da654dbcd937.png)

## ②API GatewayのARNを取得
- API Gateway -> API -> リソース -> 該当メソッドのARNをコピーする
    - 例：`arn:aws:execute-api:ap-northeast-1:01234568901:abcdefghijk/*/*/`

## ③リソースベースのポリシーステートメントを設定する
（CLI利用可能な場合は⑥で表示されるコマンドを使えるのでスキップしてください）

- Lambda関数 -> 「エイリアス」タブ -> 作成したエイリアスを選択 -> アクセス権限 -> リソースベースのポリシーステートメント -> アクセス権限の追加
    - ポリシーステートメント：AWS のサービス
    - サービス：API Gateway
    - ステートメント ID：一意のIDにする
    - プリンシパル：`apigateway.amazonaws.com`　（デフォルト）
    - ソース ARN：上記②でコピーしたARNを貼り付け
    - アクション： `lambda:InvokeFunction`
 
※上記は以下CLI（以下⑤のAPI Gateway側で設定する際に表示されるコマンド）でも実行可能です。CLIが制限されている環境の場合は上記の手順で実施します

```
aws lambda add-permission \
--function-name "arn:aws:lambda:ap-northeast-1:01234568901:function:sample-api-gateway:${stageVariables.alias}" \
--source-arn "arn:aws:execute-api:ap-northeast-1:01234568901:abcdefghijk/*/*/" \
--principal apigateway.amazonaws.com \
--statement-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx \
--action lambda:InvokeFunction
```

## ④プロビジョニングされた同時実行を設定する
- Lambda関数 ->「エイリアス」タブ -> 作成したエイリアスを選択 -> 「プロビジョニングされた同時実行」を選択
    - 任意の台数を記入

少し経つと起動し始めます。
LambdaのCloudwatchlogsのロググループに、その分だけログストリームが作成されますので、そこで確認が可能です。また、ストリーム名にはエイリアスに紐づくバージョン番号(`[1]`とか)が入っているので、わかりやすいですね。

## ⑤統合リクエストの関数に`${stageVariables.alias}`を指定する
- API Gateway -> API -> リソース -> 統合リクエスト
    - Lambda 関数：「sample-api-gateway」を「`sample-api-gateway:${stageVariables.alias}`」に変更。プルダウンメニューに見えますが、直接追加記入が可能です（少しわかりづらい笑）
    - 「/`{proxy+}`」もある場合はそちらも同様に設定

この設定変更時に以下のように親切にメッセージが表示されます。
これが上述の③で行った設定をCLIで行う場合のコマンドです。以下の「許可の追加コマンド」でコマンドが出てくるのでそれでも可能です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/b0c25797-5074-f028-70d6-224ce7cc1e5c.png)


## ⑥ステージ変数を登録する
- API Gateway -> API -> ステージ変数 -> 該当ステージ名を選択し、以下を変数＆値として登録
    - （例）`alias`：`dev`

※変数名`alias`は`stageVariables.alias`の`alias`と一致させます。
これにより、Lambdaで設定したエイリアス名を指定することができます。

## ⑦APIをデプロイ
- API Gateway -> API -> リソース -> APIをデプロイ

1-2分で反映されると思います。これによりAPI Gatewayから、プロビジョニングされているLambdaバージョンのエイリアスにアクセスすることが可能になります。