VPC Lambdaでインターネット接続を可能にする設定手順と注意点

はじめに

AWSの実務において、VPC内のリソース(RDSやElastiCacheなど)にセキュアにアクセスしつつ、外部のWeb APIやSaaSといったインターネット上のリソースにも同時に通信したいケースは頻繁に発生します。

しかし、Lambda関数を単にVPC内に配置しただけでは、インターネットへのアウトバウンド通信を行うことはできません。本記事では、VPC Lambdaがインターネットに接続できない原因を整理し、解決するためのネットワーク設計と具体的な設定手順をステップバイステップで解説します。

前提知識とVPC LambdaにNAT Gatewayが必要な理由

通常のLambda関数はAWSが管理するネットワーク上で実行され、デフォルトでインターネットへのアクセスが可能です。しかし、「VPC接続」を有効にすると、Lambdaは指定されたVPCのプライベートサブネット内にElastic Network Interface(ENI)を作成して起動します。

この際、以下の制約が発生するため、パブリックIPを持たないLambdaは直接インターネットと通信できません。

  • VPC内のLambdaに付与されるIPアドレスはプライベートIPのみである
  • Internet GatewayはパブリックIPを持つリソースの通信しか中継できない
  • そのため、プライベートIPをパブリックIPに変換(SNAT)するNAT Gatewayが必須となる

正しい構成としては、「Lambdaはプライベートサブネットに配置し、通信をパブリックサブネットに配置したNAT Gateway経由でInternet Gatewayへルーティングする」設計になります。

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

1. VPCとサブネットの作成

まずはベースとなるネットワークを構築します。冗長性を確保するため、2つのアベイラビリティーゾーン(AZ)にそれぞれパブリックサブネットとプライベートサブネットを作成します。

  • パブリックサブネット(2つ): NAT GatewayやInternet Gatewayとの通信用
  • プライベートサブネット(2つ): Lambda関数やRDSなどの配置用

2. Internet GatewayとNAT Gatewayの配置

次に、外部と通信するためのゲートウェイを設定します。

  • Internet Gatewayの作成と接続: 作成したInternet Gatewayを対象のVPCにアタッチします。
  • NAT Gatewayの作成: パブリックサブネットにNAT Gatewayを作成します(1つでも動作しますが、本番環境では冗長化のため各AZに1つずつの配置を推奨します)。この際、Elastic IP(EIP)を割り当ててください。

3. ルートテーブルの設定

パブリックサブネットとプライベートサブネットでルートテーブルを切り分けます。

パブリックサブネット用ルートテーブル

デフォルトルート(0.0.0.0/0)の送信先を、先ほど作成したInternet Gateway(igw-xxxx)に設定し、パブリックサブネットに関連付けます。

プライベートサブネット用ルートテーブル

デフォルトルート(0.0.0.0/0)の送信先を、作成したNAT Gateway(nat-xxxx)に設定し、プライベートサブネットに関連付けます。これにより、Lambdaからのインターネット宛ての通信がNAT Gatewayに転送されます。

4. Lambda関数の作成とVPC接続設定

Lambda関数を作成し、VPC設定を行います。

  • AWS管理コンソールでLambda関数を作成します。
  • 「設定」タブ > 「VPC」を選択し、「編集」をクリックします。
  • 対象のVPC、およびLambdaを起動するプライベートサブネット(推奨:2箇所以上)を選択します。
  • セキュリティグループを選択します。このセキュリティグループのアウトバウンドルールでは、すべてのトラフィック(0.0.0.0/0)を許可しておきます。

5. 接続テストコードの実行

Lambdaから実際にインターネット経由で外部APIを呼び出せるかテストします。以下のPythonコードをLambda関数に貼り付けてテストを実行してください。

※本手順では、外部ライブラリの導入が不要なPython標準の urllib.request を使用します。もし実務で requests などのサードパーティ製ライブラリを使用する場合は、事前にローカル環境で pip install requests -t . を実行してパッケージングするか、AWS Lambdaレイヤー(Layer)にZIP形式でアップロードして関数に適用してください。

import json
import urllib.request
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    # 接続テスト用のパブリックAPI(GitHub API)
    url = "https://api.github.com/zen"
    req = urllib.request.Request(url, headers={'User-Agent': 'AWS-Lambda-Test'})
    
    try:
        with urllib.request.urlopen(req, timeout=5) as response:
            html = response.read().decode('utf-8')
            logger.info(f"Success! Response: {html}")
            return {
                'statusCode': 200,
                'body': json.dumps({'message': 'Internet Connection Success', 'data': html})
            }
    except Exception as e:
        logger.error(f"Connection Failed: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps({'message': 'Connection Failed', 'error': str(e)})
        }

実務での注意点

NAT Gatewayのコスト

NAT Gatewayは時間単位の起動料金と、処理されたデータ量に応じた課金が発生します。開発環境など、常時起動が不要な場合は、NAT Gatewayの代わりに「NATインスタンス」を使用することでコストを抑えることが可能です。ただし、本番環境では可用性と帯域幅の観点からNAT Gatewayの採用を強く推奨します。

サブネット選択の間違い

よくあるトラブルとして、Lambda関数のVPC設定で「パブリックサブネット」を選択してしまうケースがあります。上述の通り、パブリックサブネットに配置されたLambdaはパブリックIPを持たないため、Internet Gateway経由で外に出られずタイムアウトします。Lambdaは必ずプライベートサブネットに配置してください。

セキュリティグループとネットワークACLの設定

セキュリティグループのインバウンドルールに制限があってもアウトバウンドが許可されていれば通信可能ですが、ネットワークACL(NACL)はステートフルではないため、送信時(アウトバウンド)と受信時(インバウンド)の両方でエフェメラルポート(1024-65535)の通信が許可されている必要があります。通信が詰まる場合はNACLの設定も見直してください。

まとめ

VPC内のLambdaからインターネットへ通信させるには、正しいVPC設計(パブリック/プライベートサブネットの切り分け)とNAT Gatewayのルーティング設定が必須です。

構築時には「Lambdaはプライベートサブネットに置く」「デフォルトルート(0.0.0.0/0)をNAT Gatewayに向ける」という2点を確実に押さえ、セキュアで可用性の高いサーバーレスシステムを設計しましょう。

上部へスクロール