はじめに
AWSのサーバーレスアーキテクチャを構築する際、AWS LambdaからAmazon RDS(リレーショナルデータベース)への接続は非常によくある構成です。しかし、Lambdaの急激なスケールアウトに伴い、DBの最大接続数(コネクション数)が枯渇してシステムがダウンするというトラブルが頻発します。
この課題を解決するのがAmazon RDS Proxyです。本記事では、LambdaからRDS Proxyを経由してデータベースへ安全かつ高速に接続するための具体的な設定手順と、実務でのベストプラクティスをステップバイステップで解説します。
前提知識/必要な理由
Lambda×RDBで発生する「コネクション枯渇問題」
Lambda関数は、リクエストの増加に応じて自動的にインスタンスがスケールアウトします。それぞれのLambdaインスタンスがDB接続を確立すると、一瞬でDBの最大接続上限に達してしまい、新規の接続要求がエラー(Too many connections)になります。
RDS Proxyを導入するメリット
- コネクションプーリング: データベース接続をプールして再利用するため、DBのメモリやCPU負荷を大幅に削減します。
- フェイルオーバーの高速化: DBがマルチAZでフェイルオーバーした際、接続の切り替えをProxyが吸収するため、アプリケーション側のエラーを抑え、復旧時間を30%〜60%短縮します。
- セキュリティの向上: IAM認証とAWS Secrets Managerを統合することで、Lambdaコード内にDBパスワードをハードコードする必要がなくなります。
具体的な設定手順・設計方法
今回は「Python(PyMySQL)」を使用し、Aurora MySQL/RDS MySQLへ接続する構成を例に解説します。
Step 1: Secrets Managerでデータベースの認証情報を保存する
RDS Proxyは、データベースに接続するためにSecrets Managerから認証情報を取得します。
- AWS Secrets Managerコンソールを開き、「新しいシークレットを保存する」をクリックします。
- シークレットのタイプで「Amazon RDS データベースの認証情報」を選択します。
- DBのユーザー名とパスワードを入力し、対象のRDSデータベースを選択します。
- 任意のシークレット名(例:
rds/my-db-secret)を付けて保存します。
Step 2: RDS Proxy用のIAMロールを作成する
RDS ProxyがSecrets Managerからシークレットを読み取るための権限(IAMロール)を作成します。
以下のポリシーをアタッチしたIAMロール(信頼されたエンティティ: rds.amazonaws.com)を作成してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDecryptSecret",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"kms:Decrypt"
],
"Resource": [
"arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:rds/my-db-secret-*"
]
}
]
}
Step 3: RDS Proxyを作成する
- RDSコンソールの「プロキシ」メニューから「プロキシの作成」をクリックします。
- プロキシ識別子を入力し、対象のデータベースを選択します。
- 接続: Secrets Managerで作成したシークレットと、Step 2で作成したIAMロールを指定します。
- ネットワーク: 対象のRDSと同じVPC、および少なくとも2つの異なるアベイラビリティーゾーン(AZ)のサブネットを選択します。
- セキュリティグループ: 以下のルールを設定したセキュリティグループを作成し、Proxyに付与します。
- インバウンドルール: Lambdaのセキュリティグループからの「ポート3306」の通信を許可
- アウトバウンドルール: RDSのセキュリティグループへの「ポート3306」の通信を許可
Step 4: Lambda関数の作成とコードの実装
Lambda関数をRDS Proxyと同じVPC内に配置し、接続用コードを実装します。
【重要】外部ライブラリ(PyMySQL)の導入手順
AWS Lambdaの標準環境には pymysql ライブラリが含まれていません。以下のコマンドでLambdaレイヤーを作成してアップロードするか、デプロイパッケージ(ZIP)に同梱してください。
# ローカルの作業ディレクトリで実行
mkdir -p python
pip install pymysql -t python/
zip -r pymysql-layer.zip python/
作成した pymysql-layer.zip をAWS Lambdaの「レイヤー」として登録し、対象のLambda関数にアタッチします。
Lambda関数の接続コード例
import os
import pymysql
# 環境変数からRDS ProxyのエンドポイントとDB名を取得
PROXY_ENDPOINT = os.environ['DB_PROXY_ENDPOINT']
DB_USER = os.environ['DB_USER']
DB_PASSWORD = os.environ['DB_PASSWORD']
DB_NAME = os.environ['DB_NAME']
def lambda_handler(event, context):
connection = None
try:
# RDS Proxyのエンドポイントへ接続
connection = pymysql.connect(
host=PROXY_ENDPOINT,
user=DB_USER,
password=DB_PASSWORD,
db=DB_NAME,
connect_timeout=5,
cursorclass=pymysql.cursors.DictCursor
)
with connection.cursor() as cursor:
sql = "SELECT NOW() as current_time;"
cursor.execute(sql)
result = cursor.fetchone()
return {
"statusCode": 200,
"body": f"Database connection successful! Time: {result['current_time']}"
}
except Exception as e:
print(f"Error connecting to database: {e}")
return {
"statusCode": 500,
"body": "Database connection failed"
}
finally:
if connection:
connection.close()
実務での注意点
1. ピン留め(Pinning)の発生に注意する
RDS Proxyには、特定のセッション状態が発生した際に、コネクションプールを破棄して特定のLambdaインスタンスに接続を固定してしまう「ピン留め(Pinning)」という現象があります。ピン留めが発生すると、コネクションプーリングの効果が失われます。
- 原因の例: 一時テーブルの作成、セッション変数の変更、プリペアドステートメントの使用など。
- 対策: 可能な限りセッション状態を変更するSQL演算を避け、シンプルなクエリ設計を心がけてください。
2. サブネットとIPアドレスの設計
RDS Proxyは指定したサブネット内に複数のリソース(ENI)を作成します。スケールアウト時にIPアドレスが不足しないよう、RDS Proxyを配置するVPCサブネットには十分な空きIPアドレス(推奨: 各サブネットで/26以上)を確保してください。また、可用性を担保するために必ずマルチAZ(2つ以上のサブネット)を指定してください。
3. 追加コストの発生
RDS Proxyは便利な反面、プロキシを実行するAurora/RDSのデータベースインスタンスのVCPU数に応じて「時間あたり」の追加料金が発生します。トラフィックが極めて少ない開発環境や、1日数回しか起動しないタスクなどの場合は、あえてRDS Proxyを導入せず、Lambdaの「同時実行数の制限(予約された同時実行)」で対処したほうがコストパフォーマンスが良い場合があります。
まとめ
AWS LambdaからRDSへ接続する実務案件において、RDS Proxyの導入は事実上のデファクトスタンダード(標準設計)となっています。
セキュリティグループの疎通確認、Secrets Managerの権限付与、そして不要なピン留めを回避するコーディングを意識することで、サーバーレスのスケールメリットを最大限に活かした堅牢なデータベースアクセス基盤を構築できます。新規システムの設計や、既存システムの負荷対策にぜひお役立てください。