Cloud NATのポート枯渇に泣く前に:その「接続エラー」の正体を見極めろ
クラウドインフラを設計・運用していると、ある日突然、アプリケーションログに「Connection timed out」や「Connection refused」が散見され始める――そんな悪夢のような経験はないだろうか。特に、複数のマイクロサービスが外部のWeb APIを頻繁に叩くような環境において、GCPの Cloud NAT を使っているなら、真っ先に疑うべきは「ポート枯渇(Port Exhaustion)」だ。
今日は、教科書的な仕様解説ではなく、現場で障害対応にあたるエンジニアが「なぜそこで止まるのか」を腹落ちするための、泥臭いネットワークの話をしよう。
—
1. なぜ「64,000」で限界が来るのか?
TCP/IPの通信において、クライアント側の送信元ポート番号は、0 から 65535 までの範囲で割り当てられる。Cloud NAT は、プライベートIPを持つVMが外部と通信する際、その送信元IPとポートを「NATゲートウェイの外部IP」と「動的に割り当てたポート」に書き換える。
ここで重要な制約がある。GCPの Cloud NAT では、1つの外部IPアドレスあたり最大64,000個のソースポートしか保持できないという仕様だ。
接続の「四元数(4-tuple)」を意識せよ
ネットワーク接続は以下の組み合わせで一意に識別される。
1. 送信元IP(NATゲートウェイのIP)
2. 送信元ポート(NATゲートウェイが割り当てたポート)
3. 送信先IP(APIサーバーのIP)
4. 送信先ポート(APIサーバーのポート)
「特定のAPIサーバーに対して大量のコネクションを張る」場合、送信先IPとポートは固定される。すると、NATゲートウェイは「送信元ポート」の組み合わせだけで通信を識別しなければならない。このとき、NATのポートプールを使い切ってしまうと、新しいコネクションは作れず、 Connection timed out が発生する。
—
2. 現場で「ポート枯渇」を検知する
「なんとなく遅い」で済ませてはいけない。Cloud Monitoringで可視化するのが鉄則だ。
Cloud Monitoringでの監視ポイント
以下のメトリクスをダッシュボードに表示しておくべきだ。
nat/gateway/sent_bytes_countnat/gateway/used_nat_ipsnat/gateway/allocated_ports(ここが重要!)
特に allocated_ports が64,000に近づいている、あるいはNATの割り当てルール上限に達している場合は、迷わず対策を打つ必要がある。
—
3. 実践:ポート枯渇を解消するテクニック
ポートが足りないなら、単純に「IPを増やす」か「効率を上げる」かの二択だ。
A. 外部IPアドレスの追加(スケーリング)
Cloud NATに複数の外部IPを割り当てることで、理論上の最大ポート数は IP数 × 64,000 にまで拡張できる。
# 既存のNATゲートウェイにIPアドレスを追加するgcloudコマンド例
gcloud compute routers nats update [NAT_NAME] \
--router=[ROUTER_NAME] \
--nat-external-ip-pool=[IP_ADDRESS_1],[IP_ADDRESS_2] \
--region=[REGION]
B. アプリケーション側の改善(コネクションプーリング)
最も健全なのは、インフラを増強する前に「無駄なコネクションを捨てる」ことだ。特にPythonやNode.jsで毎回 requests や fetch を使い捨てていないだろうか?
NGなコード例(毎回接続を切る):
# 毎回セッションを生成すると、TCPハンドシェイクとポート消費が激しい
import requests
def call_api():
# 毎回接続するため、FIN/ACKのやり取りでポートがTIME_WAIT状態に滞留する
response = requests.get("https://api.external-service.com/data")
return response.json()
OKなコード例(Session/Connection Poolを利用):
import requests
# セッションを再利用することで、同一ホストへのコネクションを維持する
session = requests.Session()
def call_api():
# 既存のコネクションを再利用するため、ポートの再割り当てが発生しにくい
response = session.get("https://api.external-service.com/data")
return response.json()
—
4. プロの視点:TCPコネクションの「寿命」を制御する
NATゲートウェイには TCP Established Timeout や TCP Transitory Idle Timeout といった設定値が存在する。これらは「アイドル状態のコネクションをどれくらいの時間保持するか」を定義するものだ。
もし、クライアント側のアプリケーションがコネクションを掴んだまま放置しているなら、これらのタイムアウト値を短く調整することで、不要なポート占有を強制的に解消できる。
- Established Timeout: 通信中のコネクションをどれだけ維持するか(デフォルトは通常1200秒)。
- Transitory Idle Timeout: 通信終了後の
TIME_WAIT等の状態をどれだけ維持するか(デフォルト30秒)。
これを短く設定することで、ポートの回転率を上げることができる。ただし、短すぎると頻繁に再接続が発生し、オーバーヘッドが増えるため、サービスの特性(APIのレスポンス頻度)を見極めてチューニングしてほしい。
—
まとめ:インフラは「繋がるのが当たり前」ではない
ポート枯渇問題は、ある日突然やってくる「サイレント・キラー」だ。特に開発環境では問題にならなかったのに、本番環境でトラフィックが増えた瞬間に発生する。
1. まずは監視: allocated_ports をダッシュボードに置く。
2. アプリを見直す: コネクションプーリングが正しく設定されているか確認する。
3. インフラを拡張: それでも足りなければ、Cloud NATの外部IPプールを増やす。
ネットワークのトラブルシューティングは、パケットの行方を想像することから始まる。NATの裏側で何が起きているのか、常にこの「四元数」の考え方を頭の片隅に置いておいてほしい。現場からは以上だ。
コメント