はじめに
サーバーレスアーキテクチャの普及に伴い、AWS LambdaからVPC(Virtual Private Cloud)内にあるAmazon RDSに接続するユースケースが一般的になっています。しかし、ネットワーク設計やセキュリティグループの設定を誤ると、接続できないトラブルや、セキュリティリスクが生じる原因になります。
本記事では、実務で役立つ「LambdaからVPC内のRDS(PostgreSQL)へ安全に接続する」ための具体的な設定手順と設計ベストプラクティスを解説します。
前提知識/必要な理由
なぜVPC設定が必要なのか
デフォルト状態のLambda関数は、AWSが管理するパブリックなネットワーク上で実行されます。一方、本番環境のRDSはセキュリティ向上のため、通常はパブリックアクセスを禁止したプライベートサブネット内に配置されます。このため、LambdaからRDSにアクセスするには、Lambda関数をRDSと同じ、あるいは通信可能なVPC内に配置(VPC Lambda化)する必要があります。
セキュリティグループとIAMの役割
安全な接続を確立するためには、以下の2点が重要です。
- 最小特権のセキュリティグループ制御: LambdaからRDSへの送信(Outbound)と、RDSでの受信(Inbound)を、特定のセキュリティグループ同士でのみ許可します。
- IAMによる認証と認証情報の保護: データベースの認証情報をソースコードに直書きせず、AWS Secrets ManagerやIAMデータベース認証を利用して安全に管理します。
具体的な設定手順・設計方法
ここでは、Pythonを用いてVPC内のRDS(PostgreSQL)に接続するLambda関数を構築する手順を説明します。
ステップ1:セキュリティグループの作成と設定
まずは、Lambda用とRDS用の2つのセキュリティグループを作成します。
- Lambda用セキュリティグループ(例: sg-lambda): アウトバウンドルールで、RDSへの通信(ポート5432)を許可します。
- RDS用セキュリティグループ(例: sg-rds): インバウンドルールで、送信元として上記「sg-lambda」を指定し、ポート5432からの通信のみを受け入れるように設定します。
ステップ2:Lambda関数のVPC設定
Lambda関数の設定タブから「VPC」を選択し、以下の項目を設定します。
- VPC: RDSが所属しているVPCを選択します。
- サブネット: 最低2つのアベイラビリティゾーン(AZ)のプライベートサブネットを選択します(高可用性確保のため)。
- セキュリティグループ: ステップ1で作成した「sg-lambda」を設定します。
※LambdaがVPC内でElastic Network Interface(ENI)を生成できるようにするため、Lambdaの実行ロールに AWSLambdaVPCAccessExecutionRole ポリシーを必ずアタッチしてください。
ステップ3:Lambda関数のコード実装とライブラリ導入
今回は、Pythonの接続ライブラリである psycopg2 を使用してRDSに接続します。Lambdaの標準環境には psycopg2 が含まれていないため、事前にLambdaレイヤーとして追加する必要があります。
【補足】psycopg2ライブラリの導入方法
LambdaはAmazon Linux環境で動作するため、ローカルでビルドしたバイナリをZip化してレイヤーを作成するか、有志が公開している「Klayer」などのAWS Lambda Layer用の事前ビルドパッケージを利用すると簡単です。今回は、以下のように手動でビルドパッケージを作成してアップロードします。
# Amazon Linux環境(Dockerなど)で実行
mkdir -p python/lib/python3.9/site-packages
pip install psycopg2-binary -t python/lib/python3.9/site-packages/
zip -r psycopg2-layer.zip python/
# 作成したzipファイルをLambdaレイヤーにアップロードし、関数に紐付けます。
Lambda関数のコード例
以下は、Secrets Managerから接続情報を取得し、RDS(PostgreSQL)に接続するサンプルコードです。
import os
import json
import boto3
import psycopg2
def lambda_handler(event, context):
# Secrets Managerから認証情報を取得
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)
secret = json.loads(response['SecretString'])
# データベース接続
try:
conn = psycopg2.connect(
host=secret['host'],
database=secret['dbname'],
user=secret['username'],
password=secret['password'],
port=secret['port']
)
cur = conn.cursor()
cur.execute("SELECT version();")
db_version = cur.fetchone()
cur.close()
conn.close()
return {
'statusCode': 200,
'body': json.dumps(f"Database connection successful! Version: {db_version}")
}
except Exception as e:
print(f"Error connecting to database: {e}")
raise e
実務での注意点
1. データベース接続数の枯渇に注意(RDS Proxyの検討)
Lambdaはリクエストに応じてスケールアウト(並行実行)するため、大量のコンカレンシーが発生すると、RDSへの接続数が上限に達し「Too many connections」エラーが発生します。実務では、接続をプールして効率的に管理してくれる Amazon RDS Proxy をLambdaとRDSの間に挟む構成を強く推奨します。
2. インターネット通信の確保(NAT Gatewayの配置)
VPC内のLambda関数は、デフォルトではインターネットにアクセスできません。例えば、外部のサードパーティAPIを叩く処理がある場合、Lambdaを配置するサブネットから NAT Gateway 経由でインターネットへルーティングする必要があります。
3. コールドスタート問題とENI作成時間
現在、AWSはHyperplane技術の導入により、VPC Lambdaの起動オーバーヘッド(ENI作成に伴う遅延)を大幅に削減しています。しかし、初回のコンテナ起動(コールドスタート)時は依然としてわずかな遅延が発生するため、ミリ秒単位のレスポンスが求められるAPIでは、プロビジョニングされた同時実行(Provisioned Concurrency)の設定を検討してください。
まとめ
AWS LambdaからVPC内のRDSに接続するには、適切なVPC配置、セキュリティグループによる相互通信の許可、そしてライブラリの適切なパッケージングが必要です。さらに、本番運用においては、接続のスパイクを防ぐための「RDS Proxy」の導入や、シークレット情報の適切な管理を徹底することで、安全で堅牢なサーバーレスアーキテクチャを実現できます。本記事の手順を参考に、セキュアなインフラ構築を目指してください。