Post

[DNS의 뿌리를 찾아서 - 2편] DNS 캐시와 레코드

[DNS의 뿌리를 찾아서 - 2편] DNS 캐시와 레코드

1편에서는 DNS가 왜 필요한지, 그리고 로컬 네임 서버가 루트 네임 서버부터 차례로 물어 내려가며 IP 주소를 찾아오는 과정을 살펴봤습니다.

그런데 IP 주소 하나를 얻으려고 매번 그 8단계를 거친다면 어떨까요? 느릴 뿐 아니라, 전 세계의 질문이 루트 네임 서버로 몰릴 것입니다. 실제로는 그렇게 되지 않는데, 그 이유가 이번 편에서 볼 DNS 캐시입니다. 이어서 네임 서버가 답을 어떤 모양으로 저장하는지, DNS 레코드도 함께 살펴보겠습니다.

DNS Cache

DNS 캐시란?

DNS 캐시는 한 번 조회한 결과를 일정 시간 저장해 두었다가, 같은 질문이 오면 다시 묻지 않고 그 결과를 쓰는 것입니다.

전화번호 비유로 돌아가 보겠습니다. 자주 거는 번호를 매번 전화번호부에서 찾지 않고 연락처에 저장해 두는 것과 같습니다. 한 번 찾아 둔 번호는 다음부터 바로 꺼내 쓸 수 있습니다.

DNS도 마찬가지입니다. 누군가 www.example.com을 한 번 조회했다면, 그 답을 기억해 두었다가 다음 질문에는 8단계를 거치지 않고 바로 답합니다. 덕분에 응답이 빨라지고, 루트 네임 서버로 질문이 몰리는 일도 막을 수 있습니다.

캐시는 어디에 있을까?

캐시는 한 곳에만 있지 않습니다. 질문이 지나가는 길목마다 있습니다.

브라우저, 운영체제, 로컬 네임 서버가 차례로 자신의 캐시를 확인하고, 모두 없을 때만 네임 서버 계층에 묻는 과정 각 단계는 먼저 자기 캐시를 확인하고, 답이 없을 때만 다음 단계에 묻는다

  1. 브라우저 캐시: 브라우저가 직접 기억해 둔 결과입니다. Chrome에서는 chrome://net-internals/#dns에서 도메인을 조회해 보거나 캐시를 비울 수 있습니다.
  2. 운영체제 캐시: 운영체제가 기억해 둔 결과입니다. 1편에서 본 hosts 파일도 이 단계에서 확인합니다.
  3. 로컬 네임 서버 캐시: 로컬 네임 서버가 기억해 둔 결과입니다.

어느 단계에서든 답을 찾으면 거기서 끝나고, 그 뒤로는 묻지 않습니다. 세 곳 모두에 답이 없을 때만 루트 네임 서버부터 묻는 8단계가 시작됩니다.

로컬 네임 서버의 캐시가 특히 중요하다

브라우저와 운영체제의 캐시는 내 컴퓨터 하나만 씁니다. 반면 로컬 네임 서버의 캐시는 같은 로컬 네임 서버를 쓰는 모든 사람이 함께 씁니다. KT 인터넷을 쓰는 다른 누군가가 방금 www.naver.com을 조회했다면, 나는 처음 접속하더라도 바로 답을 받을 수 있습니다.

또 로컬 네임 서버는 최종 답(IP 주소)만 기억하지 않습니다. 조회하는 도중에 받은 “다음은 이 서버에 물어보세요”라는 안내도 함께 기억합니다.

  • com을 맡은 TLD 네임 서버가 누구인지 기억하고 있다면, 다른 .com 도메인을 조회할 때 루트를 건너뛰고 TLD 네임 서버부터 묻습니다.
  • example.com을 맡은 책임 네임 서버를 기억하고 있다면, mail.example.com을 조회할 때 곧바로 책임 네임 서버에 묻습니다.

1편의 dig +trace 출력에서 com.의 NS 레코드 TTL이 172800이었던 것을 떠올려 보세요. 172800초, 즉 이틀 동안 이 안내를 기억해도 된다는 뜻입니다. 그래서 실제로 루트 네임 서버까지 질문이 올라가는 일은 생각보다 드뭅니다.

TTL: 캐시의 유효기간

캐시를 영원히 들고 있을 수는 없습니다. 서버의 IP 주소가 바뀌면 기억해 둔 답이 틀린 답이 되기 때문입니다. 그래서 DNS의 모든 답에는 유효기간이 붙어 있는데, 이것이 TTL(Time To Live)입니다.

