「またDNSのトラブルか……」
深夜2時、鳴り響くアラートに目をこすりながら、私たちは何度この言葉を吐き捨ててきたでしょうか。
「ネットワークが繋がらない」「Web APIの呼び出しがタイムアウトする」「特定の取引先からメールが届かない」――インフラの現場で発生する怪奇現象の実に8割は、突き詰めるとDNS(Domain Name System)の名前解決に起因しています。そして、障害対応の最前線(NOC)で冷や汗を流しながらパケットキャプチャを追いかけるとき、私たちの最強の相棒となるのが、どのOSにも標準的に転がっている泥臭いツール、そう、nslookup です。
「いまさら nslookup か? 時代は dig だろう」
そんな声が聞こえてきそうです。確かに、詳細なDNSヘッダー情報や生の応答パケットを解析するには dig が優れています。しかし、踏み台サーバーに制限がある現場や、Windows/Linuxが混在するマルチプラットフォームの混迷を極めるトラブルシューティングにおいて、一切の事前準備なしにその場で立ち上がり、対話的にDNSの挙動を解剖できる nslookup の機動力は、いまだに色褪せていません。
今回は、標準的なRFCの仕様に則りつつ、実務でWeb API設計やインフラ運用に携わるエンジニアの皆さんに向け、nslookup の対話モードを駆使したDNSレコードの徹底的な調査手法と、その通信の裏側、そしてシステムへ組み込む際の実践的なコード例まで、現場のリアルな知見を凝縮してお届けします。
—
1. DNS問い合わせの深層:パケットが駆け巡る通信フロー
まずは、私たちが nslookup のキーを叩いたとき、ネットワーク上で何が起きているのかを復習しておきましょう。DNSの名前解決は、決して魔法ではありません。
クライアント(スタブリゾルバ)がDNSサーバーに問い合わせを行うとき、大きく分けて「再帰的問い合わせ(Recursive Query)」と「反復的問い合わせ(Iterative Query)」の2つのフェーズが存在します。
DNS名前解決のシーケンス図
[クライアント (nslookup)]
│
(1) │ 問い合わせ (再帰的: Recursive Query)
│ 「example.com の Aレコードは?」
▼
[キャッシュDNSサーバー (リゾルバ)]
│
├─(2) 問い合わせ (反復的: Iterative Query) ──► [ルートDNSサーバー]
│ ◄─ 「.com のネームサーバーを教える」 ──────┘
│
├─(3) 問い合わせ (反復的: Iterative Query) ──► [.com 権威DNSサーバー]
│ ◄─ 「example.com のネームサーバーを教える」 ─┘
│
└─(4) 問い合わせ (反復的: Iterative Query) ──► [example.com 権威DNSサーバー]
◄─ 「Aレコードは 192.0.2.1 です (TTL 3600)」 ─┘
│
(5) ▼ 応答 (クライアントへ返却)
「IPアドレスは 192.0.2.1 です」
1. スタブリゾルバ(クライアント)は、OSの設定(/etc/resolv.conf やネットワーク設定)に登録されているキャッシュDNSサーバーに向けて、ポート 53 (通常はUDP) で「再帰的問い合わせ」を投げます。「答えを知っていたら教えて、知らなければ自分で調べて持ってきて!」という丸投げの依頼です。
2. キャッシュを持たないリゾルバは、世界の頂点にあるルートDNSサーバーから順に、「反復的問い合わせ(お前がダメなら次を紹介してくれ)」を繰り返し、ドメインの階層(ツリー)を一段ずつ下っていきます。
3. 最終的にドメインの管理元である権威DNSサーバー(Authoritative Name Server)に到達し、正当なレコード情報を取得。
4. キャッシュDNSサーバーは、その結果を自身のメモリにTTL(Time To Live)秒間保持しつつ、クライアントへ応答を返します。
このとき、パケットサイズが 512 バイト(RFC 1035の古典的制限)を超える場合、またはDNSSECの検証などで情報量が膨らむ場合は、EDNS0 (Extension Mechanisms for DNS) という拡張仕様により大きなUDPパケットが使われます。それでも収まらない場合は、TCPポート 53 へのフォールバック(接続切り替え)が発生します。
現場のファイアウォールで「TCP 53番ポート」や「大きめのUDPフラグメントパケット」を遮断していると、特定のTXTレコードや多数のIPを持つドメインの名前解決だけが奇妙に失敗するという、非常に厄介なバグを引き起こすのです。
—
2. nslookup 対話モードの極意:各種レコードの引き方
それでは本題に入りましょう。nslookup を単発のコマンド(非対話モード)で実行するのも悪くありませんが、トラブルシューティングの現場では「対話モード」を強く推奨します。
対話モードに入るには、引数を指定せずに nslookup とだけ入力してEnterを押します。
$ nslookup
Default Server: your-local-dns.internal
Address: 192.168.1.1
>
プロンプトが > に変われば準備完了です。ここから、対話的に問い合わせ条件を切り替えながら、DNSの健康状態を診断していきます。
2.1 Aレコード(IPv4アドレス)の照会
基本中の基本、ホスト名からIPv4アドレス(32ビット)を引く A レコードです。
> set type=A
> www.example.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
Name: www.example.com
Address: 93.184.215.14
set type=A(またはset q=a)で、問い合わせるレコードタイプをAに固定します。Non-authoritative answer(権威のない回答)という表記に注目してください。これは、「権威DNSサーバーから直接聞いたのではなく、途中のキャッシュサーバーが持っていたキャッシュデータを返したよ」という意味です。本番環境のDNS変更が反映されないトラブルの多くは、この「権威のない回答」のキャッシュが残っていることが原因です。
2.2 AAAAレコード(IPv6アドレス)の照会
近年、コンテナ環境やAWSなどのクラウドインフラ、そしてモバイルネットワークではIPv6化が急速に進んでいます。IPv6アドレス(128ビット)を照会する場合は AAAA を指定します。
> set type=AAAA
> www.example.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
Name: www.example.com
Address: 2606:2800:220:1:248:1893:25c8:1946
「IPv4では繋がるのに、IPv6(AAAA)だとAPIの接続がタイムアウトする」というケースが多発しています。クライアントのOSが、デュアルスタック環境でIPv6を優先(RFC 6724)した結果、名前解決はできてもIPv6側のルーティングが詰まっている、といった原因を特定するのに役立ちます。
2.3 CNAMEレコード(別名)の照会
CDN(Cloudflare、CloudFrontなど)やロードバランサー(ALBなど)の配下にシステムを置く場合、必ずお世話になるのが CNAME(Canonical Name)レコードです。
> set type=CNAME
> api.yourdomain.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
api.yourdomain.com canonical name = d1234567890.cloudfront.net.
実務でよくある地獄は、「CNAMEの無限ループ」や「CNAMEと他のレコードの同居制限(RFC 1034)」の違反です。例えば、ドメインのルート(Zone Apex、例:yourdomain.com そのもの)に CNAME を設定することはRFC上禁止されています(MXレコードなどと競合するため)。これを確認するために、CNAME の参照先が正しく設定されているかを個別に検証する必要があります。
2.4 MXレコード(メールサーバー)の照会
「システムからの通知メールが顧客に届かない!」という悲鳴が上がったら、即座に調べるべきが MX(Mail Exchanger)レコードです。
> set type=MX
> yourdomain.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
yourdomain.com mail exchanger = 10 mail-server1.yourdomain.com.
yourdomain.com mail exchanger = 20 mail-server2.yourdomain.com.
MXレコードは、メールを受信するサーバーのホスト名と、その優先度(Preference)を返します。上記の例では、10 の方が優先度が高く、それが落ちている場合に 20 のサーバーへ配送されます。この優先度の設計ミスや、指定先ホストの A レコードが登録されていないというイージーミスが、メール不達トラブルの裏に潜んでいます。
2.5 TXTレコード(テキスト情報:SPF / DKIM / DMARC / ドメイン所有権確認)
昨今のセキュリティ要件において、最もトラブルの温床になりやすいのがこの TXT レコードです。送信ドメイン認証(SPF、DKIM、DMARC)の設定や、Google Workspaceなどの外部サービス連携時の「ドメイン所有権確認」に酷使されています。
> set type=TXT
> yourdomain.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
yourdomain.com text = "v=spf1 ip4:192.0.2.0/24 include:_spf.google.com ~all"
- SPFレコードの罠: SPF(
v=spf1...)の記述内にinclude:が多用され、ネストされた名前解決が 10回を超えるとSPF認証エラー(PermError) になります。nslookupでTXTを引き、その中身を分解して再帰的に調べることで、この「10回制限」を越えていないかチェックできます。 - 文字数制限の罠: RFC 1035において、1つのTXT文字列は
255バイト以内にする必要があります。それを超える場合(長大なDKIM公開鍵など)は、"string1" "string2"のように分割して記述する必要があります。ここが崩れてパースエラーになっていないか、生のTXT出力を凝視しましょう。
2.6 究極の武器:server コマンドによる問い合わせ先の変更
トラブルシューティング時、社内のDNSキャッシュサーバーを叩いているだけでは「世界からどう見えているか」が分かりません。「ネームサーバーの登録変更が世界に浸透したか」を知るためには、問い合わせ先DNSサーバーを直接指定します。
# 問い合わせ先を Google のパブリックDNS(8.8.8.8)に変更する
> server 8.8.8.8
Default Server: dns.google
Address: 8.8.8.8#53
# この状態で改めて A レコードを引く
> www.example.com
さらに、自社のドメインを管理している「権威DNSサーバー」をDNSレジストリ(Whois情報)から突き止め、そこに直接問い合わせを投げる(例: server ns-123.awsdns-15.com)ことで、「キャッシュのせいなのか、それとも権威DNSの設定自体が間違っているのか」の切り分けが一瞬で完了します。これは現場で最も重宝するテクニックです。
—
3. 現場で直面するDNSトラブルシューティングのリアル
ここで、私が過去に遭遇した血の気の引くような現場トラブルの事例と、nslookup を使った解決へのアプローチを共有します。
障害事例:新機能リリース直後、APIサーバーからのレスポンスが「時々」遅くなる
あるWebアプリケーションのリリース後、特定のAPIコールが時折数秒間ハングアップするという報告が入りました。アプリケーションログを見ても、DBクエリもCPUも正常。ネットワークの疎通も問題ありません。
私はおもむろに、APIサーバーのターミナルから nslookup を対話モードで起動し、外部連携先APIのドメインを繰り返し引いてみました。
$ nslookup
> server 127.0.0.11 # コンテナ環境のローカルリゾルバ
> set type=A
> partner-api.com
(1回目:一瞬で返ってくる)
> partner-api.com
(2回目:一瞬で返ってくる)
> partner-api.com
(3回目:5秒間の沈黙のあと、返ってくる!)
3回目に発生した「5秒の沈黙」。これこそがヒントでした。
DNSサーバーのタイムアウトのデフォルト値は通常 5 秒に設定されています。コンテナ内の /etc/resolv.conf を確認したところ、2つのネームサーバー(プライマリとセカンダリ)が指定されていました。
nameserver 10.0.0.2
nameserver 8.8.8.8
実は、プライマリである内部DNS(10.0.0.2)のルーティングテーブルに一瞬だけパケットロスが発生しており、問い合わせが失敗した際、OSのスタブリゾルバがセカンダリ(8.8.8.8)にフォールバックするまでの 「タイムアウト 5秒」 がそのままアプリケーションの遅延として露出していたのです。
nslookup で意図的に複数回リクエストを投げ、問い合わせ先サーバーを個別に server 10.0.0.2 と server 8.8.8.8 に切り分けて検証することで、内部DNSの特定のノードが悲鳴を上げている事実をわずか数分で突き止めることができました。
—
4. プログラムからDNSを制御する:Pythonによる高信頼な名前解決コード
インフラのデバッグができたら、次は日々の運用自動化や、アプリケーションのヘルスチェック機能にDNS検証を組み込むフェーズです。
実務において、Python標準の socket.gethostbyname() を使うのはお勧めしません。なぜなら、OSのキャッシュやホストファイル(/etc/hosts)に依存してしまい、特定のDNSレコード(MXやTXTなど)を詳細に検証できないからです。
ここでは、本番環境のDNS監視スクリプトでもそのまま使える、頑健な dnspython ライブラリを使用した実践的なコード例を示します。
まずは必要なライブラリをインストールします。
pip install dnspython
実用Pythonスクリプト:指定DNSサーバー経由でのレコード検証
import dns.resolver
import sys
def verify_dns_record(domain, record_type, target_dns_server=None):
"""
特定のDNSサーバーを指定し、各種DNSレコードを安全かつ詳細に照会する関数。
:param domain: 調査対象のドメイン(例: 'example.com')
:param record_type: レコードタイプ('A', 'AAAA', 'MX', 'TXT', 'CNAME' など)
:param target_dns_server: 問い合わせ先DNSサーバーのIP(Noneの場合はシステム既定)
"""
resolver = dns.resolver.Resolver()
# 問い合わせ先DNSサーバーを明示的に指定する場合(nslookupの 'server' コマンドに相当)
if target_dns_server:
resolver.nameservers = [target_dns_server]
# タイムアウトとリトライの制御(実務で非常に重要)
resolver.lifetime = 5.0 # 全体のタイムアウト(秒)
resolver.timeout = 2.0 # 1回あたりの応答待ち時間(秒)
print(f"--- Querying '{domain}' ({record_type}) via DNS Server: {resolver.nameservers} ---")
try:
# DNS問い合わせの実行
answers = resolver.resolve(domain, record_type)
for rdata in answers:
# レコードタイプに応じたパース処理
if record_type == 'MX':
print(f"[MX] Preference: {rdata.preference}, Mail Exchange: {rdata.exchange}")
elif record_type == 'TXT':
# TXTレコードは複数行に分割されている場合があるため結合して表示
txt_content = b"".join(rdata.strings).decode('utf-8')
print(f"[TXT] {txt_content}")
else:
print(f"[{record_type}] {rdata}")
# TTL(Time to Live)の確認
print(f"TTL: {answers.ttl} seconds (Remaining cache lifetime)")
except dns.resolver.NoAnswer:
print(f"[Error] レコードタイプ '{record_type}' は存在しますが、データが空です。", file=sys.stderr)
except dns.resolver.NXDOMAIN:
print(f"[Error] ドメイン '{domain}' が存在しません。", file=sys.stderr)
except dns.exception.Timeout:
print("[Error] DNS問い合わせがタイムアウトしました。ネットワークまたはDNSサーバーを確認してください。", file=sys.stderr)
except Exception as e:
print(f"[Error] 予期せぬエラーが発生しました: {e}", file=sys.stderr)
# 実用例の実行
if __name__ == "__main__":
# 1. Google のパブリックDNSを使って A レコードを引く
verify_dns_record("example.com", "A", "8.8.8.8")
# 2. 自社ドメインの SPF(TXT)レコードの確認
verify_dns_record("google.com", "TXT")
このコードは、まさに nslookup の対話モードで行う作業を、プログラムに自動実行させるためのものです。
resolver.nameservers を動的に書き換えることで、本番環境のデプロイ後に「特定の権威DNSサーバーにレコードが確実に伝播したか」をポーリングして監視するCI/CDパイプラインのスクリプトとしても応用可能です。
—
5. シニアエンジニアからのメッセージ:パケットを信じよ、キャッシュを疑え
ネットワークの世界において、DNSは空気のような存在です。普段は存在を意識することすらありません。しかし、ひとたびその空気が濁ると、世界中のあらゆるモダンなWebアプリケーション、マイクロサービス、クラウドインフラが一瞬にして沈黙します。
DNSのトラブルに直面したとき、私たちが肝に銘じるべき鉄則は以下の3つです。
1. 「DNSは浸透しない。キャッシュが残っているだけだ」
「DNSの反映には24〜72時間かかります」という古い説明を真に受けてはいけません。それは単に、古いレコードのTTL値が長すぎるか、どこかのプロバイダの行儀の悪いDNSサーバーがRFCを無視してキャッシュを保持し続けているだけです。nslookup で権威サーバーに直接当てて、自分の設定が正しいかをまず確認してください。
2. 「IPv6 (AAAA) と IPv4 (A) の挙動は等価ではない」
「自分のPCから繋がるから大丈夫」は禁物です。APIサーバーがIPv6優先で動いている場合、AAAAレコードの引き当てとその先のルーティングが死んでいれば、システムは沈黙します。必ず両方のレコードを個別にテストしてください。
3. 「セキュリティレコードの検証を怠るな」
SPF/DKIM/DMARCのミスは、企業の社会的信用(メールがスパム扱いされる)に直撃します。TXTレコードは手入力でのミス(スペースの有無、ダブルクォーテーションの閉じ忘れ)が発生しやすい戦場です。ツール任せにせず、生のTXTの中身を自分の目でパースする癖をつけましょう。
次にアラートが鳴ったときは、慌てず騒がず、ターミナルを開いて nslookup を起動してください。
シンプルなコマンドの向こう側で、パケットが世界中のネームサーバーを巡る様子を脳内に思い描くことができれば、あなたはもう一人前のトラブルシューターです。
コメント