【実務・中級編】 AWS NAT GatewayとVPCピアリング/Transit Gatewayのトラフィック優先順位 – クラウド&コンテナネットワーク実践ガイド

こんにちは。SREチームのシニアエンジニアです。

本番環境のインフラを運用していると、ある日突然、VPCピアリングやAWS Transit Gateway(TGW)を導入した途端に、プライベートサブネットからインターネットへの外向き通信(NAT Gateway経由の外部API呼び出しなど)が怪しい挙動を示したり、逆にオンプレミスや別VPCへのトラフィックがNATを突き抜けて外に出ていこうとしたりする、あの冷や汗をかくような瞬間に出くわしたことはないでしょうか?

「ルートテーブルの優先順位なんて、最長一致(Longest Prefix Match)の教科書通りに動くだろう」と高をくくっていると、AWSのネットワークの裏側でうごめくルーティングの評価順序に足元をすくわれます。

今回は、プライベートサブネットにおけるVPCピアリング/Transit Gateway向けのルートと、NATゲートウェイ向けのデフォルトルート(0.0.0.0/0)が競合した際に、AWSのVPCルーターがどのようにパケットを裁いているのか、そのリアルな挙動と実務での設計・デバッグ手法を徹底解説します。

—

1. ルーティングの基本原則:最長一致と「例外なき事実」

まず、パケットがVPCのルートテーブルをヒットする瞬間の挙動を思い出してください。AWSのVPCルーターは、パケットの宛先IPアドレスに対し、ルートテーブル内で最もプレフィックス長が長い(=より具体的な)エントリを優先して選択する最長一致(Longest Prefix Match)の原則で動作します。

ここで多くのエンジニアが陥りがちな誤解があります。
「それなら、10.0.0.0/16 などの特定VPCレンジや、TGW経由の 192.168.0.0/16 宛てのルートがあれば、デフォルトルートである 0.0.0.0/0 よりもそちらが優先されるはずだ。だから競合しない」と。

理論上はその通りです。宛先が明確に分かれている分には、最長一致によって正しくTGWやVPCピアリングへパケットが流れます。問題が起きるのは、「どのルートにも完全一致しない宛先(インターネット上の外部APIなど)」への通信や、設計ミス・意図しないトラフィックの流出が発生したときです。

現場で起きる「思わぬトラフィックの吸い込まれ」

例えば、外部のSaaSやWeb API(例: api.stripe.com や api.sendgrid.com)を叩くアプリケーションがプライベートサブネットにあるとします。通常、これらはインターネット行きのパケットとなるため、NATゲートウェイ(nat-xxxxxxxx)を指す 0.0.0.0/0 のルートにヒットするはずです。

しかし、もしTransit Gateway側のルートテーブルやVPCピアリングのルート設定で、広範囲なプレフィックス(あるいはうっかり 0.0.0.0/0 をTGW側に向けようとした場合、実際にはAWSの仕様でTGWに 0.0.0.0/0 は直接デフォルトとしては置けませんが、それに準ずる集約ルート)が設定されていたりすると、トラフィックの迷子やセキュリティ上の重大なインシデントに直結します。

—

2. パケットのライフサイクル:NAT Gateway vs TGW/VPCピアリングの競合シナリオ

では、実際にプライベートサブネットからパケットが発信され、宛先に到達するまでのシーケンスを追ってみましょう。

[Private Subnet App] 
       │
       ▼ (外部APIへHTTPSリクエスト)
[VPC Route Table 評価]
       │
       ├── 1. 最長一致ルート (例: 10.100.0.0/16 -> TGW)
       │      └─ 宛先がマッチすれば Transit Gateway へ直行
       │
       └── 2. デフォルトルート (0.0.0.0/0 -> NAT Gateway)
              └─ マッチしないものはすべて NAT Gateway 経由でインターネットへ

ここで重要になるのは、「AWSのルートテーブルにおいて、ターゲットが異なる複数のルートが存在する場合のプライオリティ」です。

1. 特定CIDR(例: 10.0.0.0/8 や 172.16.0.0/12 など)

  • VPCピアリング、Transit Gateway、VPN、Direct Connectなどのターゲットが設定されている場合、宛先IPがこの範囲に含まれていれば、迷わずそのターゲットへルーティングされます。

2. ローカルルート(VPC CIDR自体)

  • local ターゲットは自動的に設定され、VPC内の通信を最優先で処理します。これは削除も変更もできません。

3. デフォルトルート(0.0.0.0/0)

  • 上記のいずれにも該当しないすべてのトラフィックを、NAT Gatewayやインターネットゲートウェイ(IGW)へ流し込みます。

陥りやすい罠:プレフィックスの重複とオーバーラップ