TTL은 이 답을 몇 초 동안 캐시해 둬도 되는지를 나타냅니다.

  • TTL은 책임 네임 서버의 관리자, 즉 도메인 주인이 레코드마다 정합니다.
  • 캐시에 저장된 순간부터 1초씩 줄어들고, 0이 되면 캐시에서 지운 뒤 다시 물어봅니다.

직접 확인해 보기

dig로 같은 도메인을 5초 간격으로 두 번 조회해 보겠습니다. +noall +answer는 답 부분만 간단히 출력하는 옵션입니다.

1
2
3
4
5
6
7
$ dig +noall +answer www.example.com
www.example.com.   300  IN  A  172.66.147.243
www.example.com.   300  IN  A  104.20.23.154

$ dig +noall +answer www.example.com   # 5초 뒤
www.example.com.   295  IN  A  172.66.147.243
www.example.com.   295  IN  A  104.20.23.154

첫 번째 조회에서는 로컬 네임 서버가 책임 네임 서버에게서 답을 새로 받아 와서 TTL이 원래 값인 300입니다. 두 번째 조회에서는 로컬 네임 서버가 캐시에 있던 답을 돌려줬기 때문에, 그사이 5초가 줄어든 295가 나왔습니다. 295초가 더 지나면 이 답은 캐시에서 사라지고, 그다음 조회 때 다시 책임 네임 서버에 묻게 됩니다.

TTL은 길게 잡아야 할까, 짧게 잡아야 할까?

TTL에는 정답이 없고, 무엇을 우선하느냐에 따라 달라집니다.

 TTL이 길면TTL이 짧으면
응답 속도캐시를 오래 써서 빠르다다시 묻는 일이 잦아 상대적으로 느리다
네임 서버 부담적다많다
IP 주소 변경 반영늦다 (옛 답이 오래 남는다)빠르다

그래서 잘 바뀌지 않는 com.의 NS 레코드는 이틀처럼 길게, 서비스 주소처럼 바뀔 수 있는 레코드는 몇 분처럼 짧게 잡는 경우가 많습니다.

서버를 옮기기 전에는 TTL부터 낮춰 두세요. TTL이 하루(86400초)인 상태에서 IP 주소를 바꾸면, 이미 캐시된 옛 주소가 최대 하루 동안 남아 일부 사용자가 옛 서버로 접속합니다. 그래서 서버를 옮기기 하루 이상 앞서 TTL을 짧게(예: 300초) 낮춰 둡니다. TTL을 바꿔도 이미 하루짜리로 캐시된 기록은 그대로 남아 있어서, 그 기록이 모두 만료된 뒤에야 짧은 TTL이 적용되기 때문입니다. 이전이 끝나면 TTL을 다시 원래대로 늘립니다.

없는 도메인도 캐시된다

DNS 캐시는 “있다”는 답만 기억하지 않습니다. “그런 도메인은 없다”는 답(NXDOMAIN)도 일정 시간 기억합니다. 이를 부정 캐시(Negative Cache)라고 합니다. NXDOMAIN이 어떤 의미인지는 이전 글에서 자세히 다뤘습니다.

존재하지 않는 도메인을 조회하면 다음과 같은 답이 옵니다.

1
2
3
4
5
$ dig this-domain-does-not-exist-12345.com
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 64506
...
;; AUTHORITY SECTION:
com.  900  IN  SOA  a.gtld-servers.net. nstld.verisign-grs.com. 1791967159 1800 900 604800 900

status: NXDOMAIN과 함께 SOA라는 레코드가 따라옵니다. 이 레코드의 TTL과 맨 마지막 숫자 중 작은 값만큼 “없다”는 답을 캐시합니다. 여기서는 900초, 즉 15분입니다.

부정 캐시는 오타 도메인처럼 없는 이름에 대한 질문이 계속 책임 네임 서버로 올라가는 것을 막아 줍니다. 다만 부작용도 있습니다. 새 레코드를 등록하기 직전에 그 이름을 한 번 조회했다면, “없다”는 답이 캐시에 남아 레코드를 만든 뒤에도 한동안 계속 없다고 나올 수 있습니다.

캐시 비우기

잘못된 답이 캐시에 남아 있다면 TTL이 끝나기를 기다리지 않고 직접 비울 수 있습니다.

1
2
3
4
5
6
7
8
# macOS
$ sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

# Windows
> ipconfig /flushdns

# Linux (systemd-resolved를 쓰는 경우)
$ resolvectl flush-caches

Chrome은 자체 캐시를 따로 가지고 있어서 chrome://net-internals/#dns에 들어가 Clear host cache 버튼을 눌러야 합니다.

