夜間帯のデータセンターで、突如として鳴り響くアラート音。Web APIのレイテンシが跳ね上がり、フロントエンドからのリクエストがタイムアウトの嵐に飲まれている……。こういう修羅場をくぐり抜けてきたエンジニアなら誰もが知っているはずです。大抵の場合、犯人はコードではなく「DNS」です。
「いや、ドメインの向き先は変えたよ!」
「TTLも下げたし、キャッシュもクリアしたはずだ!」
チャットルームに飛び交う焦燥感に満ちたメッセージ。しかし、現実は非情にパケットを捨て続けます。こういう時、我々NOCのシニアエンジニアが真っ先に叩くのは、華麗なGUIツールでもなければ、高価なAPMツールでもありません。黒い画面を開いて放つ、地味ながら最強の武器、それが dig コマンドです。
今回は、DNSの内部構造を丸裸にし、ゾーン情報の不整合や委任エラー(Delegation Error)を一発で特定するための dig コマンドの出力セクション構造について、現場の泥臭い知見を交えて徹底的に解説します。
—
1. なぜ dig なのか? DNSパケットの裏側を覗く
世の中には多くのDNSルックアップツールがありますが、nslookup は歴史的経緯から出力フォーマットが不安定であり、実務の現場では使い物になりません。一方、dig(Domain Information Groper)は、DNSサーバーがやり取りする「生(ロー)のDNSメッセージ」をほぼそのまま我々に提示してくれます。
DNSのクエリとレスポンスは、UDP(またはTCP)の53番ポートを介してパケットとして流れています。そのメッセージ本体は、大きく分けて以下の5つの部分で構成されています。
1. Header(ヘッダーセクション): リクエストかレスポンスか、再帰問い合わせ(RD)が有効か、エラーコード(RCODE)は何かなど、通信のメタデータ。
2. QUESTION(質問セクション): 何を調べたいのか(ドメイン名とレコードタイプ)。
3. ANSWER(回答セクション): 質問に対する直接の答え。
4. AUTHORITY(権威セクション): そのゾーンの権威を持つネームサーバー(NS)の情報。
5. ADDITIONAL(追加情報セクション): AUTHORITYやANSWERを解決するために必要な追加のIPアドレスなど。
今回はこの中核をなす後半の4つのセクション(QUESTION、ANSWER、AUTHORITY、ADDITIONAL)に焦点を当てます。ここを読み解けるようになると、DNSのトラブルシューティング速度が文字通り10倍に跳ね上がります。
—
2. dig の出力セクション構造を実例で完全分解する
百聞は一見にしかず。実際に dig コマンドを実行した際の出力を例に、各セクションが何を語っているのかを紐解いていきましょう。
以下のコマンドを叩いたと仮定します。
$ dig api.example.com A
返ってくる標準的な出力の主要部分は、おおむね以下のような構造になっています(分かりやすくするために一部整形しています)。
; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> api.example.com A
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3
;; QUESTION SECTION:
;api.example.com. IN A
;; ANSWER SECTION:
api.example.com. 300 IN A 192.0.2.100
;; AUTHORITY SECTION:
example.com. 86400 IN NS ns1.dns-provider.example.
example.com. 86400 IN NS ns2.dns-provider.example.
;; ADDITIONAL SECTION:
ns1.dns-provider.example. 86400 IN A 198.51.100.1
ns2.dns-provider.example. 86400 IN A 198.51.100.2
;; Query time: 15 msec
;; RCODE: `NOERROR` (`0`)
この出力の後半、4つのセクションを上から順に解剖していきます。
① QUESTION SECTION(質問セクション)
> 「今、私は何を知りたいのか?」
ここには、クライアントがDNSサーバーに投げた「お題」がそのままエコーバックされます。
api.example.com.: 問い合わせ対象のドメイン名(FQDN)IN: クラス(Internetを指すINがほぼ100%です)A: レコードタイプ(IPv4アドレスを引く指定)
ここが意図したドメインやタイプになっていない場合、シェルのエスケープミスやタイポを疑うべきです。基本のキですが、現場ではここを見落として30分溶かす若手を何度も見てきました。
② ANSWER SECTION(回答セクション)
> 「お探しの答えはこちらです」
問い合わせに対する「ズバリの答え」がここに格納されます。
api.example.com.: 対象のドメイン300: TTL(Time To Live)。秒単位。この場合、300秒(5分間)はこの結果をキャッシュしていいという指示。IN A 192.0.2.100: 目的のIPアドレス。
【シニアの現場Tips】
Web APIの切り替え時に「新サーバーのIPに変わらない!」と騒ぐエンジニアがいますが、大抵はこのANSWERセクションのTTLが長い(例: 86400秒=24時間など)ことが原因です。キャッシュが残っているクライアントやリゾルバが古いIPを返し続けています。移行前には必ずTTLを数分(300や60など)に縮めておく、というインフラの定石を忘れないようにしましょう。
③ AUTHORITY SECTION(権威セクション)
> 「このゾーンを本当に管理しているのは俺たちだ(あるいは親ゾーンからの委任情報)」
ここが今回のメインテーマの肝です。ANSWERセクションに答えが見つかった場合、このAUTHORITYセクションには「このドメインの正当な権威サーバー(NSレコード)」の名前が列挙されます。
もし、問い合わせたサーバーがそのドメインの権威を持っていない(キャッシュサーバー等に直接聞いた場合など)かつ、ANSWERが見つからない場合、ここには「どこに聞きに行けばいいか(上位のNS情報)」が入ります。これがゾーン委任(Delegation)の仕組みです。
④ ADDITIONAL SECTION(追加情報セクション)
> 「おまけ(でもめちゃくちゃ重要)」
AUTHORITYセクションに書かれたネームサーバーの名前(例: ns1.dns-provider.example.)だけでは、IPアドレスが分からないと通信できませんよね。そのため、そのネームサーバー自身のIPアドレス(AレコードやAAAAレコード)を親切心(あるいは効率化のため)で添えてくれるのがこの追加セクションです。これがいわゆるグルーレコード(Glue Record)の絡みで重要になってきます。
—
3. 現場で頻発する「ゾーン情報の不整合」と「委任エラー」を暴く
では、これらのセクション構造を使って、実際の障害現場でどのように原因を特定するのか。よくある2つのトラブルシューティングのシナリオを解説します。
シナリオA:権威サーバーごとの「回答の不整合」を見破る
マルチクラウド構成や、社内DNSとパブリックDNS(Route 53やCloudflare等)を併用している環境で最も多いのが、「あるDNSサーバーにはレコードがあるのに、別のサーバーに行くと NXDOMAIN(ドメインが存在しない)になる」という不整合です。
これを暴くには、dig の引数に「直接調査したいネームサーバーのIP」を指定します。
# パブリックDNS(8.8.8.8)に直接聞いてみる
$ dig @8.8.8.8 api.example.com A
# 自社の権威DNSサーバー(192.0.2.50)に直接聞いてみる
$ dig @192.0.2.50 api.example.com A
ここで、一方のサーバーの ANSWER セクションには値が入っているのに、もう一方のサーバーでは ANSWER が空っぽになり、かつ AUTHORITY セクションに「SOA(Start of Authority)」レコードだけが返ってくる場合、それはゾーン転送の失敗やマスター/スレーブ間の同期漏れ(不整合)を意味しています。
;; AUTHORITY SECTION:
example.com. 3600 IN SOA ns1.example.com. admin.example.com. 2023100101 7200 3600 1209600 300
※ANSWERがなくAUTHORITYにSOAだけが返ってきたら、「このドメインの管理権限はあるけど、お前が探してる api なんてレコードはウチのゾーンファイルに無いよ!」というサーバーからの叫び声です。
シナリオB:最悪の悪夢「委任エラー(Delegation Error)」の特定
ドメインを取得し、さあネームサーバーを設定したぞ、という初期構築やドメイン移管のタイミングでよく起こるのが「委任切れ」や「不整合な委任」です。
親ドメイン(例: .com レジストリ)が指している子ドメインのNSと、子ドメイン側(自身の権威サーバー)で設定されているNSが一致していない場合、パケットは迷子になります。
これをトレースするには、ルートサーバーから順に追跡する +trace オプションを使います。
$ dig +trace api.example.com A
このコマンドを実行すると、以下のような美しい(しかし絶望的な)シーケンスがターミナルに流れます。
1. Rootサーバー(.)が com. を管理するトップレベルドメインサーバーのIPを教える(AUTHORITY & ADDITIONAL)
2. com. サーバーが example.com. の権威サーバー(NS)を教える(AUTHORITY)
3. しかし! example.com. の親が知っているNSと、子側が実際に持っているNSが食い違っていると、ここでクエリがループするか、SERVFAIL(サーバー障害)を吐いて沈黙します。
+trace の出力結果を追いかけ、どの階層の AUTHORITY セクションで足が止まっているかを確認することで、「ドメイン会社のコンパネでのNS設定ミス」なのか「DNSホスティング側のゾーン設定ミス」なのかをミリ秒単位で切り分けることができます。
—
4. 実務で役立つ!PythonスクリプトによるDNS死活・整合性チェック
「毎回コマンドを叩くのは面倒だ」「複数のネームサーバーに対して一括でレコードの存在確認をしたい」というインフラ・バックエンドエンジニアのために、Pythonの標準ライブラリ(または広く使われる dnspython ライブラリ)を使った実用的なスクリプトの断片を紹介します。
APIのヘルスチェックやCI/CDパイプラインの事前検証に組み込んでおくと非常に強力です。
import dns.resolver
import dns.exception
def check_dns_consistency(domain, expected_ip, nameservers):
"""
指定された複数のネームサーバーに対して個別にクエリを投げ、
期待するIPアドレスと一致するか(整合性が取れているか)を検証する関数
"""
resolver = dns.resolver.Resolver(configure=False)
results = {}
for ns_ip in nameservers:
resolver.nameservers = [ns_ip]
try:
# 指定したネームサーバーにAレコードを問い合わせ
answers = resolver.resolve(domain, 'A')
resolved_ips = [r.address for r in answers]
# 結果の判定
if expected_ip in resolved_ips:
results[ns_ip] = "OK (Matched)"
else:
results[ns_ip] = f"MISMATCH (Got: {resolved_ips})"
except dns.resolver.NXDOMAIN:
results[ns_ip] = "ERROR: NXDOMAIN (Domain not found)"
except dns.resolver.NoNameservers:
results[ns_ip] = "ERROR: No Nameservers available"
except dns.exception.Timeout:
results[ns_ip] = "ERROR: Timeout"
except Exception as e:
results[ns_ip] = f"ERROR: {str(e)}"
return results
if __name__ == "__main__":
target_domain = "api.example.com"
target_ip = "192.0.2.100"
# 比較検証したい主要なパブリックDNSや自社DNSのIPリスト
target_ns_list = [
"8.8.8.8", # Google Public DNS
"1.1.1.1", # Cloudflare DNS
"192.0.2.50" # 自社権威DNSサーバーのIP(例)
]
print(f"=== DNS Consistency Check for {target_domain} ===")
inspection_results = check_dns_consistency(target_domain, target_ip, target_ns_list)
for ns, status in inspection_results.items():
print(f"Nameserver [{ns}] -> {status}")
このスクリプトを運用監視サーバーや監視Lambda等に組み込んでおけば、「一部のISPユーザーからだけ繋がらない」といった局所的なDNS不整合トラブルを、ユーザーからのクレームが来る前に検知することができます。
—
5. おわりに:パケットの息遣いを感じろ
DNSは、インターネットという巨大な神経網の「名前空間」を司る心臓部です。
「設定を変えたのに繋がらない」というトラブルの9割は、DNSの仕組み、ひいては今回解説した dig の出力セクション(ANSWER、AUTHORITY、ADDITIONAL)が示す役割分担を正確に理解できていないことに起因します。
GUIの管理画面や便利なWebサービスに頼るのも悪くありませんが、いざという時に我々を救ってくれるのは、ターミナルに流れる無機質なテキストの海から真実を読み解く「眼」です。
次に障害に直面したときは、慌ててコードをいじる前に、まず dig を叩いてください。
パケットは、いつだって嘘をつきません。DNSメッセージの各セクションに耳を澄ませば、サーバーたちが今どこで迷子になっているのか、彼らの声が必ず聞こえてくるはずです。
コメント