【図解】VPC LambdaからRDSと外部APIへ安全に同時接続するネットワーク設計・構築ステップバイステップ

はじめに

AWSを用いたサーバーレス開発において、最も頻繁に遭遇し、かつ設計に頭を悩ませるのが「LambdaからVPC内のリソース(RDSなど)にアクセスしつつ、外部のインターネット(外部APIなど)にも接続したい」というユースケースです。

「LambdaをVPCに入れたらインターネットにつながらなくなった」「RDSにはつながるが、今度は外部システムとのデータ連携でエラーが出る」といったトラブルは、インフラエンジニアの登竜門とも言えます。本記事では、この課題を解決するための正しいネットワーク設計、セキュリティグループのベストプラクティス、そして具体的な設定手順を、初心者から中級者向けにステップバイステップで解説します。

前提知識と必要な理由

なぜLambdaをVPCに入れる必要があるのか?

デフォルト状態のLambda関数は、AWSが管理するシステムVPC上で実行されます。この状態ではインターネット上のリソースにはアクセスできますが、セキュリティ上の理由からプライベートサブネット内に配置されたRDS(Relational Database Service)やElastiCacheなどのリソースには直接アクセスできません。これらに安全にアクセスするためには、Lambda関数を自社管理のVPC(仮想プライベートクラウド)内に配置(VPC Lambda化)する必要があります。

VPC Lambdaが抱える「インターネット接続」の課題

Lambda関数をVPC(特にプライベートサブネット)に配置すると、今度はデフォルトでインターネットへのルートが失われます。LambdaにパブリックIPアドレスが割り当てられないため、直接インターネットゲートウェイ(IGW)を経由して外に出ることができないからです。したがって、RDSなどのデータベースと外部API(決済ゲートウェイや外部SaaSなど)の両方に同時接続するためには、適切なルートテーブルの設計と、NAT(ネットワークアドレス変換)デバイスの設置が必須となります。

具体的な設定手順・設計方法

今回構築する構成の全体像

本ハンズオンでは、以下の堅牢なアーキテクチャを構築します。

  • VPC: 10.0.0.0/16
  • プライベートサブネット(Lambda/RDS用): 10.0.1.0/24, 10.0.2.0/24(マルチAZ構成)
  • パブリックサブネット(NAT Gateway用): 10.0.11.0/24, 10.0.12.0/24
  • NAT Gateway: パブリックサブネットに配置し、Lambdaからのアウトバウンド通信をインターネットへ仲介
  • セキュリティグループ: Lambda用とRDS用をそれぞれ作成し、最小権限で疎通を許可

ステップ1:VPCとサブネットの作成

まず、LambdaとRDS、およびNAT Gatewayを配置するためのネットワークインフラを整備します。AWS管理コンソール、またはAWS CLIで以下の通り設定します。

  1. AWS管理コンソールのVPCダッシュボードを開き、「VPC を作成」をクリックします。
  2. 「VPC など」を選択し、以下のパラメータを入力して作成すると、サブネットやルートテーブルが自動生成されて便利です。
    • IPv4 CIDR ブロック: 10.0.0.0/16
    • パブリックサブネット数: 2 (マルチAZ用)
    • プライベートサブネット数: 2 (マルチAZ用)
    • NAT ゲートウェイ: 「1つのAZ内」または「AZごと(可用性重視)」を選択
    • VPC エンドポイント: 「なし」(必要に応じて後から追加)

ステップ2:セキュリティグループの設定

次に、セキュリティの要となるセキュリティグループ(SG)を2つ作成します。実務では「すべてのトラフィックを許可(0.0.0.0/0)」にするのではなく、セキュリティグループ同士の「相互参照(インバウンド連携)」を設定するのがベストプラクティスです。

1. Lambda用のセキュリティグループ (sg-lambda)

  • インバウンドルール: なし(Lambdaはトリガーされて起動するため、外部からの直接接続は不要)
  • アウトバウンドルール: すべてのトラフィック (0.0.0.0/0) を許可

2. RDS用のセキュリティグループ (sg-rds)

  • インバウンドルール:
    • タイプ: PostgreSQL(またはMySQLなど、使用するDBに合わせてポートを指定。例: 5432)
    • ソース: カスタム -> sg-lambda のセキュリティグループIDを指定
  • アウトバウンドルール: 任意(デフォルトのままで可)

ステップ3:Lambda用のIAMロールの作成

VPC内でLambdaを実行するためには、LambdaサービスがElastic Network Interface (ENI) を作成・管理するための権限(IAMポリシー)が必要です。この権限がないと、LambdaはVPC内で起動できません。

IAMロールを作成し、AWS管理ポリシーである AWSLambdaVPCAccessExecutionRole をアタッチしてください。このポリシーには以下の権限が含まれています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:CreateNetworkInterface",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DeleteNetworkInterface",
                "ec2:AssignPrivateIpAddresses",
                "ec2:UnassignPrivateIpAddresses"
            ],
            "Resource": "*"
        }
    ]
}

ステップ4:Lambda関数の作成とVPC設定