로컬 네임 서버의 캐시는 내가 직접 비울 수 없습니다. ISP의 DNS 서버라면 TTL이 끝나기를 기다려야 합니다. Google(8.8.8.8)과 Cloudflare(1.1.1.1)는 Google Public DNS Flush Cache, Cloudflare Purge Cache 페이지에서 특정 도메인의 캐시를 비우도록 요청할 수 있습니다.

정리하면

  • DNS 캐시는 한 번 조회한 결과를 TTL 동안 기억해 두고 다시 쓰는 것입니다.
  • 브라우저 -> 운영체제 -> 로컬 네임 서버 순서로 캐시를 확인하고, 모두 없을 때만 루트부터 묻습니다.
  • 로컬 네임 서버는 최종 답뿐 아니라 중간 안내도 캐시해서, 루트까지 올라가는 일은 드뭅니다.
  • TTL이 길면 빠르지만 변경 반영이 늦고, 짧으면 그 반대입니다.
  • “없다”는 답(NXDOMAIN)도 캐시됩니다.

DNS Record

지금까지 네임 서버가 “답을 알려 준다”고만 했는데, 그 답은 네임 서버 안에 어떤 모양으로 저장되어 있을까요? 네임 서버는 정보를 자원 레코드(Resource Record)라는 단위로 저장합니다. 보통 줄여서 DNS 레코드라고 부릅니다.

사실 레코드는 이미 여러 번 봤습니다. 앞의 dig 출력에서 한 줄 한 줄이 모두 레코드입니다.

레코드 읽는 법

1
www.example.com.   300   IN   A   1.2.3.4

왼쪽부터 순서대로 다음과 같습니다.

  • 이름: 이 레코드가 설명하는 도메인 네임입니다.
  • TTL: 이 레코드를 몇 초 동안 캐시해도 되는지입니다. 앞에서 본 그 TTL입니다.
  • 클래스: 거의 항상 IN(Internet)입니다.
  • 타입: 이 레코드가 어떤 종류의 정보인지 나타냅니다. 아래에서 자세히 봅니다.
  • 값: 타입에 따라 IP 주소, 다른 도메인 네임, 메일 서버 등이 들어갑니다.

한 가지 덧붙이면, www.example.com에서 example.com이 우리가 산 도메인이고 www는 그 안의 호스트 이름입니다. 그래서 가비아나 Cloudflare 같은 곳의 DNS 관리 화면에서는 이름 칸에 www만 적습니다. example.com 자체를 가리킬 때는 이름 칸을 비우거나 @라고 적습니다.

자주 쓰는 레코드 타입

타입의미값의 예
A도메인 네임 -> IPv4 주소1.2.3.4
AAAA도메인 네임 -> IPv6 주소2001:db8::1
CNAME이 이름은 다른 이름의 별칭이다example.com.
NS이 도메인을 맡은 네임 서버elliott.ns.cloudflare.com.
MX이 도메인의 메일 서버10 mx1.mail.naver.com.
TXT자유로운 텍스트 정보"v=spf1 include:_spf.google.com ~all"
SOA이 도메인 영역의 기본 관리 정보대표 네임 서버, 관리자, 부정 캐시 시간 등

A, AAAA

가장 기본이 되는 레코드입니다. 도메인 네임을 IP 주소에 연결합니다. IPv4 주소면 A, IPv6 주소면 AAAA를 씁니다. IPv6 주소가 IPv4보다 4배 길어서 A를 4개 붙였다고 기억하면 쉽습니다.

하나의 이름에 여러 개의 A 레코드를 둘 수도 있습니다. 앞에서 www.example.com을 조회했을 때 IP 주소가 두 개 나왔던 것이 그 예입니다. 이렇게 하면 여러 서버로 접속을 나눌 수 있습니다.

CNAME

CNAME(Canonical Name)은 “이 이름은 저 이름의 별칭이니, 저 이름으로 다시 찾아보세요”라는 레코드입니다.

1
2
3
$ dig +noall +answer www.github.com
www.github.com.   3413  IN  CNAME  github.com.
github.com.       38    IN  A      20.200.245.247

www.github.com을 물었더니 “github.com의 별칭”이라는 답이 오고, 이어서 github.com의 A 레코드로 IP 주소를 찾았습니다.

CNAME의 장점은 실제 IP 주소를 한 곳에서만 관리하면 된다는 점입니다. www.github.com이 github.com을 따라가게 해 두면, 서버 IP가 바뀌어도 github.com의 A 레코드 하나만 고치면 됩니다.

