【実務・中級編】 AWS Direct ConnectとNATゲートウェイの併用時におけるルーティング仕様 – クラウド&コンテナネットワーク実践ガイド

AWS Direct ConnectとNATゲートウェイの「境界線」を攻略する:非対称ルーティングの罠と正しい設計術

こんにちは。クラウドインフラの現場で幾多のパケットロスや接続障害の火消しに走ってきたSREです。

オンプレミスとAWSを Direct Connect (DX) で結び、さらにそのプライベートサブネットからインターネットへ抜ける――この構成は、エンタープライズ環境では「王道」ですが、いざ設計・運用となると、ネットワークエンジニアの胃を痛めるような落とし穴が潜んでいます。

特に「NATゲートウェイ(NATGW)を使ったインターネットアクセス」と「DX経由のオンプレ通信」が同居する場合、もっとも警戒すべきは非対称ルーティング(Asymmetric Routing)です。今回は、現場で泣きを見ないためのルーティング設計の勘所を伝授します。

—

1. パケットはどこへ行く?「非対称ルーティング」の悪夢

まず、通信の基本原則を確認しましょう。TCP通信において、クライアントとサーバーは「行き」と「帰り」の経路が整合していることを期待します。

オンプレミスのサーバーが NATGW を経由してインターネット上のAPI(例えば https://api.example.com )を叩く際、通信は以下のように流れます。

1. Request: [On-prem] -> [DX] -> [VPC/Private Subnet] -> [NATGW] -> [Internet]
2. Response: [Internet] -> [NATGW] -> [VPC/Private Subnet] -> [On-prem]

ここで問題になるのは、オンプレからの通信が DX を介してVPCに到達した際、VPC側のルートテーブルで「NATGWをデフォルトゲートウェイ(0.0.0.0/0)として認識しているか」という点です。

もし DX 接続している Virtual Private Gateway (VGW) や Transit Gateway (TGW) 側で、オンプレミス宛のルートが適切に制御されていないと、パケットは「迷子」になり、ステートフルなファイアウォール(セキュリティグループやNACL)によってドロップされます。これが「パケットは飛んでいるはずなのに、なぜか通信が確立しない」という地獄の入り口です。

—

2. 実践的なルートテーブル設計の指針

VPC内のプライベートサブネットにおけるルートテーブルは、以下のように明確に「役割」を分離するのが鉄則です。

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

| 送信先 (Destination) | ターゲット (Target) | 備考 |
| :— | :— | :— |
| 10.0.0.0/16 | local | VPC内通信 |
| 192.168.1.0/24 | vgw-xxxxxxxx | オンプレミスへのDXルート |
| 0.0.0.0/0 | nat-xxxxxxxx | インターネットへの出口 |

ここでのポイント:
0.0.0.0/0 を NATGW に向けているからといって、オンプレミス宛の通信までNATGWに吸い込まれないよう、192.168.1.0/24(オンプレのIPレンジ)のようなより具体的なルート(Longest Prefix Match)を明示的に VGW や TGW に向けることが不可欠です。

—

3. 実装の確認:Pythonで疎通試験を行う

設定が終わったら、まずは疎通確認です。単なる ping ではなく、実際のWeb APIのレスポンスを確認するスクリプトを書いてみましょう。

import requests

# NATGWを経由してインターネットAPIを叩く
def test_internet_access():
    url = "https://api.example.com/health"
    try:
        # タイムアウトを設定し、NATGWの遅延やパケットロスを検知できるようにする
        response = requests.get(url, timeout=5)
        print(f"Status Code: {response.status_code}")
    except requests.exceptions.RequestException as e:
        # ここで接続エラーが出る場合、ルーティングの戻り経路が怪しい
        print(f"Connection Failed: {e}")

if __name__ == "__main__":
    test_internet_access()

もし、このスクリプトが Connection Timeout を吐き続ける場合、tcpdump でインターフェースを確認してください。

# プライベートサブネット内のEC2で実行
# NATGWを通るパケットのSYN/ACKを確認
sudo tcpdump -i eth0 host api.example.com -n

—

4. トラブルシューティングの極意:デバッグの優先順位

現場でネットワーク障害が起きたら、以下の順序で確認するのが「最短ルート」です。

1. ルートテーブルのLongest Prefix Match:
0.0.0.0/0 とオンプレへのルートが競合していないか? VGW 側でオンプレIPが正しくアドバタイズされているか?
2. セキュリティグループとNACL:
「戻りパケット」を拒否していませんか?特に NACL はステートレスなので、エフェメラルポート(1024-65535)の許可を忘れると一発で落ちます。
3. NATGWのEIP制限とSNAT:
NATGWを介する通信は、クライアントIPがNATGWの EIP に書き換わります。相手側(オンプレ側やAPI先)で、そのIPからのアクセスがホワイトリストに入っているか確認してください。

Tips: curl を使う際の強力な武器

curl を使う際は、どのネットワークインターフェース(またはIP)を通って通信しているかを詳細に出力させると、切り分けが劇的に速くなります。

# 接続先の詳細なトレースを確認
curl -Iv https://api.example.com --trace-ascii debug.log

—

最後に:ネットワークは「隠蔽」できない

クラウドは抽象化が進んでいますが、ネットワークの物理法則は変わりません。パケットは必ず行きと帰りの経路を通り、それぞれのルーターが判断を下します。

「動いているからいいや」ではなく、「なぜこのパケットがこのルートを通るのか」を論理的に説明できる設計を心がけてください。それが、大規模障害を未然に防ぐ唯一の防御策です。

もし皆さんの現場で、「なぜかここだけ通信が落ちる」という不可解な事象があれば、まずはルーティングテーブルの「一番具体的なルート」がどこに向いているか、再確認することから始めてみてください。健闘を祈ります。

コメント

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