LambdaからVPC内のRDSへ安全に接続する設定手順と注意点

はじめに

サーバーレスアーキテクチャの普及に伴い、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つのセキュリティグループを作成します。

  1. Lambda用セキュリティグループ(例: sg-lambda): アウトバウンドルールで、RDSへの通信(ポート5432)を許可します。
  2. 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」の導入や、シークレット情報の適切な管理を徹底することで、安全で堅牢なサーバーレスアーキテクチャを実現できます。本記事の手順を参考に、セキュアなインフラ構築を目指してください。

上部へスクロール