深夜3時、鳴り響くアラート音。Web APIの死活監視が赤色に染まり、スラックには「外部決済サービスへの接続がタイムアウトする」という非情なメッセージが流れ込む。こういう修羅場で、若手エンジニアが真っ先に疑うのはアプリケーションのバグやファイアウォールの設定だ。しかし、百戦錬磨のNOCエンジニアである私たちがまず疑うのは、大抵の場合「DNS」である。
「名前解決ができない。あるいは、古いIPアドレスを掴みに行っている」
このインフラの「縮図」のようなトラブルを暴くとき、私たちの手元にある最も古く、そして最も頼りになる相棒が nslookup だ。今回は、DNSレコードの照会から、意外と知られていない対話型モードの深掘り、そして実務で直面するトラブルシューティングの極意まで、現場の空気感を交えて徹底的に解説しよう。
—
1. なぜ今 nslookup なのか?(レガシー仕様の現在地)
現代のインフラ運用においては、より詳細なデバッグが可能な dig コマンドや、JSON出力に対応した kdig が好まれる傾向にある。実際、Linuxの多くのディストリビューションで nslookup は bind-utils パッケージに含まれるレガシーなユーティリティという位置づけだ。
しかし、考えてみてほしい。あなたが突然、未知の踏み台サーバーや、最小限のパッケージしか入っていないコンテナ環境に放り込まれたとき、そこに dig が入っている保証はあるだろうか? Windows環境であれば、標準で入っている名前解決ツールといえば実質的に nslookup 一択だ。
さらに言うなら、nslookup はOSのネイティブなDNSリゾルバライブラリ(Linuxなら getaddrinfo など)ではなく、独自のDNSパケットを組み立てて直接ネームサーバーに問い合わせる挙動をする(※OSや実装により多少異なるが、基本思想は独自クエリ)。この「OSのキャッシュに惑わされない純粋なDNSクエリの挙動」こそが、障害切り分けにおいて強力な武器となるのだ。
—
2. 実務で使う基本コマンドと通信フロー
まずは、パケットがネットワークをどのように駆け巡っているのかを頭に描こう。
あなたが nslookup api.example.com 8.8.8.8 と叩いた瞬間、以下のようなシーケンスがバックグラウンドで実行されている。
[Client (nslookup)] -- (UDP/TCP Port 53: DNS Query) --> [Public DNS (8.8.8.8)]
[Client (nslookup)] <-- (UDP/TCP Port 53: DNS Response) -- [Public DNS (8.8.8.8)]
この時、DNSサーバーに対して「どのレコードタイプが欲しいのか」を明示的に指定するのが基本だ。
各種レコードの照会パターン
実務で頻繁に叩くコマンドの基本形を以下にまとめる。コードブロック内のコメントを参考に、用途を身体に覚え込ませてほしい。
# 1. デフォルト(AレコードおよびAAAAレコード)の取得
nslookup api.example.com
# 2. 指定したフルリゾルバ(ここではGoogle Public DNS: 8.8.8.8)に対して直接問い合わせる
nslookup api.example.com 8.8.8.8
# 3. IPv4アドレスを引くための「Aレコード」を明示的に指定
nslookup -type=A api.example.com 8.8.8.8
# 4. IPv6アドレスを引くための「AAAAレコード」を指定(IPv6対応状況の確認に必須)
nslookup -type=AAAA api.example.com 8.8.8.8
# 5. メール配信サーバーを特定するための「MXレコード」を指定
nslookup -type=MX example.com 1.1.1.1
# 6. エイリアス(CNAME)の紐づきを確認する
nslookup -type=CNAME shop.example.com 8.8.8.8
# 7. 権威DNSサーバー(SOAやNSレコード)の情報を引く
nslookup -type=NS example.com
ここで -type= (または -q=) オプションを使いこなせるかどうかが、プロとアマの分かれ道だ。特にマイクロサービスの移行期には、CNAME がどこを指していて、最終的にどの A レコードに着地するのかを追うために、この切り替えが頻繁に必要になる。
—
3. 現場で重宝する「対話型モード」の真価
単発のコマンド実行も便利だが、同じDNSサーバーに対して何度も異なるレコードタイプや別ドメインを問い合わせる必要がある場合、毎回 nslookup を呼び出すのは非効率だ。
ここで真価を発揮するのが 「対話型モード (Interactive Mode)」 である。
引数なしで単に nslookup と叩くだけで、プロンプトが > に変わり、セッションが維持される。
対話型セッションの実行例
$ nslookup
> server 8.8.8.8 # クエリを投げるDNSサーバーをGoogleに変更
Default server: 8.8.8.8
Address: 8.8.8.8#53
> set type=MX # 以降のクエリタイプをMX(メール)に固定
> example.com # example.comのMXレコードを問い合わせ
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
example.com mail exchanger = 10 mail.example.com.
> set type=A # クエリタイプをAレコードに変更
> mail.example.com # 先ほど見つけたメールサーバーのIPを引く
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: example.com
Address: 192.0.2.50
> exit # 終了
この対話型モードの何が素晴らしいかといえば、「サーバーのスイッチングコストがゼロ」になる点だ。社内DNS (10.0.0.10) で名前解決できない社外ドメインを、パブリックDNS (1.1.1.1) に瞬時に切り替えて比較検証するといった泥臭いデバッグ作業において、このテンポの良さは何物にも代えがたい。
—
4. トラブルシューティングの現場から:ありがちな罠
NOCの現場で若手がよくハマる「DNSトラブルの罠」をいくつか共有しよう。これを読んでいるあなたには、同じハマり方を避けてほしい。
罠1: 「キャッシュ」という名の幻影
アプリケーションから curl や Python の requests で接続テストを行うと失敗するのに、手元の nslookup では一発で正しく名前解決できてしまう現象がある。
これは、OSやアプリケーション内部のDNSキャッシュが古いIPアドレスを保持し続けていることが原因だ。nslookup はOSのキャッシュをバイパスしてサーバーに直接聞きに行くため、「正しく解決できている」と誤認しやすい。
キャッシュの生存期間(TTL)や、アプリケーション層(JVMやPythonのurllib等)のコネクションプーリング、DNSキャッシュ設定を疑う必要がある。
罠2: 権威サーバー(Authoritative)とキャッシュサーバー(Non-authoritative)の混同
返ってきたレスポンスに Non-authoritative answer:(権威のない回答)と表示されることがある。これは、問い合わせたDNSサーバー(キャッシュDNS)が、ドメインの管理元(権威DNS)から直接データを引いたのではなく、自分が持っているキャッシュを返した状態を指している。
もしDNSレコードを書き換えた直後であれば、世界中のキャッシュDNSに伝搬(フルリゾルバのTTL切れ)するまで、この Non-authoritative answer が古い情報を返し続けることになる。「レコードを変えたのに反映されない!」と嘆く前に、ドメインの権威DNSサーバー(nslookup -type=NS で調べたサーバー)に対して直接クエリを投げ、そちら側の値が正しく更新されているかを確認するのがプロの定石だ。
—
5. 実務応用:スクリプトやCI/CDパイプラインへの組み込み
インフラの自動化やヘルスチェックにおいて、単に人間がコマンドを叩くだけでなく、スクリプトからDNSの状態を監視したいケースもある。
例えば、Pythonを使って特定のドメインが意図したIPを返すか検証するスニペットを置いておく。
import subprocess
import sys
def check_dns_a_record(domain, expected_ip, nameserver="8.8.8.8"):
"""
nslookupを使用して指定ドメインのAレコードが期待するIPと一致するか検証する
"""
try:
# nslookupコマンドを安全に構築して実行
cmd = ["nslookup", "-type=A", domain, nameserver]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
# 出力結果からIPアドレスの行をパース
output = result.stdout
if expected_ip in output:
print(f"[SUCCESS] {domain} resolved to {expected_ip} via {nameserver}")
return True
else:
print(f"[ERROR] {domain} did not resolve to {expected_ip}. Output:\n{output}")
return False
except subprocess.CalledProcessError as e:
print(f"[FATAL] Failed to execute nslookup: {e}", file=sys.stderr)
return False
# 実行例(本番移行前のロードバランサーIP検証など)
if __name__ == "__main__":
TARGET_DOMAIN = "api.example.com"
EXPECTED_IP = "198.51.100.25"
is_ok = check_dns_a_record(TARGET_DOMAIN, EXPECTED_IP)
sys.exit(0 if is_ok else 1)
このようなスクリプトをデプロイパイプラインの終盤に組み込んでおけば、「DNSの切り替え忘れ」や「伝搬遅延によるブラックホール化」をデプロイ前に自動で検知し、未然に障害を防ぐことができる。
—
最後に
ネットワークのトラブルシューティングにおいて、派手なツールや最新のフレームワークが役に立つとは限らない。むしろ、今回紹介した nslookup のような枯れたコマンドの挙動を深く理解し、パケットの流れを頭の中で正確にトレースできる能力こそが、過酷な障害現場で最も頼りになる武器となる。
次にアラートが鳴り響いたとき、慌ててコードをいじる前に、まずターミナルを開いて静かにこうつぶやいてほしい。
*「おい、DNSは本当に何を指している?」*
その一手間が、システムを、そしてあなたの夜の平穏を守るはずだ。
コメント