「なぜDNSクエリが突如失敗するのか?」NATゲートウェイとUDPの切ない関係を紐解く
クラウドエンジニアとして現場を歩いていると、避けては通れないのが「UDPとNATの不仲」です。TCPならば3ウェイ・ハンドシェイクでセッション確立を明示できますが、UDPは本質的にステートレス。しかし、AWSのNAT GatewayやGCPのCloud NATといったメガクラウドのゲートウェイは、ステートフルにパケットを監視し、セッションを管理しています。
この「ステートレスなプロトコルを、無理やりステートフルなNATゲートウェイに通す」という歪みが、時として本番環境で致命的なトラブルを引き起こします。今日は、この「NATタイムアウト」という泥沼に足を取られないための知見を共有しましょう。
—
1. なぜNATゲートウェイは「ステートフル」に振る舞うのか
パブリックサブネットにあるNATゲートウェイは、プライベートサブネット内のリソースから送信されるパケットを待ち受け、送信元IPを自身のアドレスに書き換え(SNAT)、パブリックインターネットへと送り出します。
ここで重要なのは、「戻りのパケットを誰に返すべきか」をNATゲートウェイが記憶しておく必要があるという点です。これを「ステートフルパケットインスペクション(SPI)」と呼びます。
UDPにおけるセッション管理の限界
TCPの場合、FINやRSTパケットによってセッション終了が明確ですが、UDPにはそうした「終わり」の概念がありません。そのため、NATゲートウェイは以下の戦略をとります。
1. セッション登録: 初めてのUDPパケットが来たら、送信元IP/Portと宛先IP/Portの組をNATテーブルに記録する。
2. タイマー起動: 一定時間(アイドル状態)が経過したら、そのセッションを強制的に破棄する。
この「一定時間」が、AWSでは350秒、GCPでは30秒〜10分(プロトコルや設定による)といった仕様になっています。このタイムアウト値を跨いで通信を行うと、NATゲートウェイは「もうこのセッションは終わった」と判断し、後から返ってきたパケットを容赦なく破棄(ドロップ)します。
—
2. 現場で遭遇する「消えたパケット」の正体
DNS(UDP/53)やSNMP(UDP/161)のような短命なトラフィックでは問題になりにくいのですが、例えば「UDPベースのロングポーリング」や「独自プロトコルのストリーミング」を行っている場合、このタイマーは最大の敵となります。
トラブルシューティングの定石:tcpdumpで追いかける
もしアプリケーション層で「レスポンスが返ってこない」という事象が発生したら、まずはNATゲートウェイの前後でパケットが消失していないか確認しましょう。
# プライベート側のインスタンスでパケットを確認
# 宛先ポート53への通信が送信されているか
tcpdump -i eth0 udp dst port 53 -n
もし、送信パケットはあるのに戻りのパケットが全く見当たらない場合、NATゲートウェイが「セッションを既に切断している」可能性を疑ってください。
—
3. 実務で使える回避策:Keepaliveの実装
アプリケーションコード側でこのタイムアウトを回避する唯一にして最強の方法は、「タイマーが切れる前にダミー通信を送り続ける」ことです。これをKeepaliveと呼びます。
Pythonでの実装例
UDPソケットを利用する際、送信間隔をNATのタイムアウト値(例:30秒)よりも短い間隔(例えば20秒)に設定します。
import socket
import time
def send_keepalive(target_ip, target_port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
# 空のパケットを送信してNATテーブルの寿命をリセットする
# ※サーバー側でこのパケットを無視する実装が必要
sock.sendto(b'', (target_ip, target_port))
print("Keepaliveパケットを送信しました")
# NATタイムアウト(30秒)より短い間隔でループ
time.sleep(20)
# 実行
# send_keepalive('1.1.1.1', 53)
—
4. 設計段階で意識すべき「攻め」のアーキテクチャ
トラブルが起きてから対処するのはSREの仕事ですが、そもそも「UDPで長時間通信を維持する」設計は、メガクラウドのネットワーク構成上、アンチパターンです。以下の設計指針を強く推奨します。
- TCPへの移行を検討: セッション管理をOSやプロトコルに任せられるTCPは、やはり安定しています。
- プロトコルの見直し: もしHTTPベースであれば、gRPC(HTTP/2)のような持続的な接続を前提としたモダンなプロトコルを採用してください。
- NATゲートウェイの制限を理解する:
- AWSのNAT Gatewayは、UDPのセッションアイドルタイムアウトが固定(350秒)です。これを調整することはできません。
- GCPのCloud NATは、設定次第でUDPのタイムアウト値を調整可能ですが、いたずらに延ばすとリソース枯渇を招きます。
最後に
ネットワークのトラブルは、往々にして「レイヤー4の仕様をレイヤー7のエンジニアが知らない」ことに起因します。NATゲートウェイは魔法の箱ではなく、限られたテーブルリソースをやり繰りする「番人」です。
「なぜこのパケットが捨てられたのか?」と悩んだときは、パケットがNATゲートウェイという関所を通り抜けた後の「記憶」が、どれくらいの時間保持されているのかを想像してみてください。その視点を持てれば、ネットワークトラブルは必ず解決できます。
現場からは以上です。引き続き、堅牢なシステム構築を楽しんでいきましょう。
コメント