夜中の3時、データセンターの静寂を切り裂くアラート音。
「APIのレスポンスが突如としてタイムアウトしている」「マイクロサービス間の名前解決に失敗している」――。障害対応の現場において、インフラエンジニアやWebエンジニアが真っ直ぐに向き合うべき悪夢の始まりです。
こういう時、多くのエンジニアはとりあえず ping を叩き、次にブラウザでアクセスしてみたりします。しかし、百戦錬磨のNOCエンジニアから言わせれば、現代のクラウドネイティブなインフラストラクチャや複雑なWeb APIアーキテクチャにおいて、アプリケーション層の不調の9割は「DNS」に原因があります。そして、そのDNSの迷宮に迷い込んだとき、教科書通りの nslookup や無機質な dig では、真実の姿を捉えることはできません。
今回は、パケットがDNSの海をどのように渡り、どのネームサーバで座礁しているのかを丸裸にする dig コマンドの高度な利用法、特に +trace オプションを用いた再帰的問い合わせの追跡について、現場の泥臭い知見を交えて徹底的に解説します。
—
1. なぜ nslookup では戦えないのか? DNSの裏側で起きていること
実務の現場でトラブルシューティングを行う際、私は若手エンジニアに「nslookup は今すぐアンインストールしろ、あるいは封印しろ」と指導することがあります。言い過ぎかもしれませんが、それくらい nslookup の挙動はブラックボックスであり、OSの resolver ライブラリの挙動に依存するため、デバッグツールとしては不完全なのです。
一方の dig (Domain Information Groper) は、DNSのパケットフォーマットをそのままむき出しでDNSサーバに叩き込む、まさにネットワークエンジニアのためのメスです。
DNSの解決フローを思い出してください。私たちが api.example.com というAPIのエンドポイントを叩いたとき、背後では次のような壮大なリレーが行われています。
1. フルスタブリゾルバ(クライアント) から ローカルDNSキャッシュサーバ(ISPや社内DNS、8.8.8.8 など) への問い合わせ。
2. キャッシュにヒットしなければ、ローカルDNSサーバが「再帰的問い合わせ(Recursive Query)」の代理として立ち上がる。
3. ルートネームサーバ(.)に問い合わせ、TLD(.com)のネームサーバのIPアドレスを手に入れる。
4. TLDネームサーバに問い合わせ、権威DNSサーバ(Authoritative DNS Server)のIPアドレスを手に入れる。
5. 権威DNSサーバに直接「api.example.com のIPは何か?」と問い詰め、最終的なレコード(AやCNAME)を手に入れる。
この一連のバケツリレーのどこかで、TTL(Time To Live)のキャッシュ汚染が起きているのか、あるいは権威DNSのレコード設定が間違っているのかを突き止めるには、キャッシュの介在しない「本当の経路」をトレースする必要があるのです。
—
2. +trace オプションでルートヒントから名前解決を暴く
ローカルのDNSキャッシュ(systemd-resolved や nscd、あるいはパブリックDNSのキャッシュ)に毒された古い情報をいくら眺めていても、障害の根本解決には至りません。そこで登場するのが、dig の必殺技 +trace です。
実際に、私の手元にある検証環境で example.com の名前解決の全貌をトレースしてみましょう。以下のコマンドを実行してください。
# キャッシュを一切バイパスし、ルートヒントから再帰的問い合わせを完全トレースする
dig +trace example.com
このコマンドを実行すると、ターミナルには次のようなログが滝のように流れます(出力はイメージです)。
; <<>> DiG 9.16.1-Ubuntu <<>> +trace example.com
;; global options: +cmd
. 5h17m48s IN NS a.root-servers.net.
. 5h17m48s IN NS b.root-servers.net.
... (中略: ルートサーバー13基の情報がキャッシュなしで引かれる) ...
;; Received 525 bytes from 127.0.0.1#53(53) in 2 ms
com. 172800 IN NS a.gtld-servers.net.
... (中略: .com TLDサーバーの情報) ...
;; Received 1172 bytes from 198.41.0.4#53(a.root-servers.net) in 42ms
example.com. 172800 IN NS a.iana-servers.net.
example.com. 172800 IN NS b.iana-servers.net.
;; Received 811 bytes from 192.5.6.30(a.gtld-servers.net) in 15ms
example.com. 86400 IN A 93.184.216.34
;; Received 56 bytes from 199.43.135.53(a.iana-servers.net) in 12ms
このトレースから何を読み解くべきか?
1. 最初のステップ: dig はあらかじめ持っているルートヒント(世界に13基あるルートサーバのIP群)に対し、一番最初のパケットを投げます。上記の出力では 198.41.0.4 (a.root-servers.net) から .com の権威サーバ群のリストをゲットしています。
2. 次のステップ: 次に .com のTLDサーバ(192.5.6.30)へ直接突撃し、example.com の権威サーバ(a.iana-servers.net)の情報を引きずり出しています。
3. 最終着地: 最後に example.com の権威サーバに対して直接クエリを投げ、目的の Aレコード (93.184.216.34) を獲得しています。
もし途中のステップでタイムアウトしたり、古いIPアドレスが返ってきた場合、「どの階層の権威DNSがゾンビ化しているか」が一目瞭然になります。大規模障害の切り分けにおいて、これほど頼りになる情報はありません。
—
3. 実務で即座に使える dig の高度なパラメーター集
+trace 以外にも、現場のインフラエンジニアが知っておくべき実用的な dig のオプションがあります。これらを組み合わせることで、ほとんどのDNSトラブルは解体可能です。
① 特定の権威DNSサーバを直接指し撃つ (@server)
パブリックDNS(8.8.8.8 や 1.1.1.1)や、特定の社内プライベートDNS(例: 10.0.0.53)に直接クエリを飛ばして、そのサーバがどう応答するかをテストします。
# GoogleのパブリックDNSに対して直接、api.example.comのAレコードを問う
dig @8.8.8.8 api.example.com A
# 自社の社内プライベートDNS(スプリットDNS環境など)の動作確認
dig @10.0.0.53 internal-api.corp.local A
② ゾーン転送の脆弱性を突く(セキュリティ監査) (AXFR)
インフラのセキュリティ監査やペネトレーションテストでは、不適切な設定によって誰でもドメインの全レコードを吸い出せてしまう「ゾーン転送 (AXFR)」の穴がないかをチェックします。
# 権威DNSサーバに対してゾーン転送を要求する
dig @ns1.example.com example.com AXFR
*※もしここで全レコードがダンプされてしまったら、即座にマネージャへエスカレーションしてください。重大なセキュリティインシデントの芽です。*
③ 冗長な出力を削ぎ落とし、純粋な値だけを得る (+short)
スクリプトやCI/CDパイプライン、コンテナの起動スクリプトなどで、IPアドレスだけをサクッと取得したい場合に重宝します。
# 余計なヘッダーやフッターを一切削ぎ落とし、IPアドレスのみを出力
dig +short api.example.com A
—
4. コードからのDNS解決と、トラブル発生時のアプローチ
インフラエンジニアが調査した結果を、アプリケーション層(Web API設計者やバックエンドエンジニア)へフィードバックし、コード側でどのようにハンドリングすべきかも合わせて解説します。
例えば、Pythonの requests や Node.js の fetch を使って外部APIを叩く際、DNSの引き直しやタイムアウトが適切に設定されていないと、DNSサーバの一時的なスローダウンに引きずられてアプリケーション全体がスレッドプール枯渇(ドミノ障害)を起こします。
Pythonによる堅牢なAPIリクエストとDNS挙動の確認
Pythonの requests ライブラリ自体は低レイヤーのDNSキャッシュを持ちませんが、OSの getaddrinfo を呼び出します。TTLの挙動を意識したカスタムセッションの例を示します。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_robust_session():
"""
DNSの不安定さや一時的なネットワーク断に対応するため、
リトライ戦略とコネクションプールを最適化したセッションを生成する。
"""
session = requests.Session()
# リトライ戦略の設定(DNS障害や5xxエラーに対する備え)
retries = Retry(
total=3,
backoff_factor=1, # 1秒, 2秒, 4秒とウェイトを増やす
status_forcelist=[500, 502, 503, 504],
raise_on_status=False
)
adapter = HTTPAdapter(max_retries=retries)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
if __name__ == "__main__":
api_endpoint = "https://api.example.com/v1/health"
client = create_robust_session()
try:
# タイムアウトを明示的に指定(接続に3秒、読み取りに5秒)
response = client.get(api_endpoint, timeout=(3.0, 5.0))
print(f"Status Code: {response.status_code}")
print(f"Response Body: {response.json()}")
except requests.exceptions.Timeout:
print("CRITICAL: APIへの接続がタイムアウトしました。DNSの遅延またはルーティング障害の可能性があります。")
except requests.exceptions.ConnectionError as e:
print(f"CRITICAL: 接続エラーです。digコマンドでの名前解決を確認してください: {e}")
トラブルシューティングの現場での実践的なフロー
もしあなたが今、APIの接続エラーに直面しているなら、次のステップでデバッグを進めてください。
1. まずは dig +short <domain> で現在の名前解決を確認する。
- ここでエラーになる、あるいは古いIPが出るなら、ローカルのキャッシュ汚染を疑う。
2. dig @8.8.8.8 <domain> でパブリックDNS上の状態を確認する。
- パブリック側が正常であれば、社内DNSやISPのDNSキャッシュが古い。
3. 最後に dig +trace <domain> を叩く。
- 権威DNSの設定ミス、あるいはドメインのレジストラ(お名前.comやRoute53など)でのネームサーバ(NSレコード)の登録不備をあぶり出す。
—
5. おわりに
DNSは、インターネットという巨大な神経網において「住所録」の役割を果たす極めて重要なインフラです。ここが揺らぐと、どれほど洗練されたマイクロサービスアーキテクチャも、美しく設計されたWeb APIも、一瞬にして砂上の楼閣と化します。
dig コマンドの +trace オプションをはじめとする高度な診断手法は、単なるコマンドの暗記ではありません。パケットが世界のどこを旅し、どの権威のバトンリレーで躓いているのかを「頭の中で視覚化する」ための羅針盤です。
障害の夜、パニックになりそうな時こそ、深呼吸をしてターミナルを開き、dig に語りかけてみてください。DNSのパケットは、必ず真実のルートを教えてくれるはずです。さあ、次のアラートに備えましょう。
コメント