NS 레코드는 두 곳에 있습니다

리졸버가 www.example.com을 조회할 때 내 DNS 공급자부터 찾아가지 않습니다. 먼저 .com 서버에 묻고, .com 서버는 “example.com은 이 네임서버들이 담당한다”는 위임(referral)으로 답합니다. 이 목록은 상위 zone, 즉 레지스트리에 있습니다. 등록대행사의 ‘네임서버’ 설정으로 바꾸고, TTL은 내가 아니라 레지스트리가 정합니다. .com은 172800초, 이틀입니다.

DNS 공급자에 있는 내 zone에도 루트(apex)에 NS 레코드가 따로 있습니다. 네임서버에 직접 물었을 때 돌아오는 것이 이것이고, 관리형 DNS라면 보통 공급자가 알아서 채워 둡니다.

두 목록은 같아야 하고, 목록에 있는 모든 서버가 그 zone에 대해 답해야 합니다. ‘네임서버 불일치’는 이것이 어긋나는 여러 경우를 묶어 부르는 말입니다.

  1. 레지스트리 목록에 아예 없는 공급자에서 레코드를 수정함. 압도적으로 흔합니다.
  2. 레지스트리의 목록과 zone 자체의 NS 목록이 다름.
  3. 목록에 있는 서버가 zone을 서비스하지 않음(lame delegation).
  4. 레지스트리의 glue에 네임서버의 예전 IP가 남아 있음.
  5. 레지스트리의 DNSSEC DS 레코드가 지금 zone을 서비스하는 공급자의 키와 맞지 않음.

증상은 겹칩니다. 변경이 끝내 반영되지 않음, 어떤 네트워크에서는 되고 어떤 곳에서는 안 됨, 첫 조회가 느림, SERVFAIL. 아래 점검으로 이것들을 구별할 수 있습니다.

1단계: 레지스트리가 가진 값

TLD 서버를 하나 찾고, 재귀를 끈 채로 내 도메인을 물어봅니다.

$ dig +short NS com
a.gtld-servers.net.
b.gtld-servers.net.
...

$ dig @a.gtld-servers.net example.com NS +norecurse

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30411
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1

;; AUTHORITY SECTION:
example.com.     172800  IN  NS  ns1.new-dns.example.
example.com.     172800  IN  NS  ns2.new-dns.example.

aa 플래그가 없고 answer 섹션이 비어 있습니다. 정상입니다. 상위 zone은 내 NS 레코드에 대해 권한이 없고, 위임만 내줍니다. 다른 TLD라면 dig +short NS net, dig +short NS kr처럼 시작하세요.

RDAP 조회도 레지스트리가 가진 네임서버 목록을 도메인 상태와 함께 보여 줍니다. 등록대행사 화면의 값이 RDAP이나 TLD 서버와 다르다면 변경이 아직 레지스트리에 도달하지 않았거나 실패한 것입니다.

2단계: 네임서버 각각의 응답

이제 그 목록의 모든 서버, 그리고 내 zone의 NS 레코드에 있는 모든 서버에 같은 질문을 합니다.

$ dig @ns1.new-dns.example example.com NS +norecurse

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5893
;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
example.com.     86400   IN  NS  ns1.new-dns.example.
example.com.     86400   IN  NS  ns2.new-dns.example.

이 서버는 정상입니다. status: NOERROR, aa 플래그(권한 있는 응답), 그리고 레지스트리와 같은 두 이름.

한 번에 전부, 각 서버가 내주는 SOA serial까지 확인하려면 이렇게 합니다.

parent=$(dig @a.gtld-servers.net example.com NS +norecurse +noall +authority | awk '$4=="NS" {print $5}')
for ns in $parent; do
  echo "== $ns"
  dig @"$ns" example.com SOA +norecurse | grep -E 'status:|flags:|^example\.com\..*SOA'
done

dig +nssearch example.com은 더 빠른 방법입니다. 리졸버가 알고 있는 각 네임서버의 SOA serial을 보여 줍니다. 다만 레지스트리 목록이 아니라 리졸버를 거쳐 본 zone의 NS 목록을 쓰므로, 위 반복문을 대신하지는 못하고 보완하는 용도입니다.

결과는 이렇게 읽습니다.

서버의 응답뜻
NOERROR, aa 플래그, 다른 서버와 같은 serial정상
REFUSED이 서버에 내 zone이 없습니다. lame.
SERVFAILzone 설정은 있지만 서비스하지 못합니다. 직접 운영하는 서버에서 zone 로드가 실패한 경우가 많습니다. lame.
aa 없는 NOERROR권한 서버가 아니라 캐시나 리졸버로서 답하고 있습니다. lame.
시간 초과다운, 경로 없음, 방화벽. 리졸버 입장에서는 lame입니다.
aa인데 serial이나 레코드가 다름동기화가 깨졌습니다. secondary가 zone transfer를 못 받고 있거나, 이전 공급자에 남은 예전 zone입니다.

애니캐스트를 쓰는 관리형 공급자는 업데이트 중에 잠깐 serial이 다르게 보일 수 있습니다. 차이가 계속 유지된다면 문제입니다.

