NATゲートウェイの向こう側を透視する:VPCフローログで通信断の「犯人」を特定する実務的アプローチ
「開発環境からは繋がるのに、本番のプライベートサブネットからだとAPIがタイムアウトする」。
SREとして深夜の呼び出しを受けたとき、真っ先に疑うべきは「NATゲートウェイ(NAT GW)」と「セキュリティグループ(SG)」の境界線です。
パケットがプライベートサブネットを飛び出し、NAT GWでグローバルIPに変換され、インターネットの荒波へ漕ぎ出す……。この一連の動きの中で何が起きているのか。今回は、VPCフローログを武器に、NAT変換前後のパケットを追いかけて「通信断の真因」を炙り出す、現場のデバッグ術を解説します。
—
1. なぜNAT GWでパケットが迷子になるのか
プライベートサブネット内のインスタンスが外部APIを叩く際、パケットは次のように変身します。
1. 送信元(Private IP):10.0.1.5 から api.service.com へ送信
2. NAT変換:NAT GWを通過する際、送信元IPがNAT GWの Elastic IP に書き換えられる
3. 宛先(Public IP):インターネット上のAPIサーバーに到達
ここで「通信断」が起きる原因の多くは、以下の3点に集約されます。
- ポート枯渇:同一IPへの同時接続数が多く、Ephemeral Port(エフェメラルポート)が足りない。
- ACL/SGの不整合:NAT GWの戻りパケットを許可するルールが抜けている。
- パスMTU問題:大きなパケットがNAT GWでフラグメント処理できずに破棄されている。
これらを推測ではなく「証拠」として掴むために、フローログが不可欠なのです。
—
2. フローログで「追跡」するための設定と準備
フローログを有効にする際、単に「全て許可」にするのはやめましょう。コストとノイズの無駄です。特にデバッグ時は、対象のENI(Elastic Network Interface)に絞るのが定石です。
AWS CLIでのフローログ設定例
特定のENIに対して、詳細なログを取得する設定です。
# ターゲットとなるインスタンスのENI IDを確認し、フローログを作成する
aws ec2 create-flow-logs \
--resource-type NetworkInterface \
--resource-ids eni-0123456789abcdef0 \
--traffic-type ALL \
--log-group-name /vpc/flow-logs/debug-api \
--max-aggregation-interval 60 # 集約間隔を最短にしてリアルタイム性を確保
—
3. 実践:NAT変換前後の「痕跡」を追跡する
フローログをCloudWatch Logs Insightsで解析すると、NAT変換の前後関係が鮮明になります。以下のクエリは、特定の宛先IPへの通信を追いかける際に非常に有用です。
CloudWatch Logs Insightsの解析クエリ例
# NAT変換前後のIP/ポートの挙動を可視化するクエリ
fields @timestamp, srcAddr, dstAddr, srcPort, dstPort, protocol, action
| filter dstAddr = "203.0.113.10" # 接続先のAPIサーバーのグローバルIP
| sort @timestamp desc
ここで注目すべきは srcPort です。
もし action が REJECT になっているなら、それはセキュリティグループかネットワークACLの仕業です。逆に ACCEPT なのに応答がない場合は、NAT GWの先でのドロップか、サーバー側のレスポンスが戻ってきていないことを示唆します。
—
4. 検証コード:通信の「振る舞い」を再現する
デバッグのために、意図的に特定のポートを使って通信を試行するスクリプトを書いておくと便利です。Pythonの requests ライブラリを使えば、接続のタイムアウトを細かく制御できます。
import requests
# タイムアウトを短く設定し、失敗時の挙動を即座に確認する
def test_api_connection(url):
try:
# タイムアウトを2秒に設定
response = requests.get(url, timeout=2)
print(f"Status: {response.status_code}")
except requests.exceptions.ConnectTimeout:
print("エラー: 接続がタイムアウトしました。NATゲートウェイ、または宛先SGを確認してください。")
except requests.exceptions.RequestException as e:
print(f"予期せぬエラー: {e}")
# 実行
test_api_connection("https://api.external-service.com/v1/health")
—
5. SREが教える「現場のトラブルシューティングTips」
フローログを読み解く際、以下の事実に気づけるかどうかがシニアの分かれ道です。
1. エフェメラルポートの限界を見抜く:
srcPort が 32768 から 61000 の範囲で埋め尽くされている場合、接続数のオーバーフローが疑われます。この場合は、NAT GWを増設するか、接続先に対して Keep-Alive を適切に設定してコネクションを使い回す設計変更が必要です。
2. 非対称ルーティングの罠:
フローログで ACCEPT が続いているのに通信が成立しない場合、往路(パケット送信)はNAT GWを通るのに、復路(レスポンス)が別のルートを通ろうとしてファイアウォールに遮断されているケースがあります。ルーティングテーブルとルートの対称性を常に意識してください。
3. ログの「集約」に注意:
AWSのフローログはデフォルトで数分間集約されます。秒単位のスパイクを追う場合は、必ず max-aggregation-interval を最小(60秒)に設定すること。これを忘れると、重要な「瞬間的な拒否」を見逃します。
—
最後に:ネットワークは「嘘をつかない」
ネットワークトラブルにおいて、ログは唯一の真実を語ります。
「設定は合っているはずだ」という思い込みを捨て、パケットの送信元IPがNAT GWのEIPに変換されているか、変換後のポート番号が意図した範囲内か、そして戻りのパケットがどのインターフェースを通過しようとしているのか。
この一連のフローをフローログ上でなぞれるようになれば、あなたはもう単なる運用者ではなく、クラウドインフラを自在に操るエンジニアです。さあ、次はあなたの担当している環境のログを開いてみてください。そこに、まだ見ぬボトルネックが隠れているはずです。
コメント