VPC Peeringを「NATゲートウェイ共有のハブ」にする:コストと運用の現実解
こんにちは、SREの現場で日々パケットの行方を追っているエンジニアです。
皆さんはAWSやGCPでマルチVPC環境を構築する際、各VPCに律儀にNAT Gatewayを配置していませんか?開発環境、ステージング、本番環境……と数が増えるたびにNAT Gatewayの利用料(時間課金+データ処理課金)が積み上がり、月初の請求書を見て冷や汗をかいた経験は一度や二度ではないはずです。
今回は、そんな「VPCごとのNAT Gateway」という贅沢な設計を卒業し、「共有NATゲートウェイ(Transit VPC / Centralized Egress)」を構築するための実務的な設計パターンと、その裏側にあるルーティングの深淵を紐解いていきます。
—
なぜ「NATゲートウェイの共有」が必要なのか
理論上、各VPCにNAT Gatewayを置くのはもっともシンプルです。しかし、実務においてそれが最適解にならない理由は明白です。
1. コストの最適化: NAT Gatewayは地味に高い。小規模なVPCが10個あれば、それだけで毎月数万円の固定費が消えます。
2. 監査とセキュリティ: 外部への出口(Egress)を一箇所に絞ることで、FirewallやIDS/IPSのログ収集・監視を一元化できます。「どこから誰が外に出ているか」を単一のポイントで制御するのは、ガバナンスの観点からも極めて有効です。
—
通信フロー:パケットはどこを駆け巡るのか
VPC Peering経由でNAT Gatewayを共有する場合、重要なのは「ルーティングの再帰性」です。
基本的な通信シーケンス
1. Source VPC (Private Subnet): アプリケーションが curl https://api.example.com を実行。
2. Route Table: 0.0.0.0/0 の宛先を「VPC Peering Connection」経由で「Transit VPC」へ転送。
3. Transit VPC (NAT Gateway): パケットが到着。NAT Gatewayが送信元IPを自身のEIP(Elastic IP)に書き換え、インターネットへ放流。
4. Return Path: 戻りのパケットは NAT Gateway → インターネット → Private Subnet という経路を辿る。
注意点: VPC Peeringは推移的なルーティングをサポートしません。つまり、AとBが繋がっていて、BとCが繋がっていても、AからCへは通信できません。NAT Gateway共有モデルでは、あくまで「各VPCからハブVPCへのスター型接続」を前提とする必要があります。
—
設定の実装:Terraformでの定義例
言葉よりもコードの方が雄弁です。Terraformで「ハブ&スポーク」のルーティングを定義する際、最もミスが起きやすいのがルートテーブルの記述です。
# スポークVPC側(ここからハブVPCへパケットを投げる)
resource "aws_route" "to_nat_gateway" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
vpc_peering_connection_id = aws_vpc_peering_connection.spoke_to_hub.id
# ここでハブVPC側を向くように設定する
}
# ハブVPC側(戻りパケットをスポークへ返す)
resource "aws_route" "to_spoke_vpc" {
route_table_id = aws_route_table.nat_gateway_rt.id
destination_cidr_block = "10.1.0.0/16" # スポークVPCのネットワーク範囲
vpc_peering_connection_id = aws_vpc_peering_connection.spoke_to_hub.id
}
—
運用上の落とし穴:トラブルシューティングの作法
この構成を導入して「通信できない!」と叫ぶ新人の多くは、以下のいずれかにハマっています。
1. MTUサイズの不一致
VPC Peering経由の通信は、カプセル化の影響でMTU制限を受けることがあります。特に大きなHTTPリクエストを送る場合、パケットがドロップすることがあります。ping コマンドでフラグメントサイズを確認してみてください。
# MTUサイズを調整して疎通確認(Linuxの場合)
ping -M do -s 1460 8.8.8.8
2. セキュリティグループの盲点
「ルートテーブルは完璧なのに繋がらない」場合、ハブVPC側のNAT Gatewayが配置されているサブネットの「ネットワークACL(NACL)」を疑ってください。NACLはステートレスなので、インバウンド(Return)の許可設定を忘れると通信は即座に遮断されます。
3. アプリケーションコードでのタイムアウト設定
共有NAT Gatewayは、当然ながらトラフィックが集中します。接続プールが枯渇すると Connection Timeout が頻発します。PythonでAPIを叩く際は、必ず timeout パラメータを明示的に設定しましょう。
import requests
# タイムアウトを設定せずに放置するのは、ネットワークエンジニアとして厳禁
try:
response = requests.get(
'https://external-api.com/data',
timeout=(3.05, 10) # 接続に3.05秒、読み取りに10秒の猶予を持たせる
)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"ネットワーク経路上の問題の可能性: {e}")
—
最後に:SREからのアドバイス
NAT Gatewayの共有は、コスト削減という甘い果実の裏に、ネットワーク構成の複雑化という代償を伴います。
もしあなたが今後、さらに大規模なマルチリージョン、マルチアカウント環境を構築する予定なら、VPC Peeringではなく AWS Transit Gateway への移行を検討してください。Transit Gatewayは今回解説したような「ルーティングのパズル」をハードウェア的に解決し、より柔軟な拡張性を与えてくれます。
ネットワークは「繋がって当たり前」の世界です。しかし、その「当たり前」の裏側で、無数のパケットがどのようなルールで転送されているのかを理解しておくこと。それが、障害発生時に冷静に traceroute を叩けるエンジニアの条件です。
皆さんのインフラが、今日も安定してトラフィックを捌ききれますように。何か詰まったら、いつでもレイヤー3の視点に立ち返ってくださいね。
コメント