흔한 상황과 해결

위임되지 않은 공급자에서 레코드를 수정함

새 DNS 공급자로 옮겼거나, 등록대행사 DNS를 떠난 적이 없는데, 세상이 쓰지 않는 대시보드에서 레코드를 고친 경우입니다. 1단계에서는 A 공급자의 네임서버가 나오는데 변경은 B 공급자에서 했습니다. 리졸버는 B에 묻지 않으니 모든 변경이 ‘전파가 안 되는’ 것처럼 보입니다.

해결: 어느 공급자가 zone을 서비스할지 정하세요. 거기서 변경하거나, 그동안 수정해 온 공급자로 레지스트리 네임서버를 바꾸세요. 바꾸기 전에 그 공급자에 zone이 빠짐없이 있는지 확인해야 합니다(그 네임서버를 대상으로 2단계를 하면 보입니다).

레지스트리 목록과 zone의 NS 목록이 다름

이전 작업 뒤에 흔합니다. 가져온 zone 파일에 이전 공급자의 NS 레코드가 루트에 그대로 들어 있거나, 등록대행사에 세 번째 네임서버를 추가하고 zone은 잊었거나, 그 반대입니다.

리졸버가 이런 불일치를 어떻게 다루는지는 제각각입니다. 어떤 리졸버는 위임 TTL 내내 상위 zone의 목록을 쓰고, 어떤 리졸버는 권한 있는 응답을 한 번 보고 나면 내 zone의 NS 레코드로 바꿔 씁니다. 권한 데이터가 더 높은 신뢰도를 갖기 때문입니다. 그래서 두 리졸버가 서로 다른 서버 집합으로 질문을 보내게 될 수 있습니다. 한쪽 집합에 오래된 레코드를 가진 이전 공급자가 섞여 있으면, 눈에 보이는 이유 없이 네트워크마다 다른 답이 나옵니다.

해결: zone의 루트 NS 레코드를 레지스트리 목록과 정확히 맞추세요. 대부분의 관리형 공급자는 루트 NS를 수정할 수 없게 해 두고, 그 값은 당연히 맞습니다. 그렇다면 불일치는 레지스트리 쪽에 있는 것입니다.

lame delegation(제 역할을 못 하는 네임서버)

목록에 있는 네임서버가 zone을 서비스하지 않습니다. 이전 공급자가 zone을 지웠거나, 계정이 해지됐거나, 네임서버 이름에 오타가 있거나, 네임서버 호스트명 자체가 더 이상 조회되지 않는 경우입니다. 리졸버는 실패하면 다른 서버를 시도하므로, 정상 서버가 하나라도 남아 있으면 도메인은 대체로 동작합니다. 다만 첫 조회가 느리고 불안정해집니다. 등록된 서버가 전부 lame이면 도메인은 SERVFAIL로 실패합니다.

Cloudflare에서만 생기는 변형이 있습니다. Cloudflare는 zone마다 특정 네임서버 이름 한 쌍(*.ns.cloudflare.com 두 개)을 배정합니다. 서로 바꿔 쓸 수 없습니다. 다른 도메인이나 다른 계정의 쌍을 복사해 넣으면, 레지스트리가 내 zone에 답하지 않는 Cloudflare 서버를 가리키게 되고 대시보드에서는 zone이 계속 대기(pending) 상태로 남습니다.

해결: 등록대행사에서 lame 서버를 빼거나, 그 서버가 zone을 서비스하도록 고치세요. “혹시 몰라서” 목록에 남겨 두지 마세요. 죽은 항목은 그 서버를 고른 모든 리졸버에게 시간 초과를 안깁니다.

오래된 glue

네임서버가 내 도메인 안에 있다면(ns1.example.com이 example.com을 서비스), 리졸버는 example.com을 조회할 수 있어야 그 네임서버의 주소를 알 수 있습니다. 레지스트리는 glue로 이 순환을 끊습니다. 그 네임서버들의 IP 주소를 레지스트리에 저장해 두었다가 위임 응답의 additional 섹션에 함께 보내는 것입니다.

$ dig @a.gtld-servers.net example.com NS +norecurse

;; AUTHORITY SECTION:
example.com.        172800  IN  NS  ns1.example.com.
example.com.        172800  IN  NS  ns2.example.com.

;; ADDITIONAL SECTION:
ns1.example.com.    172800  IN  A   198.51.100.53
ns2.example.com.    172800  IN  A   203.0.113.53

네임서버 IP를 바꾸면서 내 zone의 A 레코드만 고치면, 레지스트리는 계속 예전 주소를 내줍니다. dig @198.51.100.53 ns1.example.com A +norecurse의 결과와 glue를 비교하세요.

해결: 등록대행사에서 glue를 갱신하세요. 네임서버 목록과는 별개 화면인 경우가 많고, ‘네임서버 등록’, ‘호스트 레코드’, ‘child nameserver’ 같은 이름으로 되어 있습니다.

공급자 이전 후 남은 DS 레코드

