
nslookupとdigとは、DNSサーバーに問い合わせを送り、返ってきた応答を表示するコマンドです。調べる名前は/etc/hostsやnsswitchの設定を通らないので、わかるのは「DNSサーバーが何と答えたか」であって、「アプリが実際にどのアドレスへ接続するか」ではありません。Ubuntu 26.04では、どちらもBIND 9に付属するクライアントとしてbind9-dnsutilsパッケージに入っています。
対象は、「社内の名前が引けない」「DNSを切り替えたのに古いサーバーへ行く」という問い合わせを受けて原因を探す情シス・インフラ担当の方です。Dockerのubuntu:26.04コンテナ3つで権威サーバー(BIND 9.20.24)、キャッシュサーバー(Unbound 1.24.2)、クライアントを組み、同じ問い合わせをnslookupとdigの両方で実行しました。ラボの外へは問い合わせを出さない構成なので、同じ手順を踏めば手元でも同じ応答が返ります(ID・時刻・TTLの残り秒数は実行のたびに変わります)。読み終えると、出力のどの行を見れば「キャッシュに残った古い答え」「名前が存在しない」「サーバーが止まっている」を見分けられるかと、どちらのコマンドを先に打つかの判断基準が手元に残ります。
この記事でわかること
- nslookupの「Non-authoritative answer」(権限のない回答)とdigの
aaフラグが何を表すかと、権威サーバーに直接聞いて確かめる手順 nslookup 名前 サーバーとdig @サーバーでの問い合わせ先の指定、MX・TXT・SOA・CNAMEの引き方、PTRレコードによる逆引き(IPv6を含む)- キャッシュの中でTTLが減っていく様子と、レコードを変えた後もTTLが切れるまで古いIPアドレスが返る実例、
unbound-controlでの消し方 - NXDOMAIN・NODATA・REFUSED・SERVFAIL・タイムアウトなどで、2つのコマンドがそれぞれ出すメッセージと終了コードの一覧
- nslookupとdigが調べる名前を
/etc/hostsで引かないことの確認と、getent・pingとの使い分けを含めた「最初に打つコマンド」の判断表
nslookupとdigが確かめているもの
2つのコマンドがすることは同じで、DNSの問い合わせを組み立ててサーバーへ送り、届いた応答を表示します。問い合わせ先を指定しなければ、/etc/resolv.confのnameserver行にあるサーバーへ送ります。違うのは表示の仕方です。digは応答のヘッダー(応答コードとフラグ)と各セクションをほぼそのまま並べます。nslookupは結果を「Name:」「Address:」の形に要約し、TTLは-debugを付けないと表示しません。要約された分だけ、nslookupではNOERRORのような応答コードそのものや、キャッシュの残り時間が見えなくなります。
Windowsに入っているnslookupは、Microsoftが実装している別のプログラムです。Microsoft Learnのnslookupのページ(執筆時点)では、nslookup 名前 サーバーの形で1回だけ問い合わせる非対話モード、nslookupだけ(またはnslookup -)で入る対話モード、-type=によるレコードタイプの指定が説明されていて、書き方の基本はLinux版と同じです。ただし表示の文言は実装ごとに違います。この記事ではWindowsのnslookupを実行していないので、載せている出力はすべてLinux(BIND 9.20.24付属のnslookup)のものです。
検証用のDNSラボを作る
ラボは、Dockerのユーザー定義ネットワーク(198.51.100.0/24)につないだ3台です。クライアントの/etc/resolv.confはキャッシュサーバーを指し、キャッシュサーバーはlab.exampleと2つの逆引きゾーンだけを権威サーバーへ問い合わせます。会社のネットワークに置き換えると、PCが社内のキャッシュDNSを使い、その先に社内ゾーンを持つDNSサーバーがある形です。
| 役割 | コンテナ名 | アドレス | 中身 |
|---|---|---|---|
| 権威サーバー | nslookup-dig-command-dns-auth | 198.51.100.53 | BIND 9.20.24。lab.example、2.0.192.in-addr.arpa、8.b.d.0.1.0.0.2.ip6.arpaの3ゾーン |
| キャッシュサーバー | nslookup-dig-command-dns-cache | 198.51.100.10 | Unbound 1.24.2。上の3ゾーンだけを権威サーバーへ問い合わせる |
| クライアント | nslookup-dig-command-dns-client(ホスト名 nagoyadevlab01) | 198.51.100.100 | bind9-dnsutils 9.20.24(dig・nslookup)、iputils-ping |
ゾーン名lab.example、ホスト名nagoyadevlab01とnagoyadevlab02、作業ユーザーryoma、nslookup-dig-command-dns-で始まるコンテナ名とネットワーク名、記事中のIPアドレスは、どれも検証用に付けた例の名前と値です。自分の環境では、自分のゾーン名・ホスト名・アドレスに読み替えてください。.exampleは説明用に予約されたトップレベルドメイン(RFC 2606)、192.0.2.0/24と198.51.100.0/24は文書用のIPv4アドレス(RFC 5737)、2001:db8::/32は文書用のIPv6アドレス(RFC 3849)なので、インターネット上の実在の名前やアドレスとは重なりません。
設定ファイルを用意する
Dockerホストに作業ディレクトリ(例では~/dnslab)を作り、次の6ファイルを置きます。3台のコンテナはこのディレクトリを/labに読み取り専用でマウントし、各サーバーへはcpで配ります。まず権威サーバーの全体設定です。
options {
directory "/var/cache/bind";
recursion no;
allow-transfer { none; };
listen-on { any; };
listen-on-v6 { none; };
};recursion no;:自分が持つゾーンにだけ答え、他の名前の再帰検索は引き受けません。後半でゾーン外の名前を直接聞くとREFUSEDが返るのは、この行のためです。社内ゾーンの権威サーバーにキャッシュサーバーを兼ねさせないことで、外部から使える再帰サーバー(オープンリゾルバー)になる事故も防げます。allow-transfer { none; };:ゾーン転送(AXFR)を誰にも許しません。後でdig AXFRを試すと; Transfer failed.になります。転送が開いていると、ゾーンの全レコードを誰でも一覧できてしまいます。listen-on { any; };とlisten-on-v6 { none; };:このDockerネットワークにはIPv4しかないので、IPv6では待ち受けません。
ゾーンの定義です。逆引きゾーンの名前は、アドレスの並びを逆にして専用のドメインの下に置いたものです。192.0.2.0/24なら3オクテットを逆順にした2.0.192.in-addr.arpa、2001:db8::/32なら先頭32ビットを16進1桁ずつ逆順にした8.b.d.0.1.0.0.2.ip6.arpaになります。type primaryは、このサーバーがゾーンの元データを持つという指定です。
zone "lab.example" {
type primary;
file "/etc/bind/db.lab.example";
};
zone "2.0.192.in-addr.arpa" {
type primary;
file "/etc/bind/db.192.0.2";
};
zone "8.b.d.0.1.0.0.2.ip6.arpa" {
type primary;
file "/etc/bind/db.2001-db8";
};正引きゾーンのデータです。実験に使う値を、あらかじめ仕込んであります。
$TTL 3600
@ IN SOA ns1.lab.example. hostmaster.lab.example. (
2026110101 ; serial
3600 ; refresh
900 ; retry
604800 ; expire
300 ) ; negative TTL
IN NS ns1.lab.example.
IN MX 10 mail.lab.example.
IN TXT "v=spf1 ip4:192.0.2.25 -all"
ns1 IN A 198.51.100.53
www IN A 192.0.2.10
www IN AAAA 2001:db8::10
mail IN A 192.0.2.25
nagoyadevlab02 IN A 192.0.2.12
files IN CNAME nagoyadevlab02
app 60 IN A 192.0.2.20$TTL 3600:TTLを書いていないレコードは、キャッシュサーバーに3600秒保存されます。- SOAの最後の
300:否定応答(「その名前はない」などの答え)のキャッシュ時間に使われる値です。RFC 2308では、否定応答をキャッシュしてよい時間を、この値とSOAレコード自体のTTL(ここでは3600)の小さいほうと決めているので、このゾーンでは300秒になります。つまずきポイントの「追加したのに引けない」で効いてきます。 app 60 IN A:キャッシュの実験用に、TTLを60秒にしたレコードです。逆引き(PTR)は、わざと作っていません。TXT "v=spf1 ip4:192.0.2.25 -all":このドメインのメールは192.0.2.25からだけ送られる、と宣言するSPFレコードの例です。filesはnagoyadevlab02への別名(CNAME)です。
逆引きゾーンは、IPv4とIPv6で1ファイルずつです。IPv6のPTRの名前は、ゾーン名に含まれない残りの24桁(2001:db8::10の下位96ビット)を逆順に並べたものです。
$TTL 3600
@ IN SOA ns1.lab.example. hostmaster.lab.example. ( 2026110101 3600 900 604800 300 )
IN NS ns1.lab.example.
10 IN PTR www.lab.example.
12 IN PTR nagoyadevlab02.lab.example.
25 IN PTR mail.lab.example.$TTL 3600
@ IN SOA ns1.lab.example. hostmaster.lab.example. ( 2026110101 3600 900 604800 300 )
IN NS ns1.lab.example.
0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR www.lab.example.キャッシュサーバーの設定です。Ubuntuの既定のunbound.confは丸ごと置き換えます。
server:
interface: 0.0.0.0
access-control: 198.51.100.0/24 allow
do-ip6: no
local-zone: "." static
local-zone: "lab.example." transparent
local-zone: "2.0.192.in-addr.arpa." transparent
local-zone: "8.b.d.0.1.0.0.2.ip6.arpa." transparent
stub-zone:
name: "lab.example."
stub-addr: 198.51.100.53
stub-zone:
name: "2.0.192.in-addr.arpa."
stub-addr: 198.51.100.53
stub-zone:
name: "8.b.d.0.1.0.0.2.ip6.arpa."
stub-addr: 198.51.100.53
remote-control:
control-enable: yes
control-interface: /run/unbound.ctlinterfaceとaccess-control:Unboundは既定ではlocalhostからの問い合わせしか受け付けないので、ラボのネットワーク198.51.100.0/24を許可します。local-zone: "." static:ラボを閉じるための行です。下で例外にした3ゾーン以外の名前は、インターネットのルートサーバーへ聞きに行かず、Unbound自身がNXDOMAINで答えます。社内の本番キャッシュサーバーには書きません。local-zone: … transparentの3行:"." staticの例外として、この3ゾーンは通常どおり問い合わせます。2.0.192.in-addr.arpaと8.b.d.0.1.0.0.2.ip6.arpaは、Unboundが最初から自分で答える既定のローカルゾーンにも入っているので、この行がないと逆引きが引けません(つまずきポイントで再現します)。stub-zone:3ゾーンの問い合わせを、権威サーバー198.51.100.53へ直接送ります。remote-controlは、キャッシュを消すunbound-controlをUNIXソケット(/run/unbound.ctl)経由で使うための設定です。
インターネットへ再帰検索させる普通のキャッシュサーバーとしての作り方は、Unboundで自宅用キャッシュDNSサーバーを構築する記事にまとめています。
コンテナを起動する
作業ディレクトリでネットワークと3台のコンテナを作ります。アドレスを--ipで固定しているのは、ゾーンファイルと出力に書いたアドレスと揃えるためです。ポートはどれもホストに公開しません(-pなし)。--initは、後半でnamedを止めて起動し直すときに、終了したプロセスを回収させるためです。docker runはコンテナIDを1行表示しますが、ここでは省きました。
docker network create --subnet 198.51.100.0/24 nslookup-dig-command-dns-net
docker run -d --init --name nslookup-dig-command-dns-auth --network nslookup-dig-command-dns-net --ip 198.51.100.53 --memory 256m -v "$PWD":/lab:ro ubuntu:26.04 sleep infinity
docker run -d --init --name nslookup-dig-command-dns-cache --network nslookup-dig-command-dns-net --ip 198.51.100.10 --memory 256m -v "$PWD":/lab:ro ubuntu:26.04 sleep infinity
docker run -d --init --name nslookup-dig-command-dns-client --hostname nagoyadevlab01 --network nslookup-dig-command-dns-net --ip 198.51.100.100 --memory 256m -v "$PWD":/lab:ro ubuntu:26.04 sleep infinity
docker ps --filter name=nslookup-dig-command-dns --format '{{.Names}}'
nslookup-dig-command-dns-client
nslookup-dig-command-dns-cache
nslookup-dig-command-dns-auth198.51.100.0/24と重なる範囲のDockerネットワークがすでにあると、docker network createはError response from daemon: invalid pool request: Pool overlaps with other one on this address spaceで失敗しました。その場合は別の範囲を使い、ゾーンファイルとunbound.confの中の198.51.100.xも同じように書き換えます。
権威サーバー(BIND 9)を起動する
ホストからdocker exec -it nslookup-dig-command-dns-auth bashで権威サーバーに入り、rootのまま実行します。コンテナにはsystemdがないので、namedは直接起動します。named-checkconf -zは設定とゾーンファイルを読んで、各ゾーンのserial(版番号)を表示します。ここでエラーが出たら、namedを起動する前に直します。
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bind9 >/dev/null 2>&1; echo "exit=$?"
exit=0
named -v
BIND 9.20.24-1ubuntu0.4-Ubuntu (Stable Release) <id:>
cp /lab/named.conf.options /lab/named.conf.local /lab/db.lab.example /lab/db.192.0.2 /lab/db.2001-db8 /etc/bind/
named-checkconf -z
zone lab.example/IN: loaded serial 2026110101
zone 2.0.192.in-addr.arpa/IN: loaded serial 2026110101
zone 8.b.d.0.1.0.0.2.ip6.arpa/IN: loaded serial 2026110101
named -u bindキャッシュサーバー(Unbound)を起動する
docker exec -it nslookup-dig-command-dns-cache bashで入ります。置き換える前の既定の設定を見ると、unbound.conf.d/の読み込み、remote-control、ルートのトラストアンカー(DNSSEC検証)が入っています。置き換え後の設定はトラストアンカーを読まないので、このラボではDNSSECの検証をしません(ラボのゾーンには署名していません)。
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unbound >/dev/null 2>&1; echo "exit=$?"
exit=0
unbound -V | head -n 1
Version 1.24.2
grep -h -v -e '^ *#' -e '^$' /etc/unbound/unbound.conf /etc/unbound/unbound.conf.d/*.conf
include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"
remote-control:
control-enable: yes
control-interface: /run/unbound.ctl
server:
auto-trust-anchor-file: "/var/lib/unbound/root.key"
cp /lab/unbound.conf /etc/unbound/unbound.conf
unbound-checkconf
unbound-checkconf: no errors in /etc/unbound/unbound.conf
unbound
unbound-control list_stubs
8.b.d.0.1.0.0.2.ip6.arpa. IN stub noprime 198.51.100.53
2.0.192.in-addr.arpa. IN stub noprime 198.51.100.53
lab.example. IN stub noprime 198.51.100.53list_stubsの出力には、この3行の前にルートサーバーの一覧の行も出ます。"." staticがあるので、ラボの外の名前の問い合わせはその一覧には届かず、Unboundが自分で答えます。
クライアントを準備する
docker exec -it nslookup-dig-command-dns-client bashでrootとして入り、ツールを入れて/etc/resolv.confをキャッシュサーバーに向けます。search lab.exampleは、ドットを含まない短い名前にlab.exampleを補う設定で、つまずきポイントで使います。useraddのユーザー名も例なので、自分で決めた作業用の名前にしてください。
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bind9-dnsutils iputils-ping >/dev/null 2>&1; echo "exit=$?"
exit=0
dpkg-query -W bind9-dnsutils iputils-ping
bind9-dnsutils 1:9.20.24-1ubuntu0.4
iputils-ping 3:20250605-1ubuntu1
dpkg -L bind9-dnsutils | grep bin/
/usr/bin/delv
/usr/bin/dig
/usr/bin/mdig
/usr/bin/nslookup
/usr/bin/nsupdate
printf 'nameserver 198.51.100.10\nsearch lab.example\n' > /etc/resolv.conf
useradd -m -s /bin/bash ryomaユーザー定義ネットワークにつないだDockerコンテナの/etc/resolv.confは、最初はnameserver 127.0.0.11(Dockerの内蔵DNS)です。内蔵DNSはコンテナ名以外の問い合わせをDockerホストのDNS設定へ転送します。書き換える前にnslookup www.lab.exampleを実行すると、Server: 127.0.0.11と表示されたうえでNXDOMAINになりました。ラボの名前を引く前に、必ず書き換えてください。
ここからは作業ユーザーで入り直して実行します(docker exec -it -u ryoma -w /home/ryoma nslookup-dig-command-dns-client bash。プロンプトはryoma@nagoyadevlab01:~$になります)。記事の出力は、端末を割り当てないdocker execで記録したものです。
nslookupコマンドとdigコマンドの出力を読む
まず、問い合わせ先と版を確認してから、同じ名前を2つのコマンドで引きます。
cat /etc/resolv.conf
nameserver 198.51.100.10
search lab.example
dig -v
DiG 9.20.24-1ubuntu0.4-Ubuntu
nslookup www.lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
Name: www.lab.example
Address: 192.0.2.10
Name: www.lab.example
Address: 2001:db8::10Server:とAddress:は問い合わせたサーバーで、#53はポート番号です。Non-authoritative answer:は次の節で扱います。Name:とAddress:の組が2つあるのは、nslookupが名前だけ渡されるとAとAAAAの2種類を問い合わせるからです。-debugを付けると、送った質問が2つあることがわかります。
nslookup -debug www.lab.example | grep -A1 QUESTIONS
QUESTIONS:
www.lab.example, type = A, class = IN
--
QUESTIONS:
www.lab.example, type = AAAA, class = INdigは、引数に名前だけ渡すとAレコードを1回問い合わせ、応答の中身を並べます。
dig www.lab.example
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> www.lab.example
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58136
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www.lab.example. IN A
;; ANSWER SECTION:
www.lab.example. 3600 IN A 192.0.2.10
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:01:52 UTC 2026
;; MSG SIZE rcvd: 60status: NOERROR:応答コードです。名前がなければNXDOMAIN、サーバーが答えを得られなければSERVFAILになります(後の節で全部出します)。flags: qr rd ra:qrは応答であること、rdは「再帰検索をしてほしい」という依頼(digが既定で立てます)、raはサーバーが再帰検索に対応していることを示します。aaがないので、この答えはキャッシュから来ています。QUERY / ANSWER / AUTHORITY / ADDITIONAL:各セクションのレコード数です。ADDITIONALの1は、EDNSの情報(OPT PSEUDOSECTION)の分です。- ANSWERの
3600:この答えがキャッシュにあと何秒残るか(TTL)です。直前のnslookupの問い合わせでキャッシュに入ったばかりなので、ゾーンに書いたTTL(3600)とほぼ同じ値で、時間がたつと減っていきます。続けて引いたAAAAでは3599でした。 SERVER:行:実際に答えたサーバーとポート、使ったプロトコル(UDP)です。/etc/resolv.confに複数のサーバーが並んでいるときも、この行でどれが答えたかがわかります。
毎回この量を読む必要はありません。値だけなら+short、TTL付きの答えの行だけなら+noall +answerです。調査では、TTLが見える+noall +answerをおすすめします。+shortはスクリプト向きですが、TTLも応答コードも消えるので、何も表示されなかったときに理由がわかりません。
dig +short www.lab.example
192.0.2.10
dig +noall +answer www.lab.example AAAA
www.lab.example. 3599 IN AAAA 2001:db8::10「Non-authoritative answer」(権限のない回答)とaaフラグ
どのサーバーがゾーンの権威(ゾーンの元データを持つサーバー)かは、NSレコードでわかります。NSに書かれた名前のアドレスを引けば、直接問い合わせる相手が決まります。
dig +short NS lab.example
ns1.lab.example.
dig +short ns1.lab.example
198.51.100.53
nslookup www.lab.example 198.51.100.53
Server: 198.51.100.53
Address: 198.51.100.53#53
Name: www.lab.example
Address: 192.0.2.10
Name: www.lab.example
Address: 2001:db8::10
dig @198.51.100.53 www.lab.example
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> @198.51.100.53 www.lab.example
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45783
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: fe32968a74a9f729010000006ae681721093657ae0e3c780 (good)
;; QUESTION SECTION:
;www.lab.example. IN A
;; ANSWER SECTION:
www.lab.example. 3600 IN A 192.0.2.10
;; Query time: 0 msec
;; SERVER: 198.51.100.53#53(198.51.100.53) (UDP)
;; WHEN: Sun Nov 01 00:01:54 UTC 2026
;; MSG SIZE rcvd: 88権威サーバーに直接聞くと、nslookupのNon-authoritative answer:の行が消え、digのflags:にaaが付きました。aaはDNSヘッダーのAuthoritative Answerビットで、RFC 1035では「答えたサーバーが、質問された名前の権威である」ことを示すビットと定められています。キャッシュサーバーは、権威サーバーからaa付きで受け取った答えを保存し、クライアントには自分のキャッシュからaaなしで返します。このラボでは、aaのない応答にnslookupがNon-authoritative answer:を付け、aaのある応答では付けませんでした。TTLも、権威サーバーはゾーンに書いた3600をそのまま返し、キャッシュ経由では時間がたつほど減った値になります(キャッシュとTTLの節で確かめます)。
WARNING: recursion requested but not availableは、digがrdを立てて送ったのに、権威サーバーがrecursion noなのでraを返さなかった、という意味です。答えそのものには影響しません。+norecでrdを外して聞くと、警告が消え、flags:はqr aaだけになります。
dig @198.51.100.53 www.lab.example +norec +noall +comments +answer
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53160
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: c7176e3e671f2680010000006ae681721afa3eaedcf52196 (good)
;; ANSWER SECTION:
www.lab.example. 3600 IN A 192.0.2.10クライアントがキャッシュサーバーを使う構成では、キャッシュ経由の答えにNon-authoritative answer:が付くのが正常で、エラーではありません。確かめるべきなのは「キャッシュの答えと権威サーバーの答えが同じか」です。レコードを変更したあとは、dig 名前とdig @権威サーバー 名前 +norecを並べ、値とTTLを比べるのを手順にしておくと迷いません。
aaは「正しい答え」の印ではありません。このラボのUnboundは、local-zoneの設定で自分で答えたNXDOMAINにもaaを付けました(つまずきポイントのdig wwwの出力)。aaを見たら、SERVER:行でどのサーバーが答えたか、AUTHORITYのSOAでどのゾーンの答えかも合わせて確かめます。
Windowsの日本語表示のnslookupで「権限のない回答」と出たときも、確かめ方は同じです。同じ名前をnslookup 名前 権威サーバーで引き直し、その表示が消えるか、アドレスが一致するかを見ます。この環境ではWindowsのnslookupを実行していないため、日本語の画面そのものは確認していません。
問い合わせるDNSサーバーを指定する(nslookupのサーバー指定とdig @)
nslookupは2つ目の引数、digは@付きの引数が問い合わせ先です。digの@サーバーは、名前の前にもオプションのあとにも書けます(この記事では両方の形を使っています)。サーバーは名前でも指定できます。
| やりたいこと | nslookup | dig |
|---|---|---|
| resolv.confのサーバーに聞く | nslookup 名前 | dig 名前 |
| サーバーを指定する | nslookup 名前 サーバー | dig @サーバー 名前 |
| 権威サーバーにキャッシュなしで聞く | この記事では試していません | dig @サーバー 名前 +norec |
| TCPで聞く | この記事では試していません | dig +tcp @サーバー 名前 |
| 同じサーバーに続けて何件も聞く | 対話モードのserverとset type= | 引数を変えて繰り返す |
dig @ns1.lab.example www.lab.example +norec +short
192.0.2.10
printf 'server 198.51.100.53\nset type=mx\nlab.example\nexit\n' | nslookup
Default server: 198.51.100.53
Address: 198.51.100.53#53
Server: 198.51.100.53
Address: 198.51.100.53#53
lab.example mail exchanger = 10 mail.lab.example.
dig +tcp @198.51.100.53 www.lab.example | grep SERVER
;; SERVER: 198.51.100.53#53(198.51.100.53) (TCP)nslookupの対話モードは、serverで問い合わせ先を、set type=mxでレコードタイプを切り替えてから名前を入力します。ここではコマンドをパイプで渡したので、対話モードの>のプロンプトは表示されていません。+tcpの結果はSERVER:行の末尾が(TCP)になります。UDPの53番だけが途中のファイアウォールで止められている疑いがあるときは、UDPとTCPの両方で聞き比べると切り分けられます。
ゾーン転送も試しておきます。allow-transfer { none; };にしてあるので、権威サーバーは全レコードの一覧を渡しません。自分の権威サーバーで同じコマンドを打ってレコードが並んだら、転送の制限を見直してください。
dig @198.51.100.53 lab.example AXFR
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> @198.51.100.53 lab.example AXFR
; (1 server found)
;; global options: +cmd
; Transfer failed.レコードタイプを指定して引く(MX・TXT・SOA・CNAME)
nslookupは-type=、digは名前のあとにタイプ名を書きます。何も指定しないときの動きは違い、nslookupはAとAAAA、digはAだけを問い合わせます。
| 調べたいもの | タイプ | nslookup | dig |
|---|---|---|---|
| IPv4アドレス | A | nslookup -type=a 名前 | dig 名前 |
| IPv6アドレス | AAAA | nslookup -type=aaaa 名前 | dig 名前 AAAA |
| メールの配送先 | MX | nslookup -type=mx ドメイン | dig ドメイン MX |
| SPFなどの文字列 | TXT | nslookup -type=txt ドメイン | dig ドメイン TXT |
| ゾーンの管理情報(serialなど) | SOA | nslookup -type=soa ドメイン | dig ドメイン SOA |
| 権威サーバーの名前 | NS | nslookup -type=ns ドメイン | dig ドメイン NS |
| IPアドレスからの名前(逆引き) | PTR | nslookup IPアドレス | dig -x IPアドレス |
dig +noall +answer lab.example MX
lab.example. 3600 IN MX 10 mail.lab.example.
nslookup -type=mx lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
lab.example mail exchanger = 10 mail.lab.example.
Authoritative answers can be found from:
dig lab.example MX | grep flags
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
; EDNS: version: 0, flags:; udp: 1232MX・TXT・SOAをキャッシュ経由で引くと、nslookupは最後にAuthoritative answers can be found from:という見出しを出しました。続きが空なのは、キャッシュサーバーの応答にAUTHORITYセクションも追加のアドレス情報も入っていない(AUTHORITY: 0、ADDITIONAL: 1はEDNSの分だけ)からで、異常ではありません。nslookup -type=ns lab.exampleで引いたときは、この見出しの下にns1.lab.example internet address = 198.51.100.53が出ました。
dig +short lab.example TXT
"v=spf1 ip4:192.0.2.25 -all"
nslookup -type=txt lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
lab.example text = "v=spf1 ip4:192.0.2.25 -all"
Authoritative answers can be found from:
nslookup -type=soa lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
lab.example
origin = ns1.lab.example
mail addr = hostmaster.lab.example
serial = 2026110101
refresh = 3600
retry = 900
expire = 604800
minimum = 300
Authoritative answers can be found from:SOAの各値は、ゾーンファイルのSOAの括弧の中と1対1で対応しています。serial = 2026110101はゾーンを変えるたびに増やす版番号で、キャッシュの答えと権威サーバーの答えでserialが違えば、キャッシュに古い版が残っていると判断できます。この問い合わせでSOAもキャッシュに入ったので、後の節で使います。CNAMEは、別名と行き先のAレコードがまとめて返ります。
dig +noall +answer files.lab.example
files.lab.example. 3600 IN CNAME nagoyadevlab02.lab.example.
nagoyadevlab02.lab.example. 3600 IN A 192.0.2.12
nslookup files.lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
files.lab.example canonical name = nagoyadevlab02.lab.example.
Name: nagoyadevlab02.lab.example
Address: 192.0.2.12nslookupとdigで逆引きする(PTRレコード)
逆引きは、IPアドレスから名前を調べる問い合わせで、実体はin-addr.arpaの下の名前に対するPTRレコードの検索です。192.0.2.12なら、オクテットを逆順にした12.2.0.192.in-addr.arpaのPTRを引きます。dig -xとnslookupはこの名前を自動で組み立てます。
dig -x 192.0.2.12 +noall +answer
12.2.0.192.in-addr.arpa. 3600 IN PTR nagoyadevlab02.lab.example.
nslookup 192.0.2.12
12.2.0.192.in-addr.arpa name = nagoyadevlab02.lab.example.
Authoritative answers can be found from:
nslookup -type=ptr 12.2.0.192.in-addr.arpa
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
12.2.0.192.in-addr.arpa name = nagoyadevlab02.lab.example.
Authoritative answers can be found from:BIND 9.20.24のnslookupは、IPアドレスを渡したときにServer:とAddress:の行もNon-authoritative answer:の行も出しませんでした。どのサーバーが答えたかを残したいときは、-type=ptrでin-addr.arpaの名前を渡すか、dig -xを使います。IPv6では、アドレスを32桁の16進に展開して1桁ずつ逆順にしたip6.arpaの名前になります。手で組み立てるのは間違えやすいので、dig -xに任せるのが確実です。
dig -x 2001:db8::10 +noall +answer
0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. 3600 IN PTR www.lab.example.
nslookup 2001:db8::10
0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa name = www.lab.example.
Authoritative answers can be found from:Aレコードはあるのに逆引きのPTRがないapp(192.0.2.20)を引くと、NXDOMAINです。AUTHORITYのSOAが2.0.192.in-addr.arpaのものなので、逆引きゾーン側に名前がないことがわかります。正引きと逆引きは別のゾーンで、このラボのBINDではAレコードを足してもPTRは増えません(後で追加するtest01も同じです)。サーバーを追加するときは、AとPTRを同じ変更の中で登録し、後の動作確認のスクリプトで両方を確かめるのがおすすめです。
dig -x 192.0.2.20
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> -x 192.0.2.20
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 25822
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;20.2.0.192.in-addr.arpa. IN PTR
;; AUTHORITY SECTION:
2.0.192.in-addr.arpa. 298 IN SOA ns1.lab.example. hostmaster.lab.example. 2026110101 3600 900 604800 300
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:01:59 UTC 2026
;; MSG SIZE rcvd: 114nslookup 192.0.2.20
** server can't find 20.2.0.192.in-addr.arpa: NXDOMAINキャッシュとTTL:レコードを変えても古いIPアドレスが返る理由
TTL 60秒のappを引き、15秒待ってもう一度引きます。キャッシュサーバーは、権威サーバーから受け取った時点からTTLを数え下げ、残り秒数をそのまま答えに載せます。
dig +noall +answer app.lab.example; sleep 15; dig +noall +answer app.lab.example
app.lab.example. 60 IN A 192.0.2.20
app.lab.example. 45 IN A 192.0.2.20この状態で、権威サーバー側のappを192.0.2.21に変えます。serialも1つ増やし、rndc reloadでゾーンを読み直させます。
sed -i -e 's/2026110101 ; serial/2026110102 ; serial/' -e 's/192.0.2.20$/192.0.2.21/' /etc/bind/db.lab.example
grep -n -e serial -e '^app' /etc/bind/db.lab.example
3: 2026110102 ; serial
17:app 60 IN A 192.0.2.21
rndc reload lab.example
zone reload queuedすぐにクライアントから、キャッシュ経由と権威サーバー直接の両方で引き比べます。
dig +noall +answer app.lab.example
app.lab.example. 44 IN A 192.0.2.20
dig @198.51.100.53 +norec +noall +answer app.lab.example
app.lab.example. 60 IN A 192.0.2.21
dig +short lab.example SOA; dig +short @198.51.100.53 +norec lab.example SOA
ns1.lab.example. hostmaster.lab.example. 2026110101 3600 900 604800 300
ns1.lab.example. hostmaster.lab.example. 2026110102 3600 900 604800 300キャッシュ経由では古い192.0.2.20が残りのTTL付きで返り、権威サーバーは新しい192.0.2.21をTTL 60で返しました。SOAのserialも、キャッシュは2026110101、権威サーバーは2026110102です。変更が権威サーバーに入ったかどうかはdig @権威 +norecで、利用者に届いたかどうかはキャッシュ経由の答えで確かめる、という分担になります。残りTTLの秒数だけ待つと、キャッシュサーバーは権威サーバーに聞き直します。
t=$(dig +noall +answer app.lab.example | awk '{print $2}'); echo "wait ${t}s"; sleep $((t+1)); dig +noall +answer app.lab.example
wait 43s
app.lab.example. 60 IN A 192.0.2.21TTLが3600秒のSOAのように長いものを待てないときは、キャッシュサーバーで消します。unbound-control flush 名前はその名前の主なタイプを、flush_zone ゾーンはその名前以下をすべて、flush_negativeは否定応答だけを消します。
unbound-control flush_zone lab.example
ok removed 12 rrsets, 11 messages and 0 key entriesdig +short lab.example SOA
ns1.lab.example. hostmaster.lab.example. 2026110102 3600 900 604800 300消せるのは自分が管理するキャッシュサーバーだけです。プロバイダーや別拠点のキャッシュ、利用者の端末が持つキャッシュは、TTLが切れるまで古い答えを返し続けます。サーバーの移行でアドレスを変えるなら、切り替えより元のTTL(この例なら3600秒)以上前にTTLを短く(例:300秒)しておき、切り替えが落ち着いたら戻す、という順番にします。Windowsの端末には独自のDNSクライアントキャッシュがあり、Microsoft Learnのipconfigのページ(執筆時点)では、中身の表示にipconfig /displaydns、消去にipconfig /flushdnsが説明されています。どちらのコマンドも、この環境では実行していません。
NXDOMAIN・NODATA・REFUSED・SERVFAIL・タイムアウトを見分ける
失敗の種類によって、次に見る場所が変わります。digは、応答が届けばNXDOMAINでもSERVFAILでも終了コード0を返し(このラボで試した4種類すべて)、失敗の種類はstatus:に出ます。nslookupはメッセージで知らせ、終了コードも種類によって変わりました。スクリプトで使うときに効いてくるので、各コマンドのあとにecho "exit=$?"で終了コードも表示しています。
NXDOMAINとNODATA
dig nosuch.lab.example; echo "exit=$?"
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 23072
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nosuch.lab.example. IN A
;; AUTHORITY SECTION:
lab.example. 300 IN SOA ns1.lab.example. hostmaster.lab.example. 2026110102 3600 900 604800 300
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:03:01 UTC 2026
;; MSG SIZE rcvd: 98
exit=0nslookup nosuch.lab.example; echo "exit=$?"
Server: 198.51.100.10
Address: 198.51.100.10#53
** server can't find nosuch.lab.example: NXDOMAIN
exit=1dig mail.lab.example AAAA; echo "exit=$?"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48577
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;mail.lab.example. IN AAAA
;; AUTHORITY SECTION:
lab.example. 300 IN SOA ns1.lab.example. hostmaster.lab.example. 2026110102 3600 900 604800 300
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:03:01 UTC 2026
;; MSG SIZE rcvd: 96
exit=0nslookup -type=aaaa mail.lab.example; echo "exit=$?"
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
*** Can't find mail.lab.example: No answer
exit=0NXDOMAINは、名前そのものがない(どのタイプでもない)という答えです。NODATAは応答コードではなく、名前はあるのに頼んだタイプのレコードがない状態を指す呼び名で(RFC 2308)、digではstatus: NOERRORなのにANSWER: 0として現れます。nslookupは前者を** server can't find …: NXDOMAIN、後者を*** Can't find …: No answerと表示し、終了コードは前者が1、後者が0でした。nslookupの終了コードだけで「引けたか」を判定するスクリプトは、NODATAを成功と見なします。どちらの答えにもAUTHORITYにSOAが付き、そのTTL(300)が否定応答をキャッシュする時間です。
REFUSED
dig @198.51.100.53 www.example.net; echo "exit=$?"
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 64414
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 64c93d912a14efdb010000006ae681b6b199994d14b9c6f3 (good)
; EDE: 20 (Not Authoritative): (recursion disabled)
;; QUESTION SECTION:
;www.example.net. IN A
;; Query time: 0 msec
;; SERVER: 198.51.100.53#53(198.51.100.53) (UDP)
;; WHEN: Sun Nov 01 00:03:02 UTC 2026
;; MSG SIZE rcvd: 96
exit=0nslookup www.example.net 198.51.100.53; echo "exit=$?"
Server: 198.51.100.53
Address: 198.51.100.53#53
** server can't find www.example.net: REFUSED
exit=1権威サーバーに、そのサーバーが持っていないゾーンの名前を聞いた結果です。recursion noなので代わりに調べることもせず、断りました。BIND 9.20.24はEDE: 20 (Not Authoritative): (recursion disabled)という拡張エラーで理由も返しています。社内でREFUSEDを見たら、問い合わせ先が権威専用のサーバーになっていないか、キャッシュサーバーのアクセス制御で自分のネットワークが許可されているかを確かめます。
SERVFAILとconnection refused
権威サーバーのnamedを止め、キャッシュサーバーのlab.exampleのキャッシュを消してから引きます。キャッシュサーバーは動いているのに、その先の権威サーバーが答えない状態です。
rndc stopunbound-control flush_zone lab.example
ok removed 3 rrsets, 3 messages and 0 key entriesdig mail.lab.example; echo "exit=$?"
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 40986
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;mail.lab.example. IN A
;; Query time: 1553 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:03:04 UTC 2026
;; MSG SIZE rcvd: 45
exit=0nslookup mail.lab.example; echo "exit=$?"
;; Got SERVFAIL reply from 198.51.100.10
Server: 198.51.100.10
Address: 198.51.100.10#53
** server can't find mail.lab.example: SERVFAIL
exit=1
dig @198.51.100.53 mail.lab.example; echo "exit=$?"
;; communications error to 198.51.100.53#53: connection refused
;; communications error to 198.51.100.53#53: connection refused
;; communications error to 198.51.100.53#53: connection refused
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> @198.51.100.53 mail.lab.example
; (1 server found)
;; global options: +cmd
;; no servers could be reached
exit=9
nslookup mail.lab.example 198.51.100.53; echo "exit=$?"
;; communications error to 198.51.100.53#53: connection refused
;; communications error to 198.51.100.53#53: connection refused
;; communications error to 198.51.100.53#53: connection refused
;; no servers could be reached
exit=1キャッシュサーバー経由ではSERVFAILが返り、digのQuery timeは1553 msecでした。これは、キャッシュサーバーが権威サーバーへの問い合わせを試してから諦めるまでの時間です。nslookupは;; Got SERVFAIL reply from 198.51.100.10を先に出しています。権威サーバーに直接聞くと、こちらはconnection refusedが3回並んでno servers could be reachedになり、digの終了コードは9でした。connection refusedは「そのアドレスの機械は動いているが、53番で待ち受けているプログラムがない」状態で、DNSサービスが落ちているときの典型です。SERVFAILを見たら、まず権威サーバーへ直接聞いて、どちら側の問題かを分けます。
namedを起動し直し、キャッシュサーバーでflush_zoneしてから確認します。この記事の検証では、flushしないまま起動直後に引くと答えが返らず、少し待ってから引き直すと返りました。直したはずなのに失敗が続くときは、キャッシュに残った失敗の答えを疑います。
named -u bindunbound-control flush_zone lab.example
ok removed 0 rrsets, 1 messages and 0 key entriesdig +short mail.lab.example
192.0.2.25タイムアウトとhost unreachable
応答が返ってこない状態は、キャッシュサーバーのunboundプロセスをSIGSTOPで一時停止して作りました。プロセスもソケットも残っているので、問い合わせはOSに受け取られますが、返事は来ません。高負荷で固まったサーバーや、パケットを黙って捨てるファイアウォールと同じ見え方です。digの既定は1回5秒待ちを3回です。
kill -STOP $(pgrep -x unbound)dig -h | grep -E '\+(timeout|tries)='
+timeout=### (Set query timeout) [5]
+tries=### (Set number of UDP attempts) [3]
dig www.lab.example; echo "exit=$?"
;; communications error to 198.51.100.10#53: timed out
;; communications error to 198.51.100.10#53: timed out
;; communications error to 198.51.100.10#53: timed out
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> www.lab.example
;; global options: +cmd
;; no servers could be reached
exit=9
nslookup -timeout=2 -retry=1 www.lab.example; echo "exit=$?"
;; communications error to 198.51.100.10#53: timed out
;; no servers could be reached
exit=1digはtimed outを3回表示して約15秒で終わり、終了コードは9でした。nslookupに-timeout=2 -retry=1を付けると、2秒待ちを1回だけで諦めます。障害対応中に何度も試すなら、digでも+timeout=2 +tries=1のように短くすると待ち時間が減ります。確認が終わったら、unboundを再開させます。
kill -CONT $(pgrep -x unbound)最後に、どの機械も使っていないアドレス(198.51.100.99)をDNSサーバーとして指定します。サーバーのIPアドレスを打ち間違えたときの状態です。
dig +short www.lab.example
192.0.2.10
dig +tries=1 @198.51.100.99 www.lab.example; echo "exit=$?"
;; communications error to 198.51.100.99#53: host unreachable
; <<>> DiG 9.20.24-1ubuntu0.4-Ubuntu <<>> +tries=1 @198.51.100.99 www.lab.example
; (1 server found)
;; global options: +cmd
;; no servers could be reached
exit=9
nslookup -retry=1 www.lab.example 198.51.100.99; echo "exit=$?"
;; communications error to 198.51.100.99#53: host unreachable
;; no servers could be reached
exit=1同じネットワーク上にそのアドレスの機械がいないこのラボでは、約3秒待ってhost unreachableになりました。timed outとは違い、「相手が見つからない」という知らせです。ここまでの結果を表にまとめます。
| 状態 | digの表示 | nslookupの表示 | 終了コード(dig / nslookup) | 次に確かめること |
|---|---|---|---|---|
| NXDOMAIN(名前がない) | status: NXDOMAIN、AUTHORITYにSOA | ** server can't find 名前: NXDOMAIN | 0 / 1 | 綴り、search、権威サーバーでも同じか、否定応答のキャッシュ |
| NODATA(そのタイプがない) | status: NOERROR、ANSWER: 0 | *** Can't find 名前: No answer | 0 / 0 | タイプの指定(AかAAAAか)、レコードの登録漏れ |
| REFUSED(断られた) | status: REFUSED、EDE 20 | ** server can't find 名前: REFUSED | 0 / 1 | 問い合わせ先が権威専用でないか、アクセス制御 |
| SERVFAIL(答えを得られない) | status: SERVFAIL | ;; Got SERVFAIL reply from …と** server can't find 名前: SERVFAIL | 0 / 1 | 権威サーバーへ直接聞く |
| サービス停止 | communications error to …: connection refused | 同じ | 9 / 1 | サーバーでDNSのプロセスが動いているか |
| 応答なし | communications error to …: timed out | 同じ | 9 / 1 | 経路のファイアウォール、サーバーの負荷 |
| 相手がいない | communications error to …: host unreachable | 同じ | 9 / 1 | 指定したIPアドレスの誤り |
Windowsのnslookupの失敗メッセージとして、Microsoft Learnのnslookupのページ(執筆時点)にはtimed out、No response from server、No records、Nonexistent domain、Server failure、Refusedなどが一覧で載っています。表の文言はLinuxで実行した結果で、Windowsでの表示はこの環境では確かめていません。
nslookupとdigは調べる名前を/etc/hostsで引かない
動作確認のつもりで/etc/hostsに書いた行が残っていると、「nslookupでは正しいのにアプリだけ古いサーバーへ行く」状態になります。クライアントの/etc/hostsに、www.lab.exampleを権威サーバーのアドレスに向ける行を足して比べます(rootで実行)。
echo '198.51.100.53 www.lab.example' >> /etc/hostsgrep ^hosts: /etc/nsswitch.conf
hosts: files dns
getent ahosts www.lab.example
198.51.100.53 STREAM www.lab.example
198.51.100.53 DGRAM
198.51.100.53 RAW
ping -c 1 www.lab.example
PING www.lab.example (198.51.100.53) 56(84) bytes of data.
64 bytes from www.lab.example (198.51.100.53): icmp_seq=1 ttl=64 time=0.046 ms
--- www.lab.example ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.046/0.046/0.046/0.000 ms
dig +short www.lab.example
192.0.2.10/etc/nsswitch.confのhosts: files dnsは、「まず/etc/hosts(files)、なければDNS」という順番の指定です。pingやgetent ahostsはOSの名前解決(nsswitch)を通るのでこの順番に従い、/etc/hostsの198.51.100.53へ向かいました。digとnslookupはDNSの問い合わせを自分で組み立てるので、/etc/resolv.confのサーバーだけを見て、DNSの192.0.2.10を答えます。アプリと同じ結果を見たいときはgetent ahosts、DNSサーバーの答えを見たいときはdig、と使い分けます。
getent hosts(aなし)は別の結果になりました。下の出力のとおり、/etc/hostsのIPv4ではなく、DNSのAAAAレコード(2001:db8::10)が表示されています。getent hostsはgethostbyname2で、getent ahostsはgetaddrinfoで調べます(getentのマニュアル)。アプリの接続先を確かめる目的なら、getent ahostsを使ってください。
getent hosts www.lab.example
2001:db8::10 www.lab.example足した行を消して元に戻します。コンテナの中ではsed -iが失敗しました。/etc/hostsはDockerがコンテナに個別にマウントしているファイル(/proc/self/mountinfoに出ます)で、sed -iは一時ファイルを作って名前を付け替える方法で書き換えるためです。中身を書き戻す形なら通ります。/etc/sedのあとの一時ファイル名は実行のたびに変わります。
sed -i '/www.lab.example/d' /etc/hosts
sed: cannot rename /etc/sedvXgsZG: Device or resource busy
awk '$5 ~ /^\/etc\// {print $5}' /proc/self/mountinfo
/etc/resolv.conf
/etc/hostname
/etc/hosts
grep -v ' www.lab.example$' /etc/hosts > /tmp/hosts && cat /tmp/hosts > /etc/hostsgetent ahosts www.lab.example
192.0.2.10 STREAM www.lab.example
192.0.2.10 DGRAM
192.0.2.10 RAW DNSを切り替える前に新しいサーバーを同じ名前で試したいだけなら、/etc/hostsを書き換えるより、curlの--resolveでそのコマンドの中だけ接続先を変えるほうが、消し忘れが起きません。手順はcurlコマンドの使い方の記事にあります。
つまずきポイント
nslookup wwwは引けるのにdig wwwはNXDOMAINになる
nslookup www
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
Name: www.lab.example
Address: 192.0.2.10
Name: www.lab.example
Address: 2001:db8::10
dig www
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 58538
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www. IN A
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:03:33 UTC 2026
;; MSG SIZE rcvd: 32dig +search +short www
192.0.2.10nslookupは/etc/resolv.confのsearch lab.exampleを使ってwww.lab.exampleを引きましたが、digは既定でsearchを使わないので、質問がwww.そのものになっています。このラボではUnboundがlocal-zone: "." staticにより自分でNXDOMAINを返しているため、aa付きです。対策は、digには最初から完全な名前(FQDN)を渡すか、+searchを付けることです。障害の調査では、searchの有無で答えが変わらないよう、両方のコマンドにFQDNを渡すほうをおすすめします。
正引きは引けるのに逆引きだけNXDOMAINになる(Unboundの既定のローカルゾーン)
Unboundは、プライベートアドレスや文書用アドレスの逆引きゾーンを、最初から自分で答える「既定のローカルゾーン」として持っています。unbound.confから2.0.192.in-addr.arpaのtransparent行を消して読み直させると、stub-zoneが残っていても逆引きが失敗しました。
unbound-control list_local_zones | grep -E '^(\.|10\.in-addr|16\.172|168\.192|2\.0\.192|8\.b\.d\.0)'
. static
8.b.d.0.1.0.0.2.ip6.arpa. transparent
10.in-addr.arpa. static
16.172.in-addr.arpa. static
2.0.192.in-addr.arpa. transparent
168.192.in-addr.arpa. static
sed -i '/"2.0.192.in-addr.arpa." transparent/d' /etc/unbound/unbound.conf && unbound-control reload
okdig -x 192.0.2.12
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 23590
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;12.2.0.192.in-addr.arpa. IN PTR
;; AUTHORITY SECTION:
2.0.192.in-addr.arpa. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800
;; Query time: 0 msec
;; SERVER: 198.51.100.10#53(198.51.100.10) (UDP)
;; WHEN: Sun Nov 01 00:03:34 UTC 2026
;; MSG SIZE rcvd: 111見分ける手がかりは、AUTHORITYのSOAが自分のゾーンのもの(ns1.lab.example.)ではなくlocalhost. nobody.invalid.になっていることと、aaが付いていることです。答えたのは権威サーバーではなくキャッシュサーバー自身です。一覧に10.in-addr.arpaや168.192.in-addr.arpaがstaticで並んでいるとおり、社内や自宅のプライベートアドレス(10.x.x.x、192.168.x.x)の逆引きも既定では塞がれていて、unbound.confのマニュアルにも同じ説明があります(執筆時点)。直し方は、そのゾーンにlocal-zone: "ゾーン名." transparent(またはnodefault)を書くことです。lab.exampleのtransparent行を消した場合も、"." staticに吸い込まれて同じようにaa付きのNXDOMAINになりました。
cp /lab/unbound.conf /etc/unbound/unbound.conf && unbound-control reload
okdig -x 192.0.2.12 +short
nagoyadevlab02.lab.example.sed -i '/"lab.example." transparent/d' /etc/unbound/unbound.conf && unbound-control reload
okdig www.lab.example | grep -E 'status|flags'
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 35992
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
; EDNS: version: 0, flags:; udp: 1232cp /lab/unbound.conf /etc/unbound/unbound.conf && unbound-control reload
ok追加したレコードがNXDOMAINのまま返る(否定応答のキャッシュ)
新しいサーバーtest01を登録する前に、キャッシュ経由で引いてしまった場合です。権威サーバーにAレコードを足したあとも、キャッシュ経由ではNXDOMAINが返り続けました。
nslookup test01.lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
** server can't find test01.lab.example: NXDOMAINsed -i 's/2026110102 ; serial/2026110103 ; serial/' /etc/bind/db.lab.example && echo 'test01 IN A 192.0.2.30' >> /etc/bind/db.lab.example && rndc reload lab.example
zone reload queuednslookup test01.lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
** server can't find test01.lab.example: NXDOMAIN
dig +noall +authority test01.lab.example
lab.example. 300 IN SOA ns1.lab.example. hostmaster.lab.example. 2026110102 3600 900 604800 300
nslookup test01.lab.example 198.51.100.53
Server: 198.51.100.53
Address: 198.51.100.53#53
Name: test01.lab.example
Address: 192.0.2.30キャッシュに残っているのは「test01はない」という否定応答で、一緒に保存されたSOAのserialは追加前の2026110102のままです。権威サーバーに直接聞くと192.0.2.30が返ります。このゾーンでは否定応答が最大300秒キャッシュされるので、待つか、flush_negativeで否定応答だけを消します。新しい名前を公開するときは、登録前にキャッシュ経由で引かないこと、確認はdig @権威 名前 +norecで行うことを手順に入れておくと、この取り違えを防げます。
unbound-control flush_negative
ok removed 0 rrsets, 1 messages and 0 key entriesnslookup test01.lab.example
Server: 198.51.100.10
Address: 198.51.100.10#53
Non-authoritative answer:
Name: test01.lab.example
Address: 192.0.2.30nslookup: not foundになる(入れるパッケージ)
ubuntu:26.04のコンテナイメージには、digとnslookupを含むbind9-dnsutilsが入っていません(下のaptの出力で、新しく入るパッケージの一覧に出ています)。新しいコンテナで試すと、shではnslookup: not foundになりました。入れるパッケージはbind9-dnsutilsです。昔からの名前のdnsutilsを指定しても、bind9-dnsutilsが「dnsutilsを提供する」(Provides)パッケージなので、aptはそちらを入れました。apt-cache showには更新版(9.20.24)と元の版(9.20.18)の2つが出ますが、どちらもProvides: dnsutilsです。
docker run -d --init --name nslookup-dig-command-dns-fresh --memory 256m ubuntu:26.04 sleep infinitynslookup www.lab.example
sh: 1: nslookup: not found
apt-get update -qq && apt-get install -y dnsutils
The following NEW packages will be installed:
bind9-dnsutils bind9-host bind9-libs krb5-locales libedit2 libgssapi-krb5-2
Setting up bind9-dnsutils (1:9.20.24-1ubuntu0.4) ...
apt-cache show bind9-dnsutils | grep -E '^(Package|Version|Provides)'
Package: bind9-dnsutils
Version: 1:9.20.24-1ubuntu0.4
Provides: dnsutils
Package: bind9-dnsutils
Version: 1:9.20.18-1ubuntu2
Provides: dnsutilsdocker rm -fv nslookup-dig-command-dns-fresh動作確認
最後に、ゾーンの変更が利用者側まで届いたかを1行ずつ確かめるスクリプトです。名前ごとに、キャッシュ経由の答え、権威サーバーの答え(+norec)、権威サーバーの答えを逆引きした名前を並べます。作業ディレクトリ(~/dnslab)に置けば、マウントしてある/labからクライアントで実行できます。CNAMEの名前では最後の行(行き先のアドレス)を比べます。
#!/bin/bash
# 引数のホスト名を、キャッシュサーバー経由と権威サーバー直接の両方で引き比べる
zone=lab.example
auth=198.51.100.53
for host in "$@"; do
name="$host.$zone"
via_cache=$(dig +short "$name" A | tail -n 1)
via_auth=$(dig +short +norec @"$auth" "$name" A | tail -n 1)
ptr=none
if [ -n "$via_auth" ]; then
ptr=$(dig +short -x "$via_auth")
ptr=${ptr:-none}
fi
[ "$via_cache" = "$via_auth" ] && same=yes || same=NO
echo "$name cache=${via_cache:-none} auth=${via_auth:-none} same=$same ptr=$ptr"
donebash /lab/check-dns.sh www mail nagoyadevlab02 app test01
www.lab.example cache=192.0.2.10 auth=192.0.2.10 same=yes ptr=www.lab.example.
mail.lab.example cache=192.0.2.25 auth=192.0.2.25 same=yes ptr=mail.lab.example.
nagoyadevlab02.lab.example cache=192.0.2.12 auth=192.0.2.12 same=yes ptr=nagoyadevlab02.lab.example.
app.lab.example cache=192.0.2.21 auth=192.0.2.21 same=yes ptr=none
test01.lab.example cache=192.0.2.30 auth=192.0.2.30 same=yes ptr=none5行ともsame=yesなら、キャッシュサーバーが権威サーバーと同じ答えを返しています。same=NOの行はキャッシュに古い答えが残っている名前、ptr=noneの行は逆引きの登録が漏れている名前です。このラボでは、わざとPTRを作らなかったappと、追加したtest01がptr=noneになりました。自分の環境で使うときは、スクリプト冒頭のzoneとauthを書き換えます。
ラボはDockerホストで片付けます。消えるのはコンテナとネットワークで、ubuntu:26.04のイメージと作業ディレクトリ~/dnslabは残ります。
docker rm -fv nslookup-dig-command-dns-auth nslookup-dig-command-dns-cache nslookup-dig-command-dns-client
docker network rm nslookup-dig-command-dns-netnslookupとdig、どちらを先に使うか
Linuxで調べるなら、最初はdigをおすすめします。1回の実行で応答コード、aaの有無、TTL、答えたサーバーがすべて見え、NXDOMAINとNODATAも取り違えません。nslookupを選ぶのは、Windowsの端末で調べるときと、利用者に画面を送ってもらうときです。Windowsに付属しているので、利用者の端末でもインストールなしで同じ手順を案内できます(Windowsのnslookupはこの記事では動かしていません)。
| 確かめたいこと | 最初に打つコマンド | 理由 |
|---|---|---|
| アプリ(ブラウザ、ssh、curl)がどこへ接続するか | getent ahosts 名前 | /etc/hostsとnsswitchの順番を通る。dig・nslookupは通らない |
| 社内のキャッシュDNSが何を返しているか | dig +noall +answer 名前 | 答えと残りTTLが1行で見える |
| ゾーンの変更が権威サーバーに入ったか | dig @権威 名前 +norec | aaとTTLの満額、SOAのserialで確かめられる |
| 名前がないのか、そのタイプがないのか | dig 名前 タイプ | status:とANSWER:で区別できる。nslookupの終了コードではNODATAが0になる |
| 逆引き | dig -x アドレス | IPv6の名前も自動で組み立て、答えたサーバーも表示される |
| スクリプトや監視 | dig +shortとstatus:の確認 | digはNXDOMAINやSERVFAILでも終了コード0なので、空の出力も失敗として扱う |
| Windows端末 | nslookup 名前 サーバー | 標準で入っている(この記事では未実行) |
この表が当てはまらないのは、問題がDNSの外にある場合です。digでもgetentでも正しいアドレスが返るのにつながらないなら、名前解決は終わっていて、原因は経路やアプリ側にあります。そこから先は、HTTPならcurl -vで接続のどの段階で止まるかを見ます。
よくある質問
nslookupの「権限のない回答」とは何ですか?
この記事で動かしたLinuxのnslookupでは、キャッシュサーバー経由の答えに「Non-authoritative answer:」が付き、権威サーバーに直接聞くと消えました。応答のaaビットが立っていない、つまりゾーンの権威ではないサーバーが覚えている答えだという意味で、異常ではありません。最新の内容か確かめるには、NSレコードで権威サーバーを調べて直接引き直します。
IPアドレスからドメイン名を調べる(逆引きする)方法は?
dig -x 192.0.2.12かnslookup 192.0.2.12で、PTRレコードを引きます。IPv6も同じ書き方です。NXDOMAINなら、そのアドレスのPTRが逆引きゾーンに登録されていません。逆引きは正引きとは別のゾーンで、このラボのBINDではAレコードを足してもPTRは増えませんでした。答えたサーバーも残したいならdigを使います。
Ubuntuでnslookupやdigを使うには何を入れればいいですか?
sudo apt install bind9-dnsutilsで、dig・nslookupに加えてdelv・mdig・nsupdateが入ります。Ubuntu 26.04で試したところ、版は9.20.24でした。古い名前のdnsutilsを指定しても、aptはbind9-dnsutilsを入れました。最小構成のコンテナイメージには入っていないので、コンテナで調べるときは最初に入れます。
nslookupとdigはどちらを使えばいいですか?
Linuxではdigを先に使うのがおすすめです。応答コード・aaフラグ・TTL・答えたサーバーが一度に見えます。Windowsの端末や、利用者に同じ操作をしてもらうときはnslookupが向きます。アプリの接続先を知りたいときはどちらでもなく、/etc/hostsも読むgetent ahostsを使います。
まとめ
nslookupとdigはDNSサーバーの答えを見る道具で、調べる名前を/etc/hostsでは引きません。「Non-authoritative answer」はキャッシュ経由の答えという意味で、変更が反映されたかはdig @権威 名前 +norecと並べて値・TTL・serialを比べれば判断できます。失敗したときはstatus:とcommunications errorの文言で、名前の問題かサーバーの問題かを先に分けます。次の作業として、自分の社内ゾーンの名前をcheck-dns.shのzoneとauthに入れて実行し、same=NOとptr=noneの行がないかを確かめてください。