NATゲートウェイの「沈黙」を解き明かす:VPCフローログとAthenaで突き止めるSNAT枯渇の正体
現場で「APIのレスポンスが突然返らなくなった」「断続的に504 Gateway Timeoutが出る」というアラートに叩き起こされた経験はありますか?
AWSのインフラ運用において、NATゲートウェイ(NATGW)は非常に優秀な黒子ですが、そのキャパシティを使い果たした瞬間に、ネットワーク運用者にとっての「悪夢」へと変貌します。特にSNAT(Source NAT)のポート枯渇は、パケットがNATGWの境界で黙って破棄されるため、アプリケーションログだけでは真因に辿り着くのが困難です。
今日は、VPCフローログとAmazon Athenaを武器に、この「見えないパケット」の行方を追跡し、ボトルネックを特定する泥臭いけれど確実な手法を伝授します。
1. なぜ「ポート」が枯渇するのか?
まず、基本のおさらいです。NATGWは、プライベートサブネットのインスタンスが外部へ通信する際、送信元IPをNATGWのEIPに変換します。このとき、TCP/UDPのポート番号を使って接続を識別しています。
NATGWには「宛先IPとポートの組み合わせ」ごとに割り当てられるポート数に上限があり、この上限を超えると、パケットは容赦なく REJECT されます。curl や Fetch API を使った外部APIへのリクエストが、ある一定の負荷を超えた瞬間にタイムアウトし始めるのは、まさにこのポート枯渇の典型的な挙動です。
2. VPCフローログの罠と設計
VPCフローログを単に有効化するだけでは不十分です。NATGWでの枯渇を特定するには、以下の設定が必須です。
- ログ形式:
srcaddr,dstaddr,dstport,protocol,action,pkt-srcaddr,pkt-dstaddrを含めること。 - 集約間隔: デフォルトの10分間隔では、瞬発的なバーストによる枯渇を見逃します。トラブルシューティング時は「1分間隔」に短縮するのが鉄則です。
3. Athenaで「犯人」を特定するクエリ
S3に吐き出されたフローログをAthenaで解析しましょう。NATGWで REJECT されているトラフィックを抽出するクエリがこれです。
-- NATゲートウェイで拒否されたトラフィックを特定するクエリ
SELECT
srcaddr AS source_instance_ip,
dstaddr AS destination_api_ip,
dstport AS destination_port,
COUNT(*) AS reject_count,
SUM(bytes) AS total_bytes
FROM
vpc_flow_logs_table
WHERE
action = 'REJECT'
AND interface_id = 'eni-xxxxxxxxxxxxxxxxx' -- NATGWのENI IDを指定
AND day = '2023-10-27' -- 特定の日付
GROUP BY
srcaddr, dstaddr, dstport
ORDER BY
reject_count DESC
LIMIT 20;
このクエリを叩くと、「どのインスタンスが」「どの外部APIに対して」「どれだけの回数拒否されたか」が浮き彫りになります。特定の送信元IPが異常に高い reject_count を示している場合、そのアプリのコネクションプーリング設定が甘い可能性が高いでしょう。
4. 実践:アプリケーション側のデバッグと対策
原因が特定できたら、アプリケーション側の修正です。例えば、Pythonで requests ライブラリを使っている場合、デフォルトでは接続を使い回さない設定になっていることがあります。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# セッションを明示的に作成してコネクションを再利用する
session = requests.Session()
adapter = HTTPAdapter(
pool_connections=50, # プールの接続数
pool_maxsize=50, # 最大接続数
max_retries=Retry(total=3, backoff_factor=0.1)
)
session.mount('https://', adapter)
# これで短時間のポート消費を抑える
response = session.get('https://api.external-service.com/data')
また、curl コマンドで検証する際は、以下のフラグで接続状態を確認してください。
# 接続の状態を確認しつつ、パケットロスがないかチェック
curl -v -w "TCP handshake: %{time_connect}, Start transfer: %{time_starttransfer}\n" \
https://api.external-service.com/health
5. SREとしての「深読み」
最後に、一つだけ現場の教訓を。NATGWの枯渇は、往々にして「バックエンドのAPI応答速度の低下」が引き金になります。APIの応答が遅くなればなるほど、コネクションが解放されず、ポートが占有され続けるという負のスパイラルが発生します。
フローログ解析の結果、REJECT が多発しているなら、まずは「宛先APIのレイテンシ」を疑ってください。インフラ側の対策(NATGWの追加やIPの分散)に逃げる前に、アプリケーションのタイムアウト設定を適切に短くし、コネクションの寿命をコントロールするのが、真の解決策への最短距離です。
ネットワークは嘘をつきません。パケットの流れ(フロー)を追えば、どんなに隠れたボトルネックも必ず姿を現します。皆さんのクラウド環境が、今日も健全であることを願っています。
コメント