이전 공급자에서 DNSSEC 서명을 했다면, 레지스트리에는 이전 공급자의 키 지문인 DS 레코드가 있습니다. 다른 키로 서명하거나 서명하지 않는 공급자로 옮기면, 검증하는 리졸버는 아무것과도 맞지 않는 DS를 보게 됩니다. zone을 신뢰할 수 없다고(bogus) 판단하고 SERVFAIL을 돌려줍니다. 검증하지 않는 리졸버는 계속 정상이라서 “통신사 DNS에서는 되는데 1.1.1.1과 8.8.8.8에서는 안 된다”는 모습이 됩니다.

$ dig @a.gtld-servers.net example.com DS +norecurse +noall +answer
example.com.   86400   IN   DS   2371 13 2 9F3A...

$ dig @ns1.new-dns.example example.com DNSKEY +norecurse +noall +answer
(출력 없음: 새 공급자는 서명하지 않음)

$ dig @1.1.1.1 example.com A        # status: SERVFAIL
$ dig @1.1.1.1 example.com A +cd    # status: NOERROR, 응답 있음

+cd는 리졸버에게 검증을 건너뛰라고 요청합니다. +cd를 붙이면 답이 나오고 빼면 사라진다면, 문제는 레코드가 아니라 DNSSEC입니다. 최근 버전의 dig는 리졸버가 확장 오류를 보내 주면 EDE: 9 (DNSKEY Missing) 같은 줄도 출력합니다. 어디서 끊기는지 그림으로 보고 싶다면 DNSViz(dnsviz.net)가 체인 전체를 그려 줍니다.

이미 깨졌다면: 등록대행사에서 DS를 지우거나, 새 공급자에서 DNSSEC을 켜고 그곳이 주는 DS로 바꾸세요. 검증하는 리졸버는 캐시에서 예전 DS가 만료되는 대로 회복합니다.

레지스트리가 아직 변경을 반영하지 않음

등록대행사에서 네임서버를 바꿨는데 1단계에서 여전히 예전 값이 나옵니다. 등록대행사가 레지스트리에 변경을 제출하는데, 그 속도는 곳마다 다릅니다. 어떤 레지스트리는 새 네임서버가 zone에 대해 답하는지 먼저 점검하고, 답하지 않으면 변경을 거절하거나 보류합니다(.de의 DENIC이 그렇습니다). 몇 시간이 지나도 레지스트리에 반영되지 않는다면 등록대행사에 요청이 어떻게 됐는지 문의하세요.

불일치 없이 DNS 공급자를 옮기는 순서

불일치는 대부분 이전 작업 중에 생깁니다. 피하는 순서는 이렇습니다.

  1. zone 전체를 새 공급자에 복사합니다. A와 MX만이 아니라 모든 레코드입니다. SPF·DKIM·도메인 인증용 TXT, CAA, SRV, 모든 서브도메인.
  2. 전환 전에 확인합니다. 이름 목록을 두고 새 네임서버에 dig @새-ns ... +norecurse로 직접 물어봅니다.
  3. DNSSEC을 쓰고 있다면 먼저 등록대행사에서 DS를 지우고, 네임서버를 바꾸기 전에 최소 DS TTL만큼 기다립니다. 두 공급자가 모두 지원한다면 multi-signer 방식의 키 롤오버를 따르는 방법도 있습니다.
  4. 등록대행사에서 네임서버를 바꿉니다. 레지스트리에 반영됐는지 1단계로 확인합니다.
  5. 이전 공급자가 같은 zone을 최소 48시간, 가능하면 더 오래 서비스하게 둡니다. 예전 위임을 캐시한 리졸버는 위임 TTL 동안 계속 그쪽을 씁니다. 이 기간에는 레코드를 수정하지 않거나, 양쪽에서 똑같이 수정합니다.
  6. 이전이 안정되면 새 공급자에서 서명하고 DS를 추가합니다.
  7. 그다음에 이전 공급자의 zone을 지웁니다.

이전 공급자를 왜 살려 둬야 하는지는 DNS 전파 가이드에, 내가 바꿀 수 없는 상위 zone 쪽 TTL은 TTL 가이드에 정리돼 있습니다. 도메인 이름 자체를 바꾸는 이전이라면 도메인 이전 체크리스트를 보세요.

바깥에서 확인하기

가장 정확한 답은 위 명령들이 줍니다. 터미널 없이 빠르게 보려면 이렇게 하세요.

  • RDAP 조회: 레지스트리가 가진 네임서버와 도메인 상태.
  • DNS 조회: 공개 리졸버가 돌려주는 NS, SOA 등 레코드와 TTL.
  • 전파 점검: 공개 리졸버 여섯 곳의 응답이 일치하는지. 예전 위임이 아직 캐시돼 있는 동안 유용합니다.

RDAP과 DNS 조회에 서로 다른 네임서버가 나온다면 레지스트리와 zone 사이의 불일치가 확인된 것이고, 어느 쪽을 고쳐야 하는지는 위 내용에서 찾을 수 있습니다.

관련 가이드

내 도메인에서 바로 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.