はじめに
サーバーレスアーキテクチャの普及に伴い、AWS LambdaとAmazon RDSを組み合わせたシステム構築が増えています。しかし、LambdaからRDSに直接接続すると、Lambdaの急激なスケールアウトによってデータベースのコネクション上限(最大接続数)を超過し、接続エラーが発生するリスクがあります。
本記事では、この問題を解決する「Amazon RDS Proxy」を導入し、Lambdaから安全かつ効率的にデータベースに接続するための具体的な設定手順と設計ベストプラクティスを解説します。
前提知識/必要な理由
なぜLambdaからRDSへの直接接続は避けるべきなのか?
Lambdaはリクエスト数に応じて自動的に並列実行(スケールアウト)されます。各Lambdaインスタンスは個別にデータベースへのコネクションを確立するため、アクセスが急増すると、RDSが許容する最大接続数を容易に超えてしまいます。これにより、システム全体がダウンする原因になります。
RDS Proxyが解決する課題
RDS Proxyは、LambdaとRDSの間に立ち、データベース接続をプール(集約・管理)するフルマネージドプロキシサービスです。主なメリットは以下の通りです。
- 接続の再利用: 接続プールにより、データベースのコネクション数を最小限に抑えます。
- 耐障害性の向上: RDSのフェイルオーバー発生時、アプリケーションの再接続処理を肩代わりし、ダウンタイムを大幅に削減します。
- セキュリティの強化: IAM認証を利用してデータベースへの接続管理を一元化できます。
具体的な設定手順・設計方法
ここでは、VPC環境内のAurora MySQL(またはRDS MySQL)に対して、LambdaからRDS Proxy経由でセキュアに接続する手順を解説します。
ステップ1:RDS Proxy用のIAMロールとセキュリティグループの作成
まずは、RDS ProxyがSecrets Managerからデータベースの認証情報を安全に取得できるようにするためのIAMロールと、セキュリティグループを作成します。
1. Secrets Managerで、データベースのユーザー名とパスワードを保存するシークレットを作成します。
2. Secrets Managerへのアクセス権限を持つIAMロール(RDS Proxy用)を作成します。信頼ポリシーに rds.amazonaws.com を指定してください。
3. RDS Proxy用のセキュリティグループを作成します。インバウンドルールで、Lambdaのセキュリティグループからのデータベースポート(MySQLの場合は3306)の通信を許可します。
ステップ2:RDS Proxyの作成とターゲット登録
1. AWS管理コンソールのRDSダッシュボードから「プロキシ」を選択し、「プロキシの作成」をクリックします。
2. 以下の設定項目を入力・選択します。
- プロキシ識別子: 任意の名前(例:
my-db-proxy) - ターゲットデータベース: 接続先のRDS/Auroraインスタンス
- Secrets Managerシークレット: ステップ1で作成したシークレット
- IAMロール: ステップ1で作成したIAMロール
- VPC: RDSが所属するVPCと同一のサブネット
3. 作成完了後、RDS Proxyの「エンドポイント」が発行されるので控えておきます。
ステップ3:Lambda用の外部ライブラリ(PyMySQL)の準備
PythonでMySQLに接続するため、今回は標準外の外部ライブラリである PyMySQL を使用します。LambdaでPyMySQLを利用するには、Lambdaレイヤーとしてアップロードする必要があります。
ローカルの開発環境(Linux/macOS)で以下のコマンドを実行し、レイヤー用のZIPファイルを作成します。
# 作業用ディレクトリの作成
mkdir -p python-layer/python
cd python-layer/python
# PyMySQLをディレクトリを指定してインストール
pip install pymysql -t .
# ZIP圧縮
cd ..
zip -r pymysql-layer.zip python/
作成した pymysql-layer.zip をAWS Lambdaの「レイヤー」にアップロードし、対象のLambda関数に紐付けます。
ステップ4:Lambda関数の作成とコードの実装
Lambda関数をRDS Proxyと同じVPC内に配置し、セキュリティグループを設定します。その後、以下の接続確認用コードを記述します。環境変数にRDS Proxyのエンドポイント、DBユーザー名、DB名、Secrets Managerのパスワードを設定してください。
import os
import pymysql
# 環境変数の取得
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):
try:
# RDS Proxy経由でデータベースに接続
connection = pymysql.connect(
host=DB_PROXY_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()
print(f"Database version: {result[0]}")
connection.close()
return {
'statusCode': 200,
'body': 'Successfully connected to RDS via RDS Proxy!'
}
except Exception as e:
print(f"Error connecting to database: {e}")
return {
'statusCode': 500,
'body': 'Connection failed.'
}
実務での注意点
コールドスタートとタイムアウトの設計
Lambda関数がVPC内で起動する際、ネットワークインターフェース(ENI)の作成などで初回起動時(コールドスタート)に時間がかかる場合があります。Lambdaのタイムアウト値はデフォルトの3秒から、余裕を持って 15秒〜30秒程度 に設定しておくことを推奨します。
接続プールのクリーンアップ
Lambdaの実行コード内で connection.close() を必ず呼び出すように設計してください。コネクションを明示的に閉じないと、RDS Proxyが「アクティブな接続」とみなしてプールを無駄に消費し続け、Proxy自体のリソース枯渇を招く原因になります。
まとめ
LambdaとRDSの連携において、RDS Proxyの導入は本番運用の必須要件とも言えます。接続の枯渇エラーを防ぐだけでなく、フェイルオーバー時の耐障害性も飛躍的に向上します。
本記事で紹介したセキュリティグループの設定や外部ライブラリ(レイヤー)の導入、コネクションのクリーンアップ手順を参考に、堅牢でスケールしやすいサーバーレスシステムを構築してください。