ルーティングの迷宮を解く:VPCピアリングとNAT Gatewayが交差する戦場での最適解
クラウドインフラの設計において、ルーティングテーブルは単なる「地図」ではない。それはパケットの運命を決定づける「法」であり、一度誤ればトラフィックは死の淵へと迷い込む。
特に、プライベートサブネットから外部へ向かうパケットが、VPCピアリングやTransit Gateway(TGW)、そしてNAT Gatewayという複数の出口候補と対峙したとき、何が起きているのか。現場のSREが直面する、この静かなるルーティングの衝突について深く掘り下げよう。
ルーティング評価の鉄則:最長一致(Longest Prefix Match)
まず、基本を再確認しておく必要がある。AWSのルーティングテーブルは、常に「最長一致(Longest Prefix Match)」の原則に従う。
例えば、10.0.0.0/16 への通信と 0.0.0.0/0(NAT Gateway)へのルートが競合している場合、たとえ 0.0.0.0/0 の優先度が高そうに見えても、より具体的なプレフィックスを持つルートが常に勝つ。これは、パケットの宛先IPがどの網に属しているかを判定する、OSI参照モデルのネットワーク層における絶対的な支配ルールだ。
しかし、問題は「同じ粒度のプレフィックス」あるいは「包含関係にない複数のルート」が並存する場合だ。ここでの挙動を理解せずに大規模なマルチリージョンネットワークを構築するのは、目隠しをして高速道路を走るに等しい。
NAT Gatewayの「死角」をハックする:トラフィックの制御
NAT Gatewayは単なる「プライベートとパブリックの橋渡し」ではない。これは、SNAT(Source NAT)という名の「ID改竄装置」であり、パケットが通過するたびにコネクション追跡(Conntrack)テーブルを消費する。
パケットレベルの最適化とTCPバッファのチューニング
高負荷なマイクロサービス環境では、NAT Gatewayを経由するトラフィックのTCPスタックチューニングが、そのままレイテンシの改善に直結する。特に、RTT(Round Trip Time)が大きな環境下では、Linuxカーネルの初期ウィンドウサイズが重要になる。
# TCPウィンドウサイズを拡大し、スループットを最大化する
# NAT Gateway経由の接続が頻発する場合、この設定で輻輳制御を最適化する
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
sysctl -w net.ipv4.tcp_slow_start_after_idle=0 # アイドル後のスロースタートを抑制
NAT Gatewayを経由する際、接続数制限(ポート枯渇)に直面するケースが多い。これを回避するためには、複数のNAT Gatewayへのトラフィック分散が必要だが、それ以上に「コネクションの再利用」こそが本質的な解決策だ。Keep-Aliveの設定を詰め、FIN_WAIT状態を最小限に抑えることが、SREとしての矜持である。
TGWか、ピアリングか:境界におけるセキュリティと性能
TGWを経由する通信とVPCピアリングを使い分ける際、最も注意すべきは「MTUの不一致」だ。
- VPCピアリング: 最大MTUは
1500バイト。 - Transit Gateway: 最大MTUは
8500バイト(ジャンボフレーム対応)。
TGWを経由するルートが 0.0.0.0/0 に近い優先度を持ってしまい、本来ピアリング経由で通信すべきパケットがTGWへ迂回すると、フラグメンテーションが発生し、パケットロスやCPU負荷の増大を招く。これを防ぐには、ルートテーブルの伝播(Propagation)設定を厳密に管理し、Blackholeルートを意図的に作成して「意図しない経路への流出」を塞ぐ必要がある。
セキュリティの深淵:TLSハンドシェイクの最適化
NAT Gateway経由の通信は、パケットの書き換えが発生するため、パケットキャプチャを行っても内部のTLSペイロードは見えない。しかし、TCPハンドシェイクの「SYN/ACK」の挙動を観測することで、ネットワークのボトルネックを特定できる。
TLS 1.3が主流の今、ハンドシェイクのラウンドトリップは最小化されているが、NAT Gatewayを経由する際の「追跡テーブルのルックアップオーバーヘッド」は無視できない。これに対処するためには、TCP Fast Openを有効にすることを推奨する。
# PythonのソケットレベルでのTCP Fast Open有効化例
import socket
# TFO(TCP Fast Open)を使用して、最初のSYNでデータ送信を試みる
# これにより、RTTを1往復削減できる
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_TCP, socket.TCP_FASTOPEN, 1)
結び:インフラは「コード」ではなく「パケットの哲学」である
ネットワーク構築において、「なんとなく繋がった」状態は、数ヶ月後に発生する謎のタイムアウトやパケットロスという形で牙を剥く。
1. 最長一致の原則を理解し、ルートテーブルをクリーンに保つ。
2. NAT Gatewayの限界を把握し、TCPスタックをチューニングする。
3. パス MTU ディスカバリを意識し、TGWとピアリングの混在環境でのフラグメンテーションを防ぐ。
これらは教科書には載っていない。すべて、深夜のトラブルシューティングと、パケットダンプを眺め続けた末に得られた血肉の知見だ。皆さんのインフラが、今日も最適なルートを見出し、静寂の中で安定して稼働し続けることを願っている。
コメント