異なるルートテーブル間で、あるいは同一ルートテーブル内でCIDRが重複・包含している場合、AWSは容赦なく最長一致を適用します。
例えば、TGW側に 10.0.0.0/8 を向けている環境で、サブネットから 10.0.50.25(別VPC内のリソース)にアクセスしようとした場合、これはTGWへ流れます。しかし、もしそのリソースが存在せず、かつNAT Gateway経由でオンプレミス側のパブリックIP等へ抜けようとした場合は、ルーティングの迷子(Destination Unreachable)や非対称ルーティング(Asymmetric Routing)の原因になります。

—

3. 実務で役立つ設定例と検証コード

百聞は一見にしかず。ここからは、インフラ構成を定義するCloudFormation/Terraformのイメージと、アプリケーションから実際に通信を検証するためのコードを見ていきましょう。

Terraformでのルートテーブル設計例

プライベートサブネットから、TGWを通じた社内ニッチネットワークへの通信と、NAT Gatewayを通じた外部インターネット通信を共存させる典型的なTerraformの抜粋です。

# プライベートサブネット用ルートテーブル
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  # 1. ローカル通信(自動生成されるため明示的に書かないことも多いが理解のために記載)
  # 宛先: VPC CIDR, ターゲット: local

  # 2. Transit Gateway向けの特定ルート(例: オンプレミスや別VPC群)
  # 最長一致により、このCIDRに該当する通信はTGWへ優先的に流れる
  route {
    cidr_block         = "10.100.0.0/16"
    transit_gateway_id = aws_ec2_transit_gateway.main.id
  }

  # 3. デフォルトルート(インターネット行き)
  # 上記以外のすべてのパケットはNAT Gatewayへ吸い込まれる
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.gw.id
  }

  tags = {
    Name = "prod-private-rt"
  }
}

Python (Requests / urllib) による外部API呼び出しの疎通確認コード

インフラ構築後、意図した通りにNAT Gateway経由で外部に出ていっているか(パブリックIPがNAT Gatewayのものになっているか)を確認するためのスクリプトです。

import requests
import json

def check_egress_ip():
    """
    プライベートサブネット内のインスタンスから実行し、
    外向き通信がどのIP(NAT GatewayのEIP)から出てきているかを確認する
    """
    url = "https://httpbin.org/ip"
    
    try:
        # タイムアウトを3秒に設定し、ハングアップを防ぐ
        response = requests.get(url, timeout=3)
        response.raise_for_status()
        
        data = response.json()
        print(f"[INFO] 外部から観測されたグローバルIP: {data.get('origin')}")
        print("[SUCCESS] NAT Gateway経由のインターネット通信は正常です。")
        
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] 通信に失敗しました。ルートテーブルやセキュリティグループを確認してください: {e}")

if __name__ == "__main__":
    check_egress_ip()

—

4. トラブルシューティング:パケットが迷子になったときの現場の鉄則

もし「TGW導入後に外部API繋がらなくなった」「オンプレとの通信がNATに吸われてタイムアウトする」といった障害に直面したら、以下の手順で泥臭くデバッグしてください。

1. AWS VPC Reachability Analyzer の活用

  • まずコンソールからリサーチツールを起動し、送信元(プライベートインスタンス)から宛先(外部APIやオンプレサーバー)までのパスをシミュレーションします。どのルートテーブルのどのエントリ(最長一致)でパケットがトラップされているかが一目で分かります。

2. フローログ(VPC Flow Logs)の解析

  • REJECT や ACCEPT のステータスだけでなく、srcaddr, dstaddr, pkt-srcaddr, pkt-dstaddr を確認し、意図しないインターフェイス(NAT GatewayのエラスティックIPやTGWアタッチメント)をパケットが通過していないか追跡します。

3. セキュリティグループとNACLの二重チェック

  • ルートテーブルが正しくとも、セキュリティグループのインバウンド/アウトバウンド、あるいはネットワークACLでステートレスな戻りパケットがブロックされているケースが後を絶ちません。特にTGWを跨ぐ通信では、相手側VPCのセキュリティグループの許可漏れが頻発します。

—

まとめ

AWSのネットワーク設計において、NAT GatewayとTGW/VPCピアリングの共存は避けて通れない道です。
「最長一致の原則」を正しく理解し、ルートテーブルのプレフィックス設計を綿密に行うこと、そしていざという時にフローログやReachability Analyzerでパケットの足取りを追えるスキルこそが、私たちインフラエンジニアの最大の武器になります。

派手なアプリケーションコードの裏側で、静かに、しかし確実にパケットを導き続けるルーティングの世界。ぜひ皆さんの現場でも、今一度ルートテーブルの設計を見直してみてください。

コメント

タイトルとURLをコピーしました