はじめに
AWSのサーバーレスアーキテクチャにおいて、AWS LambdaからAmazon RDS(あるいはAmazon Aurora)への接続は非常によくあるユースケースです。しかし、Lambdaの急激なスケールアウトによる「DBコネクションの枯渇」や、VPC内への配置における「セキュリティ設計」に頭を悩ませるインフラエンジニアは少なくありません。
本記事では、これらの課題を解決するために実務で強く推奨される「RDS Proxy」を経由した安全かつ高効率な接続方法について、具体的な設定手順をステップバイステップで解説します。
前提知識/必要な理由
LambdaからRDSへ直接接続する場合、以下の2つの大きな課題が発生します。
- コネクションの枯渇: Lambda関数はリクエスト数に応じて並列実行されます。各インスタンスが個別にコネクションを確立するため、データベースの最大接続数(max_connections)を容易に超過してしまいます。
- セキュリティと認証管理: データベースのパスワードをLambdaの環境変数に直接保持することは、セキュリティベストプラクティスに反します。
これらの課題を解決するために、RDS ProxyとAWS Secrets Managerを導入します。RDS Proxyがコネクションをプールして使い回すことで枯渇を防ぎ、認証情報はSecrets Managerで安全に一元管理します。
具体的な設定手順・設計方法
ここからは、実際に構築するための具体的な手順を解説します。
ステップ1:AWS Secrets ManagerでのDBクレデンシャルの作成
まずはRDSに接続するためのユーザー名とパスワードをSecrets Managerに保存します。
- AWS管理コンソールでSecrets Managerを開き、「新しいシークレットを保存する」をクリックします。
- シークレットのタイプで「Amazon RDSデータベースの資格情報」を選択し、DBのユーザー名とパスワードを入力します。
- 対象となるRDSデータベースを選択し、シークレットに名前(例:
my-rds-secret)をつけて保存します。
ステップ2:RDS Proxy用のIAMロールの作成
RDS ProxyがSecrets Managerから認証情報を取得できるようにするためのIAMロールを作成します。以下の信頼ポリシーとアクセス許可ポリシーを設定してください。
信頼ポリシー(Trust Relationship):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "rds.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
アクセス許可ポリシー:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:my-rds-secret-*"
}
]
}
ステップ3:RDS Proxyの作成と設定
RDSのコンソールから「プロキシ」を選択し、新規作成します。
- プロキシ識別子: 任意の名前(例:
my-db-proxy) - 対象データベース: 接続先のRDSインスタンスを選択
- 接続資格情報(Secrets Manager): ステップ1で作成したシークレットを選択
- IAMロール: ステップ2で作成したIAMロールを選択
- VPCサブネット: RDSと同じVPC内の、2つ以上の可用性ゾーン(AZ)のプライベートサブネットを選択
ステップ4:Lambdaレイヤーの作成(PyMySQLの導入手順)
本手順のPythonコードでは、MySQLへの接続に外部パッケージである pymysql を使用します。Lambdaで標準提供されていないライブラリを使用するため、Lambdaレイヤーとしてパッケージ化してアップロードする必要があります。
ローカル環境(またはCloud9など)で以下のコマンドを実行し、ZIPファイルを作成します。
# 作業ディレクトリの作成
mkdir python
cd python
# パッケージを特定のフォルダにインストール
pip install pymysql -t .
# 親ディレクトリに戻り、zip圧縮
cd ..
zip -r pymysql-layer.zip python/
作成した pymysql-layer.zip をAWSコンソールの「Lambda」→「レイヤー」からアップロードし、レイヤーを作成します。その後、作成したLambda関数にこのレイヤーを紐付けます。
ステップ5:Lambda関数の作成とコードの実装
VPC内にLambda関数を配置し、以下の設定を行います。
- VPC設定: RDS Proxyと同じVPC、およびプライベートサブネットを選択します。
- 実行ロール:
AWSLambdaVPCAccessExecutionRoleポリシーをアタッチします。
以下は、RDS Proxy経由でデータベースにクエリを実行するLambda関数のサンプルコード(Python)です。
import os
import pymysql
import boto3
import json
# Secrets Managerから認証情報を取得
def get_secret():
secret_name = os.environ['SECRET_NAME']
region_name = os.environ['AWS_REGION']
client = boto3.client('secretsmanager', region_name=region_name)
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
def lambda_handler(event, context):
secrets = get_secret()
# RDS Proxyのホスト名、ユーザー情報、DB名を設定
db_host = os.environ['PROXY_ENDPOINT']
db_user = secrets['username']
db_password = secrets['password']
db_name = os.environ['DB_NAME']
try:
# RDS Proxyへの接続確立
connection = pymysql.connect(
host=db_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()
return {
'statusCode': 200,
'body': f"Database version: {result[0]}"
}
except Exception as e:
print(f"Error: {e}")
return {
'statusCode': 500,
'body': "Connection failed"
}
finally:
if 'connection' in locals():
connection.close()
実務での注意点
- セキュリティグループの設計: 通信元(Lambda)、RDS Proxy、RDSの三者間でセキュリティグループを正しく設定してください。具体的には、RDS Proxyのセキュリティグループで「Lambdaのセキュリティグループからのポート3306(MySQLの場合)のインバウンド」を許可し、RDS本体のセキュリティグループで「RDS Proxyのセキュリティグループからのインバウンド」のみを許可します。
- タイムアウトの調整: LambdaがVPC内で起動する際、またはRDS Proxyへの初回接続時にわずかな遅延(コールドスタート)が発生することがあります。Lambda関数のデフォルトタイムアウト(3秒)は短すぎるため、実務では15秒〜30秒程度に引き上げることを推奨します。
- ピン留め(Pinning)の回避: プリペアドステートメントの多用や一時テーブルの使用など、特定のSQL操作を行うとRDS Proxyのコネクションプールが特定のセッションに固定(ピン留め)され、プロキシの効果が薄れる原因になります。アプリケーション側のSQL実装にも注意を払いましょう。
まとめ
VPC内のLambdaからRDSへ接続する際、RDS Proxyを挟む設計は、高負荷な実務環境において必須と言えるベストプラクティスです。Secrets Managerを組み合わせることで、認証情報のハードコードを排除し、安全でスケーラブルなサーバーレスアーキテクチャを実現できます。本手順を参考に、堅牢なインフラ環境を構築してください。