はじめに
AWSを用いたサーバーレスアーキテクチャにおいて、AWS LambdaからAmazon RDSやAmazon Auroraなどのリレーショナルデータベースに直接接続すると、Lambdaの急激なスケールアウトに伴ってデータベースのコネクション上限に達し、接続エラーが発生する問題が頻出します。
この問題を解決するために不可欠なのが「Amazon RDS Proxy」です。本記事では、初心者から中級者のインフラエンジニア向けに、LambdaからRDS Proxyを経由してデータベースへ安全かつ安定して接続するための設計と具体的な設定手順をステップバイステップで解説します。
前提知識とRDS Proxyが必要な理由
Lambda関数はリクエストに応じて自動でスケールし、並行して多数のインスタンスが起動します。しかし、従来のデータベースは永続的な接続(コネクションプーリング)を前提に設計されているため、Lambdaから直接接続を繰り返すと、容易に最大同時接続数(Max Connections)を超過してしまいます。
RDS Proxyを導入することで、以下のメリットを享受できます。
- コネクションプーリング: データベースへの接続をプロキシがプール・再利用し、データベースの負荷を大幅に軽減します。
- 高可用性と高速なフェイルオーバー: データベースのフェイルオーバー発生時、接続の切り替えをプロキシが吸収するため、アプリケーション側のエラーを最小限に抑えます。
- セキュリティの向上: IAM認証やAWS Secrets Managerと連携し、データベースの認証情報をLambdaの環境変数に直接保持する必要がなくなります。
具体的な設定手順・設計方法
ここでは、VPC環境内に構築された「Amazon RDS (MySQL)」に対して、同じVPC内の「AWS Lambda」から「RDS Proxy」を経由して接続する設定手順を解説します。
1. AWS Secrets Managerでのデータベース認証情報の保存
RDS Proxyがデータベースに認証するために、データベースのユーザー名とパスワードをSecrets Managerに保存します。
- Secrets Managerのコンソールを開き、「新しいシークレットを保存する」をクリックします。
- シークレットのタイプで「Amazon RDS データベースの認証情報」を選択し、ユーザー名とパスワードを入力します。
- 対象のRDSデータベースを選択し、任意のシークレット名(例:
rds/db-credentials)を付けて保存します。
2. RDS Proxy用のIAMロールの作成
RDS Proxyが、先ほど作成したSecrets Managerのシークレットを読み取るためのIAMロールを作成します。信頼ポリシーには rds.amazonaws.com を指定し、以下のIAMポリシーをアタッチします。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadSecrets",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:REGION:ACCOUNT_ID:secret:rds/db-credentials-*"
}
]
}
3. RDS Proxyの作成とセキュリティグループの設定
次に、RDS Proxyを作成し、ネットワークの導通を確保するためのセキュリティグループ(SG)を設定します。
セキュリティグループの設定:
- RDS Proxy用SG: インバウンドルールで、LambdaのセキュリティグループからのMySQLポート(3306)の通信を許可します。
- RDS用SG: インバウンドルールで、上記「RDS Proxy用SG」からのMySQLポート(3306)の通信を許可します。
RDS Proxyの作成手順:
- RDSのコンソールから「プロキシ」を選択し、「プロキシの作成」をクリックします。
- ターゲットデータベースとして、対象のRDSインスタンスを選択します。
- 接続設定で、作成したRDS Proxy用SGを指定します。
- 認証セクションで、ステップ1で作成したシークレットと、ステップ2で作成したIAMロールを選択します。
- プロキシを作成すると、「プロキシエンドポイント」が発行されます。これを控えておきます。
4. Lambda関数の作成と外部ライブラリの導入
今回は、Pythonでデータベースへ接続するLambda関数を作成します。PythonからMySQLに接続するため、外部パッケージ PyMySQL を使用します。
外部ライブラリの導入手順(デプロイパッケージの作成):
AWS Lambdaの標準環境には PyMySQL などの外部ライブラリは含まれていません。ローカルの開発環境でライブラリを同一ディレクトリにインストールし、zipファイルに固めてLambdaにアップロードするか、Lambdaレイヤーとして登録する必要があります。以下は、デプロイパッケージを作成するコマンド例です。
# 作業用ディレクトリの作成
mkdir lambda_function
cd lambda_function
# PyMySQLパッケージをカレントディレクトリにインストール
pip install PyMySQL --target .
# 接続用ソースコード(lambda_function.py)を作成
touch lambda_function.py
Lambda関数のコード例(lambda_function.py):
import os
import pymysql
# 環境変数から接続情報を取得
DB_ENDPOINT = os.environ['DB_ENDPOINT']
DB_USER = os.environ['DB_USER']
DB_PASSWORD = os.environ['DB_PASSWORD']
DB_NAME = os.environ['DB_NAME']
def lambda_handler(event, context):
try:
# RDS Proxyのエンドポイントへ接続
connection = pymysql.connect(
host=DB_ENDPOINT,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME,
connect_timeout=5
)
with connection.cursor() as cursor:
cursor.execute("SELECT VERSION()")
result = cursor.fetchone()
return {
'statusCode': 200,
'body': f"Connection successful! Database version: {result[0]}"
}
except Exception as e:
print(f"Error: {e}")
return {
'statusCode': 500,
'body': "Connection failed."
}
finally:
if 'connection' in locals() and connection.open:
connection.close()
コードとパッケージ群をzip圧縮し、Lambda関数にアップロードします。また、Lambda関数の環境変数に、RDS Proxyのエンドポイント(DB_ENDPOINT)や認証情報を登録します。
実務での注意点
- VPCとサブネットの設計: Lambda関数、RDS Proxy、RDSデータベースは、同じVPC内のサブネットに配置する必要があります。耐障害性を高めるため、必ず複数のアベイラビリティーゾーン(マルチAZ)にまたがるプライベートサブネットを指定してください。
- ピン留め(Pinning)の回避: SQLの準備されたステートメント(Prepared Statements)や一時テーブルを使用すると、RDS Proxyが特定のコネクションを「ピン留め」してしまい、プーリングの効果が著しく低下します。コードの実装がピン留めを引き起こしていないか確認してください。
- コストの考慮: RDS Proxyは、関連付けられたRDSインスタンスのvCPU数に基づく時間課金が発生します。開発環境など、不要な時間帯はシャットダウンする、あるいはプロキシを削除するなどの運用ルールを策定しておくとコストを最適化できます。
まとめ
LambdaとRDS Proxyの連携は、負荷の急増に耐えうる堅牢なサーバーレスシステムを構築するためのデファクトスタンダードです。セキュリティグループの設計とSecrets Managerの連携、そして外部ライブラリを適切にデプロイする手順を押さえることで、安全かつスケーラブルな接続環境を構築できます。本手順を参考に、ぜひ実務のインフラ設計に役立ててください。