なぜDNSは突然「消える」のか?NATセッションの深淵とUDPの憂鬱
ネットワークエンジニアとして現場に立っていると、必ずと言っていいほど直面する「不可解な通信断」があります。特にクラウド環境でKubernetesを運用していると、外向けのDNS解決が時折タイムアウトしたり、特定のUDPパケットだけがブラックホールに吸い込まれたかのように消える現象に出くわしたことはないでしょうか?
「設定は間違っていないはずなのに、なぜ?」
その答えの多くは、パブリックサブネットとNATゲートウェイの境界線にあるNATセッションのライフサイクルに隠されています。今日は、UDPという「ステートレス」な存在が、ステートフルなNAT装置の中でどのように扱われ、そしてなぜ死んでいくのか、そのリアルな挙動を紐解いていきましょう。
UDPは「話しっぱなし」のプロトコル
TCPであれば、3ウェイ・ハンドシェイクがあり、FINやRSTによってセッションの終了をNAT装置に通知できます。しかし、UDPには「セッションの開始」も「終了」もありません。
NATゲートウェイ(AWSのNAT GatewayやGCPのCloud NATなど)にとって、UDPパケットは単なる「通りすがりの訪問者」です。NATゲートウェイは、送信元IPとポートを書き換え、外の世界からの戻りパケットを正しい宛先に送り返すために、内部テーブルに「マッピング情報」を一時的に保持します。
問題は、この情報をいつ削除するかです。
アイドルタイムアウトという名の死神
NAT装置は、一定時間そのエントリに対する通信が途絶えると、「もうこの通信は終わっただろう」と判断してテーブルをクリアします。AWSのNAT Gatewayの場合、UDPのアイドルタイムアウトはデフォルトで350秒です。
もし、あなたがバックエンドでDNSキャッシュを持たず、かつアプリケーションの呼び出し間隔がこのタイムアウトを超えてしまったらどうなるか? 戻りのDNS応答パケットがNATゲートウェイに到達したとき、既にそのマッピングエントリは消滅しており、パケットは容赦なくドロップ(破棄)されます。これが、本番環境で突然DNSが引けなくなる原因のトップランカーです。
実践的デバッグ:なぜパケットは届かないのか
DNSフォワーディングを例に、実際に何が起きているかを探るための手法を見ていきましょう。まずは、Pythonの scapy 等を使うまでもなく、標準的なツールで現状を把握するのが定石です。
1. 通信の生存確認(Keep-aliveの意識)
もしアプリケーション側の修正が可能なら、DNSライブラリの設定で timeout を短くし、かつ再送回数を調整します。例えば、Pythonの dnspython を使う場合、以下のようにタイムアウト値を設計します。
import dns.resolver
# タイムアウトを短めに設定し、NATのアイドルタイムアウトを意識した設計にする
resolver = dns.resolver.Resolver()
resolver.timeout = 2.0 # 2秒で諦める
resolver.lifetime = 5.0 # 合計5秒以内で処理を終える
2. NAT越えの可視化:tcpdumpの活用
NATゲートウェイの「外側」と「内側」でパケットをキャプチャすると、その消失ポイントが明確になります。
# NATゲートウェイの内側(VPC内のインスタンス)でキャプチャ
sudo tcpdump -ni eth0 udp port 53
# 同時にNATゲートウェイの外側(もし可能ならNATインスタンスやVPN経由で)キャプチャ
# これでパケットが出て行っているのに帰ってきていない、あるいは帰ってきているのにNATで捨てられているかを追跡します
NATセッションを制御するためのアーキテクチャ設計
「NATのタイムアウトが短いから」といって、インフラをいじくり回すのは得策ではありません。クラウドのマネージドサービスはブラックボックスである以上、我々エンジニアは「セッションを殺さない」設計にシフトする必要があります。
DNSフォワーディングの最適化
Kubernetes環境であれば、CoreDNS の forward プラグインの設定が重要です。外部DNSへの問い合わせをNATゲートウェイ経由で行う場合、force_tcp を検討する価値があります。
# CoreDNSの設定例
.:53 {
forward . 8.8.8.8 8.8.4.4 {
# UDPのタイムアウトに翻弄されないよう、TCPでの通信を強制するオプション
# セッション管理が明確になるため、不安定なネットワーク環境では極めて有効
force_tcp
}
cache 300
errors
}
SREとしての教訓:ステートレスを過信しない
UDPプロキシやDNSフォワーディングを扱う際に忘れてはならないのは、「通信のライフサイクルは、ネットワーク機器が握っている」という現実です。
1. タイムアウトの把握: 使用しているNATサービスのUDPアイドルタイムアウト時間をドキュメントで確認する(AWSは350秒、GCPのCloud NATはデフォルト120秒など、環境によって全く異なります)。
2. 再送制御の設計: DNSクライアントやアプリケーションには、必ず「指数バックオフ」を伴う再送アルゴリズムを実装する。
3. オブザーバビリティ: メトリクス監視で「DNS解決のレイテンシ」だけでなく、「失敗数」をアラートラインとして引いておく。
教科書的なプロトコル仕様を理解しているだけでは、現代の複雑なクラウドネットワークは守れません。パケットが「どこで捨てられたか」を想像し、インフラとアプリケーションの境界線をどう握るか。それこそが、シニアなエンジニアに求められる「勘所」なのです。
次にDNSのタイムアウトに遭遇したときは、慌てて nslookup を叩く前に、一度NATゲートウェイのタイムアウト値を思い出してみてください。案外、答えはそこに転がっているはずですよ。
コメント