【テクニカル・上級編】 Cloud NATのポート枯渇問題(Port Exhaustion)とその検知・対策 – クラウドインフラと仮想化ネットワーク実践ガイド

Cloud NATの深淵:ポート枯渇という「見えない壁」を突破するアーキテクチャ

GCP上で大規模なマイクロサービスを運用していると、ある日突然、特定の外部API呼び出しが Connection timed out や Connection refused を吐き出し始める。ログを漁り、TCPスタックの挙動を疑い、果てはアプリケーションのコネクションプールを疑う。しかし、真犯人は常にネットワーク層の「見えない制限」に潜んでいる。

そう、Cloud NATのポート枯渇(Port Exhaustion)である。

今日は、パケットがVPCの境界を越えるその瞬間に何が起きているのか、そしてなぜ「IPを増やす」という単純な解決策の裏側に深いエンジニアリングの真実が隠されているのかを解説しよう。

1. なぜ「64,000」という限界が立ちはだかるのか

Cloud NATは、RFC 6305に基づくNAPT(Network Address Port Translation)として機能する。プライベートIPを持つVMが外部へ通信する際、Cloud NATは送信元IPを自身の外部IPへ、送信元ポートを一時的なエフェメラルポートへと書き換える。

ここで数学的な制約が発生する。TCP/UDPのポート番号は16ビット、つまり最大65,535まで。予約済みポートを考慮すると、事実上、1つの外部IPアドレスあたり利用可能なポート数は最大64,000だ。

観測すべき「5タプル」の悲劇

トラフィックは (送信元IP, 送信元Port, 宛先IP, 宛先Port, プロトコル) の5タプルで識別される。Cloud NATは「宛先IP + 宛先Port」のペアごとにポートを割り当てるわけではない。「送信元VM + 宛先IP + 宛先Port」の組み合わせが同じであれば、同じポートを再利用する可能性があるが、短時間に大量の異なる宛先へ向かうトラフィック(例:マイクロサービス間通信や外部API連携)がバーストすると、あっと言う間にポート上限に到達する。

2. 現場で役立つ検知と監視

「なんとなく遅い」で済ませてはいけない。Cloud Cloud Monitoringで以下のメトリクスを常に監視下に置くべきだ。

  • nat/port_usage: 現在利用中のポート数
  • nat/allocated_ports: 割り当て済みのポート数
  • nat/dropped_sent_packets_count: ポート枯渇によりパケットが捨てられた数

特に dropped_sent_packets_count が正の値を刻み始めたら、それは既にサービスレベルの死を意味する。

3. スケールアウトの真実:外部IPの動的割り当て

ポート枯渇が確定したら、即座に外部IPを追加する。しかし、単にIPを増やすだけでは最適解ではない。

# 既存のCloud NATルーターに対して外部IPアドレスを動的に追加する例
gcloud compute routers update [ROUTER_NAME] \
    --nat-external-ip-pool=[IP_NAME_1],[IP_NAME_2] \
    --region=[REGION]

ここで重要なのは、「なぜIPを増やすとポート数が増えるのか」という点だ。Cloud NATは、割り当てられたIPの数だけ、理論上の最大ポート数を倍増させる(IPあたり最大64,000ポート)。これにより、ハッシュ空間が広がり、衝突確率が劇的に低下する。

4. トランスポート層の最適化:根本的な「ポート節約」術

IPを増やすのは対症療法に過ぎない。真のSREは、そもそもポートを消費しないアーキテクチャを設計する。

TCPコネクションの再利用(Keep-Alive)

HTTP/1.1の Connection: keep-alive を適切に設定し、接続を維持することで、TCPのハンドシェイク(SYN/SYN-ACK/ACK)を繰り返すコストを削減できる。

TCPバッファとRTTのチューニング

Linuxカーネルの net.ipv4.tcp_fin_timeout を短縮し、TIME_WAIT 状態のソケットを速やかに解放することも有効だ。しかし、あまりに短すぎると、ネットワークの遅延により迷子になったパケットが新しいコネクションに干渉するリスクがある。

# sysctlでのチューニング例
# TIME_WAITの再利用を許可(慎重に検討すること)
sysctl -w net.ipv4.tcp_tw_reuse=1
# 送信元ポートの範囲を広げる
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

TLSハンドシェイクの最適化

TLS 1.3への完全移行を推奨する。1.3はハンドシェイクのRTTが短縮されており、コネクションの寿命を最適に管理しやすい。また、可能な限り gRPC 等のコネクション多重化プロトコルを利用することで、5タプルの消費量を抑えつつ、高いスループットを維持できる。

5. 最後に:インフラ屋の矜持

Cloud NATのポート枯渇は、クラウドというブラックボックスが突きつける「物理的な現実」だ。仮想化されたネットワークの向こう側には、必ず物理的な制限と、パケットをさばくためのハードウェアの論理が存在する。

設計段階で、通信の集約点となるマイクロサービスが「どれだけの外部APIを、どの程度の頻度で叩くのか」をサイジングすることは、モダンなSREにとって不可欠なスキルである。

パケットが詰まる前に、境界条件を理解し、IPを論理的にプールし、アプリケーションのコネクションを最適化する。それこそが、大規模分散システムを安定して走らせるための「現場の知恵」だ。

次に nat/dropped_sent_packets_count が跳ねたとき、パニックになる必要はない。君はすでに、その解決策を知っているはずだ。

コメント

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