概要
API Gateway + Lambda + RDS Proxyの構築方法を紹介します。
RDSの構築までは以下の記事で紹介しましたので、この記事ではそこからRDS Proxy経由に変更する手順を紹介します。
経緯
AWS LambdaからRDSにアクセスするたびにDB接続が確立すると、接続数の急増によりリソースが逼迫することがあります。最大接続数を超えた場合はToo many connections
になり、アプリがエラーを返すこともあります。
RDS Proxyを使うとコネクションプーリングを利用できるのでDBコネクションの使い回し&メモリ負荷の軽減が可能!と言うことで、設定を実施しました。
Amazon RDS Proxy を使用すると、アプリケーションでデータベース接続をプールおよび共有して、アプリケーションのスケーリング能力を向上させることができます。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-proxy.html
手順
- ①Secrets Managerのシークレット作成
- ②SecurityGroup設定
- ③RDS Proxy作成
- ④LambdaからRDSへのエンドポイントを変更
①Secrets Managerのシークレット作成
(RDS Proxy作成時にシークレットが必要なので作成します)
- Secrets Manager -> 「新しいシークレットを保存する」を選択
- シークレットのタイプ:「Amazon RDS データベースの認証情報」を選択
- 認証情報:ユーザー名とパスワードに、それぞれRDSの情報を入力
- データベース:該当のDBを選択
- シークレットの名前:任意に命名
②SecurityGroup設定
- RDS Proxy用のセキュリティグループを作成
- インバウンドルールに、Lambda用のセキュリティグループを指定
- RDS用のセキュリティグループのインバウンドルールに上記で作成したものを追加
- インバウンドルールにLambda用のセキュリティグループが残っていれば削除します
※RDS用とRDS Proxy用のセキュリティグループは別々にしてください。疎通エラーになることがあります。詳細は以下の記事で紹介
③RDS Proxy作成
- RDS -> プロキシ -> 「プロキシの作成」を選択
- エンジンファミリー:RDSのエンジンを指定
- プロキシ識別子:任意に命名(プロキシ間で一意である必要あり)
- データベース:該当のDBを選択
- Secrets Managerのシークレット:①で作成したシークレットを選択
- サブネット:該当VPC内のサブネットを二つ指定(最低二つです)
- 「追加の接続設定」の「セキュリティグループ」:上記②で作成したRDS Proxy用のセキュリティグループを選択
- プロキシが作成されたら「プロキシエンドポイント」からエンドポイントをコピー
④LambdaからRDSへのエンドポイントを変更
- Lambda関数の「環境変数」のDBホスト名をコピーしたもので置き換える
以上で構築は完了です。
これでAPI GatewayのURLを叩けば、RDS Proxy経由になっているはずです。
CloudwatchlogsでRDS Proxy用のロググループが作成されるので、そこから確認も可能です。
RDS Proxyにすると処理性能が劣化する?
いざRDS Proxy経由にしてしばらくリクエストを投げっぱなしにすると、Proxy経由前よりもメモリ消費は確実に抑えられることがわかりました。
ただ、(私が使用したアプリの場合は)代わりに単発のリクエスト性能が悪化することもわかりました。
調べると同じような現象の記事が多く見られます。
※全ての記事でそのように言われているわけではありませんので、100%該当する、と言うわけではない?
RDS Proxy無しの時より少しパフォーマンスが悪化しています。
https://dev.classmethod.jp/articles/rds-proxy-connect-benchmark/
代わりに若干平均応答時間が遅くなっているようです。
https://spirits.appirits.com/role/engineer/17388
RDS Proxyを挟むと平均で約2.6ms、90パーセンタイルで約2.5ms程度のオーバーヘッドが発生していることが分かります。
https://dev.classmethod.jp/articles/calc-rds-proxy-latency/
これは、コネクションプーリングの初期化で時間が要するため、単発リクエストの性能が劣化するのでは?と思っています。私が使用したプログラムの場合、以下のような結果となりました。
- RDS Proxy経由では無い場合:単発1.5 - 2.5秒以内
- RDS Proxy経由の場合:単発4.0 - 4.5秒以内
VPCにあるとENI作成のため遅くなる?とも思いましたが、RDS直接の時もLambdaをVPCに置いていたので、今回の劣化には関係なさそうです。また、RDSのスペックを2段階あげて実施してみましたが、それでもやはりProxy経由ではない場合より、遅い結果となりました。
と言うことで、以下が私なりの検証結果です。
- RDS Proxy経由だとコネクションプーリングがあるので、連続したリクエストが続く場合は、DB接続数が減り、メモリ消費量も抑えられる。また、最大接続数を超えた場合の
Too many connections
エラーを回避できる - ただし、RDS Proxy経由だと(コネクションプーリングのオーバーヘッドのため?)単発リクエストの性能は劣化する
単発性能を劣化させずにRDS Proxy経由を実現できるよ!と言う方法をご存知の方がいましたら、ぜひ教えてください。