名前解決だけが遅い・たまに失敗する
「ネットワークが不安定」で片付けられがちなDNS起因の遅延と間欠失敗を特定できる。解決経路を1段ずつ分離する手順と、IPv6フォールバック・TTL・検索ドメインという3大原因の見分け方が身につく。
- アプリの遅延の一部がDNSかどうかは、curlの time_namelookup を見れば一発で分かる。接続時間と分けて計測しないと永久に見えない。
- 間欠的な失敗と5秒前後の固定的な遅延は、DNSの典型的な指紋。5秒はリゾルバの再送タイムアウトで、IPv6のAAAA問い合わせが黙って捨てられている場合に頻出する。
- 切り分けはスタブリゾルバ・キャッシュ・上位リゾルバ・権威サーバーの4段を分離すること。digで各段を直接叩けば、どこで遅延や失敗が生じているかが確定する。
DNS 起因の障害は「たまに繋がらない」「時々やたら遅い」という曖昧な形で現れ、ネットワークが不安定という結論で片付けられがちです。しかし DNS は解決経路が明確に段階化されているので、実は最も切り分けやすい部類に入ります。要点はただひとつ、名前解決の時間を接続時間から分離して計測することです。
まず名前解決だけの時間を測る
横にスクロール
curl は各フェーズの所要時間を出力できます。これを見れば、遅延の何割が DNS かが即座に分かります。
curl -o /dev/null -s -w \
'dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s total=%{time_total}s\n' \
https://api.example.com/health
dns=0.004s connect=0.031s tls=0.084s total=0.142s → DNSは無罪
dns=5.021s connect=5.048s tls=5.101s total=5.160s → ほぼ全部DNS。しかも5秒ちょうどが怪しい
time_namelookup は累積値なので、これが大きければ後続もすべてその分ずれます。「全体が5秒」ではなく「名前解決に5秒」だと分かるだけで、調査範囲が一気に狭まります。
実際の処理が偶然きっかり5秒になることはまずありません。5秒・10秒・30秒のような切りのよい値は、ほぼ確実にどこかのタイムアウトと再送です。DNSの場合、多くのリゾルバの既定タイムアウトが5秒で、応答が返らないと再送します。この値を見た時点で「何かが黙って捨てられている」と判断してよく、遅い処理を探すのは筋が悪くなります。
解決経路を4段に分けて考える
名前解決は1つの処理ではなく、少なくとも4つの段を通ります。どの段で遅延・失敗しているかを分離するのが切り分けの本体です。
| 段 | 実体 | そこを直接叩く方法 |
|---|---|---|
| 1. スタブリゾルバ | アプリ/libcの解決処理。検索ドメイン、hosts、nsswitch | getent hosts <name> |
| 2. ローカルキャッシュ | systemd-resolved、dnsmasq、コンテナ内のキャッシュ | resolvectl query(あれば) |
| 3. 上位リゾルバ | 設定された参照先(社内DNS、クラウド提供のリゾルバ) | dig @<resolver> <name> |
| 4. 権威サーバー | そのゾーンを持つサーバー | dig +trace <name> |
段を1つずつ直接叩けば、どこから遅くなるかが確定します。
# 3段目(上位リゾルバ)を直接、時間つきで
dig @10.0.0.2 api.example.com +stats | grep -E 'Query time|status'
# 4段目までルートから辿る(キャッシュを迂回する)
dig +trace api.example.com
dig が速いのにアプリが遅いなら、犯人は1段目か2段目です。dig も遅いなら3段目以降です。この一手だけで問題が半分に割れます。
三大原因
1. IPv6のAAAA問い合わせが黙って捨てられている
現代のスタブリゾルバは、A(IPv4)と AAAA(IPv6)をほぼ同時に問い合わせます。ここで AAAA だけがファイアウォールやミドルボックスに黙って捨てられると、A の応答は即座に返るのに、AAAA の応答を待ってタイムアウトまで固まります。これが「きっかり5秒」の最頻の正体です。
# AだけとAAAAだけを別々に測って比較する
dig api.example.com A +stats | grep 'Query time'
dig api.example.com AAAA +stats | grep 'Query time'
片方だけが遅い、あるいは応答が返らないなら確定です。対処は経路上で AAAA を通す(拒否するなら DROP ではなく明示的に応答を返す)か、アプリ側で IPv4 優先にするか、あるいは Happy Eyeballs で待たずに進む実装を使うことです。黙って捨てるのが最悪で、明示的に「無い」と答えるだけでこの遅延は消えます。
2. 検索ドメイン(search domain)の総当たり
/etc/resolv.conf の search に複数のドメインが並んでいると、修飾されていない名前は全候補を順に試します。候補が5つあれば、失敗するたびに問い合わせが1往復ずつ増えます。
search ns.svc.cluster.local svc.cluster.local cluster.local example.com
ndots:5
api.example.com を引く(ドットが2個 < ndots:5 なので「相対名」とみなされる)
→ api.example.com.ns.svc.cluster.local … NXDOMAIN
→ api.example.com.svc.cluster.local … NXDOMAIN
→ api.example.com.cluster.local … NXDOMAIN
→ api.example.com.example.com … NXDOMAIN
→ api.example.com. … ようやく成功
外部ドメインを引くたびに無駄な問い合わせが4往復増える計算です。コンテナ環境で「外部APIの呼び出しだけ妙に遅い」なら、まずここを疑います。対処は末尾にドットを付けた完全修飾名(api.example.com.)を使うか、ndots を下げることです。末尾のドットは「これは絶対名だから検索ドメインを試すな」という明示的な指示になります。
3. TTLとキャッシュのずれ
切り替えたのに古いIPに行き続ける、あるいは逆にキャッシュが効かず毎回問い合わせている。前者は TTL が長い、あるいはアプリやランタイムが独自に解決結果を無期限キャッシュしている場合に起きます。後者は TTL が極端に短い設定で、名前解決が毎リクエスト発生して遅延を積み上げます。
# 権威サーバーが返している本来のTTLを見る(キャッシュを迂回)
dig +trace api.example.com | tail -5
# キャッシュ経由の残りTTL(引くたびに減っていく)
dig api.example.com | grep -A1 'ANSWER SECTION'
ネガティブキャッシュも見落とされがちです。存在しない名前を引いた結果(NXDOMAIN)もキャッシュされるため、「レコードを作ったのにしばらく引けない」が起こります。この待ち時間はゾーンの SOA レコードで決まります。
一部のランタイムやライブラリは、OSのキャッシュとは独立に解決結果を保持します。無期限にキャッシュする実装だと、プロセスを再起動するまで新しいIPに切り替わりません。「dig では新しいIPが返るのにアプリだけ古いIPに行く」なら、これが答えです。長時間稼働するプロセスでは特に効いてきます。
間欠的な失敗を追う
「たまに失敗する」場合に見るべき点は3つです。
複数のリゾルバのうち1台だけが壊れている。 resolv.conf に複数の nameserver が並んでいると、順に試すか分散します。1台だけ応答しないと、そこに当たったときだけ失敗・遅延します。1台ずつ dig @ で直接叩いて比べるのが確実です。
UDPの応答サイズ超過。 応答が大きいとフラグメントされるか、TCP へのフォールバックが必要になります。中間の機器が大きなUDP応答やDNS over TCP を通さないと、レコード数が多い名前でだけ間欠的に失敗します。dig +tcp で成功するなら強い証拠です。
問い合わせ量の上限。 クラウドの仮想ネットワークやコンテナ基盤には、インスタンスあたりの秒間問い合わせ数に上限があることがあります。上限に当たると静かに捨てられるため、負荷が高いときだけ失敗するという症状になります。原因2の検索ドメイン総当たりと重なると、実際の問い合わせ数が数倍になって上限に届きやすくなります。
反復解決の仕組みそのものはDNS反復解決シミュレータで動かして確認でき、プロトコルの詳細はRFC 1035にまとめてあります。
まとめ
- 最初にやるのは
time_namelookupで名前解決の時間を分離すること。接続時間と混ぜたままでは永久に見えない。 - 5秒・10秒のような切りのよい遅延はタイムアウトの指紋。遅い処理ではなく「黙って捨てられているもの」を探す。
- 解決経路をスタブ・ローカルキャッシュ・上位リゾルバ・権威の4段に分け、
dig @とdig +traceで段ごとに直接叩く。digが速くアプリが遅いならスタブ側。 - 三大原因は AAAAの黙殺による5秒待ち・検索ドメインの総当たり・TTLとキャッシュのずれ。完全修飾名の末尾ドットは検索ドメイン問題への即効薬。
- 間欠失敗はリゾルバ1台の故障・UDP応答サイズ・問い合わせ数の上限を疑う。ランタイム独自のDNSキャッシュはOSと別に存在する点も忘れない。
トラブルシュート実戦の記事ガイド
名前解決だけが遅い・たまに失敗するを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
トラブルシュート
比較で見る軸
難易度: advanced / カテゴリ: トラブルシュート実戦 / タグ数: 5
導入後に効く点
間欠的な失敗と5秒前後の固定的な遅延は、DNSの典型的な指紋。5秒はリゾルバの再送タイムアウトで、IPv6のAAAA問い合わせが黙って捨てられている場合に頻出する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- トラブルシュート実戦
- タグ数
- 5
判断チェックリスト
- 自社の用途が「トラブルシュート / DNS」に近いか確認する。
- 強みである「アプリの遅延の一部がDNSかどうかは、curlの time_namelookup を見れば一発で分かる。接続時間と分けて計測しないと永久に見えない。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。