Lambda関数を作成し、これまでに作成したVPC、サブネット、セキュリティグループを紐付けます。

  1. Lambdaコンソールを開き、「関数の作成」をクリックします。
  2. 「一から作成」を選択し、関数名を入力、ランタイムに「Python 3.11」などを選択します。
  3. 「デフォルトの実行ロールの変更」を展開し、ステップ3で作成したIAMロールを選択します。
  4. 「詳細設定」を展開し、「VPC を有効化」にチェックを入れます。
    • VPC: ステップ1で作成したVPCを選択
    • サブネット: プライベートサブネットを2つ選択(※絶対にパブリックサブネットを選択しないでください)
    • セキュリティグループ: sg-lambda を選択
  5. 「関数の作成」をクリックします。

ステップ5:動作確認用コードの実装

Lambda関数から、内部のRDS(PostgreSQL)に接続しつつ、外部のパブリックAPI(今回はテスト用のHTTPSエンドポイント)へリクエストを送信する検証用Pythonコードです。

import json
import urllib.request
import psycopg2 # 事前にLambdaレイヤー等でライブラリの追加が必要です

def lambda_handler(event, context):
    # 1. 外部APIへの接続テスト(インターネット疎通確認)
    external_url = "https://httpbin.org/get"
    try:
        with urllib.request.urlopen(external_url, timeout=5) as response:
            html = response.read().decode('utf-8')
            print("外部API接続成功:")
            print(html[:100]) # ログ出力制限のため一部のみ
    except Exception as e:
        print(f"外部API接続失敗: {e}")
        raise e

    # 2. VPC内RDS(PostgreSQL)への接続テスト
    db_config = {
        "host": "your-rds-endpoint.amazonaws.com",
        "database": "mydb",
        "user": "dbuser",
        "password": "securepassword",
        "port": 5432
    }
    
    try:
        conn = psycopg2.connect(**db_config)
        cur = conn.cursor()
        cur.execute("SELECT version();")
        db_version = cur.fetchone()
        print(f"RDS接続成功: {db_version}")
        cur.close()
        conn.close()
    except Exception as e:
        print(f"RDS接続失敗: {e}")
        raise e

    return {
        'statusCode': 200,
        'body': json.dumps('両方の接続テストに成功しました!')
    }

実務での注意点(トラブルシューティング・設計ベストプラクティス)

1. プライベートサブネットの「IPアドレス枯渇」に注意

過去の仕様とは異なり、現在のAWS(Hyperplane ENIの導入後)では、Lambda関数がスケールアウトしても起動ごとに新しいENIが作成されることはなくなりました。しかし、同一のサブネット/セキュリティグループの組み合わせごとに1つの共有ENIが作成される仕組みであるため、依然としてIPアドレスの設計は重要です。他のシステムと共有するサブネットを設定する場合は、CIDRを十分に(最低でも/24以上)確保しておきましょう。

2. NAT Gatewayのコストに配慮する

NAT Gatewayは1時間あたりの起動コスト(約$0.045/時)と、データ処理量(GBあたり約$0.045)がかかるため、検証環境であっても維持費が割高になります。

コスト最適化のアプローチ:

  • 本番環境以外: NAT Gatewayの代わりに、安価なEC2インスタンス(NAT Instance)を利用して構築する。
  • VPCエンドポイントの活用: 外部接続の目的が「S3へのファイルアクセス」や「Systems Manager(SSM)の利用」だけであれば、NAT Gatewayを使わずにVPCエンドポイント(Gateway型/Interface型)を配置する方が、データ転送コストを劇的に抑えられます。

3. セキュリティグループのターゲット設定ミス

よくあるトラブルとして、Lambdaにアタッチしたセキュリティグループのアウトバウンドで「RDSのポートのみ」を許可してしまうケースがあります。

外部API(HTTPS)に接続するためには、DNS解決(ポート53/UDP・TCP)やHTTPS(ポート443/TCP)がアウトバウンドで開いている必要があります。そのため、Lambdaのアウトバウンドは原則としてデフォルトの「すべてのトラフィック(0.0.0.0/0)を許可」のままにしておくのがトラブルを防ぐベストプラクティスです。

まとめ

VPC Lambdaから内部RDSと外部APIへ安全に同時接続するネットワーク設計の要点は以下の通りです。

  1. Lambdaは必ずプライベートサブネットに配置する。
  2. インターネット接続を提供するために、パブリックサブネットにNAT Gatewayを配置し、プライベートサブネットのルートテーブルのデフォルトルート(0.0.0.0/0)をNAT Gatewayに向ける。
  3. セキュリティグループはIP直書きではなく、「LambdaのSGからRDSのSGへ」の接続を許可する「グループ間参照」で設計する。
  4. Lambdaには必ず AWSLambdaVPCAccessExecutionRole ポリシーを持つIAMロールを割り当てる。

この基本パターンを一度理解すれば、実務でのネットワークトラブルの9割は自力で解決・回避できるようになります。インフラ構成のセキュリティを最大化しつつ、サーバーレスの柔軟性を最大限に活かした設計を目指しましょう。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール