RDS Proxyを使った実験

こんにちは、エンジニアの野田です。
今回はAWSのサービス「RDS Proxy」について試してみたので
その結果を書いていきます。

RDS Proxyとは
その名の通りRDSの前に置かれるプロキシです。

使う目的
RDSではインスタンスタイプによって同時接続数が決まっています。
この接続数が埋まっている状態でアクセスが来た場合には接続失敗となります。
この接続失敗を避けるために、RDS Proxyを置きます。
こうすることによりRDS Proxyが同時接続数を超えた分の接続を待ち状態にし、
RDSが接続可能になるとRDSに接続してくれます。

いつ使うか
Lambdaでサーバーレスなシステムを構築するときです。
EC2などからRDSを使うときはいいのですが、問題はLambdaでサーバーレスな構成にしたときです。
Lambdaは性質上アクセスが来た分だけ起動します。
つまり大量アクセスが来てしまうと、その分全部RDSに接続しようとしますが、
ここで同時接続数を超えてしまいます。
なので今まではLambdaとRDSはアンチパターンとされてきました。

実験
実際ちゃんと動くか検証してみました。
方法としてはRDSからSELECTするプログラムをLambdaで書き、
apache JmeterからAPI Gateway経由でこれを叩きます。

条件
RDS:Aurora MySQLを使用、インスタンスタイプはt3.small
デフォルトの同時接続数は45(あくまで目安で実際は違います)
軽い処理だとさばいてしまうので、
10万レコード用意したテーブルからindex貼っていないカラムを指定してSELECTします。

計測ツール:apache Jmeter
100スレッド
10ループ
Ramp-Up 10秒

結果

RDS Proxyを使わない場合
1000リクエスト中
成功:637
失敗:363
成功したリクエストの処理時間は100ミリ秒~1000ミリ秒でした。

RDS Proxyを使った場合
1000リクエスト中
成功:1000
失敗:0
リクエストの処理時間は
同時接続数内のリクエストについてはRDS Proxyを使わない場合と同じ100ミリ秒~1000ミリ秒。
同時接続数を超えて待ちになった分のリクエストは2000ミリ秒前後でした。

まとめ
RDS Proxyを使うことで確かにすべてのリクエストをさばくことが確認できました。
注意点としては同時接続数を超えて待ちになった分のリクエストが2000ミリ秒前後かかることです。
実際にはもっとたくさんソースコードが書かれたプログラムになるのでもう少しかかると思います。
ただ同時接続内だと時間は変わらないので、非ピーク時は大丈夫だと思います。
判断基準としてはピーク時に時間がかかっても大丈夫かどうかだと思いました。
そこをクリアできれば今回のようにLambdaでもRDSを使った構成が実現できると思いました。

こんにちは、野田です。
1年以上ぶりの投稿になります。

今回はLambda×Pythonのフレームワークについて書きたいと思います。

最近AWSのLambdaで開発をすることが多く、何かフレームワークがないか探していました。
Pythonのフレームワークはいくつかありますが、特にAWSのLambdaに特化したフレームワークが
見つからなかったので、最小構成でアプリケーションを作るにはどうするか考えてみました。

結論からいうとSAMを使った開発が効率がいいかなと思いました。
SAMはAWSでサーバーレスアプリケーションを作るためのフレームワークです。
AWS SAM CLIを使用して開発を進めます。
SAMの使い方についてはほかにたくさん記事が出ているのでここでは割愛します。

ディレクトリ構成は以下です。
SAMを使う上で最低限必要なファイルと、アプリケーションを構成するファイルでできています。

├─ Application/
│ ├─ application/
│ │ ├─ __init__.py
│ │ ├─ config.py
│ │ ├─ dynamo.py
│ │ ├─ mod1.py
│ │ └─ mod2.py
│ │
│ ├─ Func1.py
│ ├─ Func2.py
│ └─ requirements.txt

└─ template.yaml

SAMを使ってデプロイする際に必要なのは
template.yaml
requirements.txt
の2つです。
普通、SAMを使う場合は最初にコマンドでプロジェクト作成して、
自動的にディレクトリやファイルが作成されたあと開発していきますが、
この2つがあれば動きます。

【template.yaml】
SAMはデプロイするときにCloudFormationを使ってLambda関数を作ります。
CloudFormationで使用する関数の設定ファイルです。

1行目AWSTemplateFormatVersionはテンプレート形式バージョンで
2020年11月現在有効な値は2020-09-09です。
2行目TransformはCloudFormationがテンプレートを処理するためのマクロで、こちらもこのままで大丈夫です。
3行目Descriptionはこのテンプレートの説明で任意の文字列です。
特に機能に関与しません。
4行目Resourcesの下から、関数の数分定義していきます。
6行目のFunctionsNameとしているところはこのテンプレート内で使用される名前です。
9行目のFunctionsNameはLambdaに登録される関数名となります。
12行目CodeUriで関数のファイルの場所を相対パスで指定します。
この例で使う関数ファイルはFunc1.pyとFunc2.pyでApplicationの直下に置いているので./Applicationとなります。
10行目は、ファイルの中のどの関数を実行するかの指定です。
例ではFunc1.pyのlambda_handlerという関数を実行したいので、Func1.lambda_handlerになります。
13行目以下はLambdaの設定です。
Environment(環境変数)でTZに対してAsia/Tokyoを指定しておくと
pythonのプログラムの中でタイムゾーンを気にする必要がなくなります。

【Func1.py】
これが実際にLambdaで動く中身です。

やっていることとしては、
・標準ライブラリの読み込み
・自作モジュールの読み込み
・ログ設定(logging)
・メイン処理
です。

application/に機能ごとにモジュールを作って置いてます。
application/は__init__.pyでパッケージ化しています。
例えばDynamoDBを使う場合は↓のような感じでモジュールを書いて、Func1で呼び出して使います。
application/dynamo.py


Func1.py

モジュール内でログを使う場合は、モジュールのファイル内でログ設定を書きます。
定数などの設定値はすべてconfigにまとめています。
なのでほぼすべてのファイルでconfigをimportしてます。

課題としては、
・よく使う機能(S3やRDSなど)を汎用的に使えるようにモジュール化する。
・本番やSTGなど複数環境がある場合にどうするか考える(現在は環境分ファイルを作ってます。)
といったことが挙げられます。

サーバーレス案件はこれから増えていくと思うので、
より効率的に開発できるフレームワークを作りたいと思います。