AWSを用いたサーバーレスアーキテクチャの構築において、AWS LambdaからAmazon RDS(Auroraなど)への接続設計は、多くのインフラエンジニアが直面する大きな課題の一つです。急激なアクセス増加に伴いLambda関数が大量にスケールアウトすると、データベースへの接続コネクションが枯渇し、「Too Many Connections」といった接続エラーが発生します。
本記事では、この問題を解決する「Amazon RDS Proxy」を導入し、安全かつ高可用なデータベース接続環境を構築するステップバイステップの手順を解説します。
前提知識とRDS Proxyが必要な理由
AWS Lambdaはイベント駆動型でシームレスにスケーリングする特性を持っています。しかし、従来のデータベース(RDBMS)は、接続ごとにメモリやスレッドを消費するため、あらかじめ最大接続数が制限されています。
Lambda関数が直接RDSに接続する場合、Lambdaのインスタンスが増えるたびに新しい接続が確立され、すぐにデータベースの限界に達してしまいます。そこで仲介役としてRDS Proxyを導入します。
- コネクションプール管理: RDS Proxyがデータベースとの接続をあらかじめプール(保持)し、複数のLambdaインスタンス間で使い回すことで、データベースの負荷を大幅に軽減します。
- フェイルオーバーの高速化: データベースのプライマリインスタンスに障害が発生した際、接続先を迅速に切り替えるため、アプリケーション側のダウンタイムを最小限に抑えられます。
具体的な設定手順・設計方法
ここからは、安全なプライベートVPC内において、LambdaからRDS Proxyを経由してRDS(MySQL)へ接続する手順を解説します。
ステップ1:Secrets Managerへのデータベース情報の登録
RDS ProxyがRDSに接続するための認証情報を取得するために、AWS Secrets Managerを使用します。これにより、環境変数やコードにパスワードを生書きするリスクを排除します。
- AWSマネジメントコンソールで「Secrets Manager」を開き、「新しいシークレットを保存する」をクリックします。
- シークレットのタイプで「Amazon RDS データベースの資格情報」を選択し、ユーザー名とパスワードを入力して対象のRDSデータベースを選択します。
- シークレット名(例:
prod/rds/credentials)を入力して保存します。
ステップ2:RDS Proxy用のIAMロールの作成
RDS Proxyが、先ほどSecrets Managerに保存した認証情報を読み取るための権限(IAMロール)を作成します。
以下のポリシーを持つIAMロールを作成してください。信頼関係(Trust Relationship)には rds.amazonaws.com を指定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDecryptSecret",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/rds/credentials-*"
},
{
"Sid": "AllowDecryptKMS",
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-kms-key-id",
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.ap-northeast-1.amazonaws.com"
}
}
}
]
}
ステップ3:RDS Proxyの作成
次に、RDS Proxy本体を作成します。
- RDSのコンソールから「プロキシ」を選択し、「プロキシの作成」をクリックします。
- 任意のプロキシ名を入力し、エンジンに「MySQL」または「PostgreSQL」を選択します。
- 「対象データベース」で接続先のRDSを選択します。
- 「接続」セクションで、RDSと同じVPC、および2つ以上のサブネット(マルチAZ対応のため)を指定します。
- 「認証」セクションで、ステップ1で作成したSecrets Managerのシークレットと、ステップ2で作成したIAMロールを選択します。
- プロキシを作成します。ステータスが「利用可能」になったら、表示される「プロキシエンドポイント」を控えておきます。
ステップ4:Lambdaレイヤーの作成(PyMySQLの導入)
LambdaからMySQLに接続するため、今回はPythonの PyMySQL ライブラリを使用します。AWSの標準環境には含まれていないため、Lambdaレイヤーとして導入します。
ローカル環境(LinuxやmacOSのターミナル)で以下のコマンドを実行し、パッケージをzip化してLambdaにアップロードします。
# フォルダ構造を維持してインストール
mkdir -p python
pip install PyMySQL -t python/
# zipファイルに圧縮
zip -r pymysql_layer.zip python/
作成した pymysql_layer.zip をAWS Lambdaの「レイヤー」コンソールからアップロードし、カスタムレイヤーとして登録します。
ステップ5:Lambda関数の作成とプログラムの実装
VPC内で動作するLambda関数を作成し、先ほど登録したレイヤーを追加します。その後、以下のコードを記述して、RDS Proxy経由で接続テストを行います。環境変数 DB_PROXY_HOST には、RDS Proxyのエンドポイントを登録してください。
import os
import pymysql
# 環境変数からRDS Proxyのエンドポイントを取得
DB_PROXY_HOST = os.environ['DB_PROXY_HOST']
DB_USER = "admin"
DB_NAME = "mydb"
def lambda_handler(event, context):
# AWS Secrets ManagerやIAM認証を使うのがベストプラクティスですが、
# ここでは簡易的に環境変数または定義された値で接続確認を行います
db_password = "YourSecurePassword" # 実際はSecrets Managerから動的取得を推奨
try:
# RDS Proxyのエンドポイントへ接続
connection = pymysql.connect(
host=DB_PROXY_HOST,
user=DB_USER,
password=db_password,
database=DB_NAME,
connect_timeout=5
)
with connection.cursor() as cursor:
cursor.execute("SELECT version();")
result = cursor.fetchone()
print(f"Database version: {result}")
connection.close()
return {
'statusCode': 200,
'body': 'Successfully connected to RDS via RDS Proxy!'
}
except Exception as e:
print(f"Connection failed: {str(e)}")
raise e
実務での注意点
- セキュリティグループの設定(最小権限の原則): Lambda用のセキュリティグループからRDS Proxy用のセキュリティグループへの、ポート3306(MySQLの場合)のインバウンド通信のみを許可してください。同様に、RDS ProxyからRDS本体への接続のみを許可するよう、セキュリティグループのチェインを適切に設計します。
- ピン留め(Pinning)の回避: SQL内で一時テーブルの作成やセッション変数の変更を行うと、RDS Proxyはその接続を特定のLambdaインスタンス専用としてロック(ピン留め)してしまいます。これによりコネクションプールの共有効率が落ちるため、アプリケーションの設計段階でセッションレベルの変更を避ける工夫が必要です。
- ランニングコストの考慮: RDS Proxyは、データベースインスタンスのvCPU数や稼働時間に応じて課金されます。開発環境やアクセス頻度が極端に低いシステムでは、コスト対効果を考慮した上で導入を検討してください。
まとめ
サーバーレス環境において、急激なトラフィック変動に耐えうる堅牢なシステムを構築するには、RDS Proxyによるコネクション管理が極めて有効です。本記事で紹介したセキュリティグループの設計、Secrets Managerとの連携、そしてLambdaレイヤーを用いた接続方法を実践することで、実務に耐えうるセキュアで高可用なデータベース接続基盤を実現できます。