다만 CNAME을 둔 이름에는 다른 레코드를 함께 둘 수 없습니다. A는 IP 주소, MX는 메일 서버처럼 레코드마다 답하는 질문이 정해져 있지만, CNAME은 어떤 질문이든 “저 이름에 가서 물어보세요”라고 넘기기 때문입니다. 같은 이름에 다른 레코드가 함께 있으면 한 질문에 대답이 두 개가 됩니다.

같은 이유로 루트 도메인(example.com, DNS 관리 화면의 @)에는 CNAME을 둘 수 없습니다. 루트 도메인에는 DNS 관리를 시작할 때부터 NS, SOA 레코드가 들어 있어서, CNAME을 추가하면 같은 이름에 다른 레코드가 함께 있게 되기 때문입니다. 이 제약 때문에 AWS 로드밸런서처럼 IP 대신 이름만 주는 서비스는 루트 도메인에 연결하기 어렵습니다. CNAME은 쓸 수 없고, A 레코드로 IP를 적자니 그 IP가 수시로 바뀌기 때문입니다.

그래서 AWS Route 53의 ALIAS, Cloudflare의 CNAME Flattening 같은 기능이 따로 있습니다. DNS 업체가 연결된 이름의 현재 IP를 대신 찾아 A 레코드처럼 답해 주는 기능으로, 겉으로는 A 레코드지만 속으로는 CNAME처럼 동작합니다.

NS

NS(Name Server)는 이 도메인을 맡은 책임 네임 서버가 누구인지를 나타냅니다. 1편의 dig +trace에서 루트와 TLD 네임 서버가 “다음은 여기에 물어보세요”라고 안내할 때 돌려준 것이 바로 NS 레코드입니다.

도메인을 산 곳(가비아 등)에서 네임 서버를 Cloudflare나 Route 53으로 바꾸는 작업이 바로 상위 TLD 네임 서버에 등록된 NS 레코드를 바꾸는 일입니다. 그래서 이 작업은 com TLD 쪽의 NS 레코드 TTL(이틀) 때문에 반영되는 데 시간이 걸리곤 합니다.

MX

MX(Mail Exchange)는 이 도메인으로 오는 메일을 받을 서버를 나타냅니다. 웹 서버와 메일 서버를 따로 운영할 수 있도록 별도의 레코드로 분리되어 있습니다.

1
2
3
4
5
$ dig +noall +answer naver.com MX
naver.com.   81  IN  MX  10 mx1.mail.naver.com.
naver.com.   81  IN  MX  20 mx5.mail.naver.com.
naver.com.   81  IN  MX  20 mx4.mail.naver.com.
naver.com.   81  IN  MX  20 mx6.mail.naver.com.

값 앞의 숫자는 우선순위이고, 작을수록 먼저 사용합니다. mx1에 먼저 보내고, 문제가 있으면 우선순위 20인 서버들로 보냅니다.

TXT, SOA

  • TXT: 아무 텍스트나 담을 수 있는 레코드입니다. 주로 “이 도메인의 주인이 맞다”는 것을 증명하는 데 씁니다. 예를 들어 Google Search Console에 사이트를 등록할 때 TXT 레코드를 추가하라고 안내받습니다. 메일 위조를 막는 SPF 설정에도 씁니다.
  • SOA(Start of Authority): 도메인 영역의 기본 관리 정보입니다. 대표 네임 서버, 관리자 메일 주소, 그리고 앞에서 본 부정 캐시 시간 등이 들어 있습니다.

정리하면

  • 네임 서버는 정보를 이름 / TTL / 클래스 / 타입 / 값 형태의 레코드로 저장합니다.
  • A·AAAA는 IP 주소, CNAME은 별칭, NS는 담당 네임 서버, MX는 메일 서버를 나타냅니다.
  • 도메인을 서버에 연결한다는 것은 결국 책임 네임 서버에 알맞은 레코드를 등록하는 일입니다.

마무리를 하면서

이 시리즈는 지난 글에서 DNS_PROBE_FINISHED_NXDOMAIN 에러를 해결하면서 시작됐습니다. 에러는 해결했지만, 정작 DNS가 어떻게 동작하는지는 제대로 모르고 있다는 걸 느꼈습니다. 그래서 이번 기회에 뿌리부터 정리해 보기로 했습니다. (물론 딥하지는 않습니다..ㅎㅎ)

확실히 CS과목 중에서는 네트워크 공부할 때 조금 더 재미를 느끼는 것 같습니다. 다음에도 저의 이해와, 다른 개발자 분들의 이해를 도울 수 있게 좋은 글로 찾아뵙겠습니다. 감사합니다.

Trending Tags