들어가며 — "It's always DNS"의 진실
SRE 커뮤니티에는 오래된 격언이 있다:
"It's always DNS. Even when it's not DNS, it's DNS."
인시던트 콜에서 가장 먼저 의심하는 게 DNS다. 왜일까? DNS가 복잡해서? 취약해서? 아니다 — DNS가 보이지 않기 때문이다. 모든 네트워크 요청의 첫 단계이면서, 대부분의 개발자가 내부를 모른다. 캐싱 계층이 여러 개 쌓여있어 변경이 반영되는 시점이 애매하고, TTL이라는 개념 하나로 수많은 버그가 설명된다.
이 글은 DNS의 모든 것을 1,500줄에 걸쳐 파헤친다. 1980년대 hosts.txt에서 출발한 이름 시스템의 역사, 쿼리 흐름의 정확한 단계, 레코드 타입들의 실제 용도, DNSSEC의 chain of trust, DoH/DoT/DoQ 암호화 DNS, Anycast가 어떻게 CDN을 빠르게 만드는지, CoreDNS가 쿠버네티스 안에서 어떻게 돌아가는지, DNS amplification 공격이 왜 그렇게 파괴적인지, 그리고 실전 운영에서 겪는 안티패턴들까지.
이 글은 TLS 1.3 + QUIC 글의 자연스러운 짝이다. HTTPS 연결이 일어나기 전에 항상 DNS가 먼저다. 그 숨은 첫 단계를 이해하자.
1. DNS의 짧은 역사
1.1 hosts.txt의 시대
1970년대 ARPANET에는 HOSTS.TXT 파일 하나로 모든 호스트 이름이 관리됐다. SRI-NIC (Stanford)에 마스터 파일이 있었고, 각 호스트가 주기적으로 FTP로 받아 동기화했다.
문제는 명확했다:
- 네트워크가 커지면 파일이 커짐.
- 중앙 갱신의 병목.
- 이름 충돌 조정이 수동.
- 새 호스트가 실제로 보이기까지 지연.
1.2 Paul Mockapetris와 DNS
1983년 USC의 Paul Mockapetris가 RFC 882/883 (나중에 RFC 1034/1035)으로 분산 계층적 이름 시스템을 제안한다. 핵심 아이디어:
- 계층:
.com,.edu,.kr같은 top-level domain에서 시작해 하위로 위임. - 권한 위임(delegation): 상위 영역이 하위 영역의 관리를 아래로 넘김.
- 캐싱: 응답을 TTL 동안 캐시.
- 변환 양방향: 이름 → IP (forward), IP → 이름 (reverse).
이 설계가 40년이 지난 지금도 여전히 인터넷의 기반이다. 변하지 않은 이유는 변할 필요가 없었기 때문이다 — 훌륭한 추상이다.
1.3 주요 변화들
- 1987년 RFC 1034/1035 표준화.
- 1990년대 BIND(Berkeley Internet Name Domain) 널리 보급.
- 1999년 DNSSEC 제안 (RFC 2535).
- 2005년 DNSSEC 재설계 (RFC 4033).
- 2010년 root zone 서명 시작.
- 2018년 DoH 표준화 (RFC 8484).
- 2022년 DoQ (DNS over QUIC) 표준화 (RFC 9250).
- 2024년 DNS HTTPS/SVCB 레코드 본격 배포.
DNS는 느리게, 그러나 꾸준히 진화한다.
2. DNS의 이름 공간
2.1 FQDN의 구조
www.example.com. 같은 이름은 오른쪽에서 왼쪽으로 읽는다:
. (root, 흔히 생략됨)
com. (TLD - Top-Level Domain)
example.com. (SLD - Second-Level Domain)
www.example.com. (subdomain / hostname)
맨 뒤의 점(dot)은 실제로는 있다 — **FQDN (Fully Qualified Domain Name)**은 root까지 명시. 평소에는 생략한다.
2.2 DNS Zone
Zone은 한 권한(authoritative) 서버가 담당하는 이름의 묶음. example.com zone은 example.com, www.example.com, mail.example.com 등을 포함할 수 있다. 하지만 sub.example.com의 관리를 다른 서버에 위임하면, sub.example.com은 별도 zone이 된다.
Zone 파일 예시(BIND 포맷):
$ORIGIN example.com.
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2026041500 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
86400 ) ; minimum TTL
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
@ IN A 192.0.2.1
www IN A 192.0.2.1
mail IN A 192.0.2.2
@ IN MX 10 mail.example.com.
2.3 TLD의 종류
- gTLD (generic):
.com,.net,.org,.dev,.io. 2012년 이후 수천 개로 확장. - ccTLD (country-code):
.kr,.jp,.uk. 국가 코드 기반. - infrastructure TLD:
.arpa. 역방향 조회 전용. - sTLD (sponsored):
.edu,.gov. 특정 기관 관리.
ICANN이 root 관리. 각 TLD는 별도 registry가 운영(Verisign이 .com, .net 등).
3. 쿼리의 실제 흐름
3.1 전체 그림
당신의 브라우저가 www.example.com을 찾을 때:
1. 브라우저 캐시 → 운영체제 캐시 → /etc/hosts → stub resolver
2. Stub resolver (libc) → Recursive resolver (ISP or 1.1.1.1 등)
3. Recursive resolver 캐시 히트? YES → 즉시 반환
NO → 다음 단계
4. Recursive → Root servers (a.root-servers.net 등 13개)
"who runs .com?" → "these NS records"
5. Recursive → .com TLD servers
"who runs example.com?" → "ns1.example.com, ns2.example.com"
6. Recursive → ns1.example.com (authoritative)
"what's www.example.com?" → "192.0.2.1"
7. Recursive가 결과를 캐시하고 stub에 반환
8. Stub이 OS에 반환, OS가 브라우저에 반환
이 과정이 "cold" 상태일 때 100–500ms 걸릴 수 있다. 캐시 히트면 1ms 미만.
3.2 Stub Resolver
OS 내장된 최소 DNS 클라이언트. getaddrinfo() 함수가 실제로 이걸 호출한다. Linux는 glibc의 resolv.conf를 읽어 어느 recursive resolver로 보낼지 결정.
# /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8
search example.com internal.local
options ndots:2 timeout:2 attempts:3
nameserver: recursive resolver 주소들.search: 도메인 접미사 자동 추가 후보.ndots: 이름에 점이 N개 미만이면 search 도메인을 시도.timeout/attempts: 각 서버 시도당 대기/반복.
현대 시스템 대부분에 systemd-resolved 같은 스텁 캐시가 로컬에 추가로 있다. resolv.conf가 127.0.0.53을 가리키면 그 뒤에 systemd-resolved가 있다.
3.3 Recursive Resolver
사용자를 위해 재귀적으로 쿼리를 해결해주는 서버. ISP가 운영하거나 공용 서비스(Cloudflare 1.1.1.1, Google 8.8.8.8, Quad9 9.9.9.9)를 쓴다.
주요 책임:
- 캐시 관리 (TTL 존중).
- Root → TLD → Authoritative 순으로 반복 쿼리.
- 응답의 RRSIG 검증 (DNSSEC 활성 시).
- 클라이언트에 최종 답 반환.
3.4 Root Servers
13개의 논리적 서버 (a.root-servers.net ~ m.root-servers.net). 실제로는 각각이 anycast로 수백 개 물리 장소에 분산. 전 세계 어디서든 가까운 노드로 라우팅.
root zone은 매우 작다 — TLD들의 NS 레코드만 포함. ICANN이 관리하고, 여러 조직(Verisign, University of Maryland, NASA 등)이 각 root server를 운영.
3.5 Authoritative Servers
특정 도메인의 "공식" 답을 주는 서버. 도메인 소유자가 운영하거나 위탁(Cloudflare DNS, AWS Route 53, Google Cloud DNS).
Primary(master) 하나 + Secondary(slave) 여러 개가 전형. Primary에서 zone 파일을 편집하면 AXFR/IXFR로 secondary들에 전파.
Glue Record: ns1.example.com의 IP를 얻으려면 example.com의 NS를 먼저 알아야 하는데, 그 NS가 ns1.example.com이면 순환. 이 문제를 풀기 위해 parent zone (.com)이 ns1.example.com의 A 레코드를 "glue"로 함께 제공.
4. 레코드 타입 — 무엇을 저장하는가
4.1 기본 레코드들
- A: IPv4 주소.
example.com. IN A 192.0.2.1 - AAAA: IPv6 주소.
example.com. IN AAAA 2001:db8::1 - CNAME: 정규 이름(canonical name).
www.example.com. IN CNAME example.com.- 주의: CNAME이 가리키는 이름에는 다른 레코드가 있을 수 없다. apex(zone 최상단)에 CNAME은 금지.
- NS: 이 zone의 권한 서버.
example.com. IN NS ns1.example.com. - SOA (Start of Authority): zone의 메타데이터 — primary, admin email, serial, refresh/retry/expire/minimum.
- MX: 메일 교환 서버. 우선순위 값 포함.
example.com. IN MX 10 mail.example.com. - TXT: 임의 텍스트. SPF, DKIM, DMARC, 도메인 검증에 사용.
- PTR: 역방향 조회(IP → 이름).
1.2.0.192.in-addr.arpa. IN PTR example.com. - SRV: 서비스 위치.
_sip._tcp.example.com. IN SRV 0 5 5060 sipserver.example.com. - CAA: 인증서 발급 정책. 어느 CA가 인증서를 발급할 수 있는지.
4.2 최근 추가된 레코드들
- HTTPS/SVCB (RFC 9460): 2024년부터 보편화. HTTP/3 지원, IP 힌트, ECH 공개키 등을 한 레코드에 담음.
example.com. HTTPS 1 . alpn="h3,h2" ipv4hint=192.0.2.1 ech=AEX+...
브라우저가 이 레코드를 보면 즉시 HTTP/3로 접속 시도, IP는 힌트로 미리 확보, ECH는 TLS 핸드셰이크의 SNI 암호화에 사용. 한 번의 DNS 조회로 많은 걸 해결.
- SVCB: 일반 서비스 바인딩. HTTPS는 SVCB의 특화 형태.
4.3 DNSSEC 전용 레코드
- DNSKEY: zone의 공개키.
- RRSIG: 레코드셋의 서명.
- DS (Delegation Signer): parent zone에 저장되는 child의 키 해시.
- NSEC / NSEC3: "이 이름은 존재하지 않음"의 증명.
4.4 CNAME 체인의 현실
CNAME이 A로 풀릴 때까지 여러 단계를 거칠 수 있다:
www.example.com. CNAME cdn.example.com.
cdn.example.com. CNAME xyz.cloudfront.net.
xyz.cloudfront.net. CNAME d123.cloudfront.net.
d123.cloudfront.net. A 192.0.2.10
브라우저는 보통 한 번에 최종 A를 받지만, resolver가 각 단계를 추적해야 한다. 체인이 길면 조회가 느려지고 실패 지점이 늘어난다. 최대 3–4단계 이하로 유지가 권장사항.
또 apex 도메인(example.com 자체)에는 CNAME을 못 쓰는 문제. CDN을 apex에 붙이려면:
- ALIAS 레코드 (DNS 제공자 고유 기능, Route 53/Cloudflare/NS1).
- ANAME (일부 제공자).
- CNAME flattening (Cloudflare): apex CNAME처럼 동작시키고, 실제로는 서버에서 A로 풀어 응답.
RFC 표준이 아니라 각 제공자가 구현한 hack. 2024년 RFC 9460(HTTPS 레코드)이 이 문제를 부분적으로 해결하지만 완전한 대안은 아직 없다.
5. 메시지 포맷 — 바이트 수준에서
5.1 DNS 패킷 구조
전체는 헤더 + 4개 섹션:
+---------------------+
| Header |
+---------------------+
| Question | — 무엇을 물어보는가
+---------------------+
| Answer | — 실제 답변
+---------------------+
| Authority | — authoritative NS
+---------------------+
| Additional | — 보너스 정보 (A records for NSes)
+---------------------+
5.2 헤더
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- ID: 트랜잭션 ID. 응답은 같은 ID로 돌아옴.
- QR: 0=query, 1=response.
- Opcode: 0=standard query, 5=update (dynamic DNS).
- AA: authoritative answer.
- TC: truncated — UDP 512바이트 넘어 잘림.
- RD: recursion desired.
- RA: recursion available.
- RCODE: 0=NOERROR, 2=SERVFAIL, 3=NXDOMAIN, 5=REFUSED.
- QDCOUNT/ANCOUNT/NSCOUNT/ARCOUNT: 각 섹션의 레코드 수.
5.3 Name Compression
같은 이름이 패킷에 여러 번 등장할 수 있다. 이를 압축하기 위해 "포인터" 형식: 첫 2비트가 11이면 나머지 14비트가 패킷 내 오프셋.
[0x03]www[0x07]example[0x03]com[0x00] -- 첫 등장
[0xC0 0x00] -- "offset 0에 있는 이름" 참조
16KB 패킷 한계 내에서만 유효하지만 대부분의 DNS 패킷이 충분히 작아 효과적.
5.4 UDP 512바이트 한계와 TC 비트
전통 DNS는 UDP 포트 53. 단일 메시지는 512바이트 한계. 이를 넘으면 TC 비트 세우고 클라이언트에게 "TCP로 다시 와"라고 암묵적으로 알린다.
DNSSEC 응답은 RRSIG 때문에 쉽게 512바이트를 넘는다. 그래서 **EDNS0 (Extension Mechanisms for DNS)**가 필수:
5.5 EDNS0
OPT 레코드로 확장 필드 운반. 주요 기능:
- UDP payload size 협상: 클라이언트가 "나는 4096바이트까지 받을 수 있음"을 광고. 서버가 그 이하면 UDP로, 넘으면 TC+TCP.
- DO (DNSSEC OK) flag: DNSSEC 응답 원함.
- extended RCODE: 4비트 이상의 에러 코드.
- Client Subnet (EDNS-CSUBNET): 실제 사용자의 서브넷을 authoritative에 전달 → CDN 지오 라우팅 정확도.
EDNS0 없이는 현대 DNS가 거의 동작 못 한다. 아주 오래된 미들박스가 EDNS0 못 알아보고 드롭하면 DNSSEC 검증 실패 → 체감상 "사이트 안 열림".
6. 캐싱과 TTL — 인터넷이 스케일되는 이유
6.1 TTL의 의미
모든 DNS 응답은 **TTL (Time To Live)**을 포함. "이 응답을 N초 동안 캐시해도 됨".
www.example.com. 300 IN A 192.0.2.1
^^^
TTL: 300초 = 5분
TTL이 작으면:
- 변경 반영 빠름.
- Recursive resolver 부하 증가.
- Authoritative 서버 부하 증가.
TTL이 크면:
- 변경 반영 느림 (최대 TTL + recursive가 캐시 리프레시 주기).
- 부하 감소.
6.2 여러 레벨의 캐시
실제로는 캐시가 여러 층에 쌓여있다:
브라우저 캐시 (보통 자체 TTL, 분 단위)
↓
운영체제 캐시 (Windows, macOS의 DNS Client)
↓
systemd-resolved (Linux)
↓
Recursive resolver (ISP/Cloudflare)
↓
Authoritative (실제 답)
한 단계에서 캐시 히트면 위층 캐시도 그 답을 받아 캐싱. 변경 전파는 이 모든 층의 TTL 만료를 기다려야 한다.
그래서 "DNS 바꿨는데 아직 옛날 IP로 갑니다"가 흔한 불평. 실제로는 체인의 어디선가 캐시가 유효한 것. 인내심이 DNS의 주 덕목.
6.3 Negative Caching
NXDOMAIN (없음) 응답도 캐시된다. SOA 레코드의 minimum 필드 값이 캐시 시간:
SOA ... ( ... 86400 ) ; 86400 = NXDOMAIN 캐시 1일
너무 크면 "내가 새로 만든 도메인이 아직 안 보임" 문제. 너무 작으면 공격자가 존재하지 않는 이름을 반복 조회해 부하 유발.
6.4 TTL 튜닝 전략
- 일반 서비스 레코드: 300–3600초 (5분–1시간).
- 이전 예정 있는 것: 60–300초로 미리 낮춤 (며칠 전).
- 거의 변하지 않는 NS: 86400–172800초 (1–2일).
- 극단적 안정성 필요 (root zone): 2일.
마이그레이션 직전에 TTL을 낮춰두는 것이 기본 기술. 변경 후 다시 올려 부하 감소.
7. DNSSEC — 서명된 DNS
7.1 왜 DNSSEC인가
기본 DNS는 신뢰가 없다. 응답이 중간에서 변조돼도 알 방법 없음. DNS hijacking, cache poisoning, Kaminsky 공격 등이 현실.
DNSSEC는 각 RRset에 디지털 서명을 붙여 무결성 보장.
7.2 구성 요소
- DNSKEY: zone의 공개키 (ZSK - Zone Signing Key, KSK - Key Signing Key).
- RRSIG: 각 RRset의 서명 (ZSK로 서명).
- DS: parent zone이 child의 KSK 해시를 보관.
- NSEC / NSEC3: 부재 증명.
7.3 Chain of Trust
Root zone KSK (전 세계 공개, ICANN 관리)
→ Root zone이 .com의 DS를 서명
→ .com zone의 KSK가 DS로 검증됨
→ .com zone이 example.com의 DS를 서명
→ example.com zone의 KSK가 DS로 검증됨
→ example.com의 ZSK가 KSK로 서명됨
→ 실제 레코드들이 ZSK로 서명됨
클라이언트(validating resolver)는 root KSK만 내장하면, 이 체인을 타고 내려가 모든 레코드의 무결성을 검증할 수 있다.
7.4 RRSIG의 구조
www.example.com. 300 IN A 192.0.2.1
www.example.com. 300 IN RRSIG A 8 3 300 (
20260501000000 20260415000000 12345 example.com.
SIGNATURE_BYTES )
- 알고리즘 (8 = RSA/SHA-256).
- Labels 수 (3 = www.example.com.의 라벨 수).
- Original TTL.
- Signature expiration / inception.
- Key tag (DNSKEY 식별자).
- Signer's name.
- 실제 서명.
서명 유효 기간이 한정되어(보통 며칠~몇 주) 정기적 재서명 필요. 이 갱신을 automatic signing으로 자동화 (BIND, PowerDNS, Knot의 기본 기능).
7.5 NSEC와 NSEC3 — 부재 증명
"foo.example.com은 없음"을 어떻게 증명하는가? sorted linked list 접근:
alpha.example.com. NSEC beta.example.com. A RRSIG NSEC
beta.example.com. NSEC charlie.example.com. A RRSIG NSEC
...
Resolver가 delta를 물으면, 서버가 charlie.example.com NSEC epsilon.example.com를 반환. "delta는 charlie와 epsilon 사이에 없음". 이 NSEC도 RRSIG로 서명됨 → 무결성 확인.
문제: 이 체인을 따라가면 zone의 모든 이름을 열거할 수 있음 (zone walking). 프라이버시 침해.
NSEC3: 이름을 해시해서 순서 짓기. 해시된 이름끼리 linked list. 역산 어려움 → zone walking 완화 (but not eliminated - 2011년 NSEC3 walker 공개).
최신 대안 NSEC5나 black lies (Cloudflare의 DNSSEC 라이브 서명) 등으로 진화 중.
7.6 실전 배포의 현실
DNSSEC 배포율은 아직 낮다. 2026년 현재:
- TLD 수준: 대부분 배포됨 (.com, .net, .org, .dev 등).
- SLD 수준: 전체 도메인의 10–30% 정도만 서명.
- Validating resolver: Cloudflare, Google, Quad9가 기본 검증.
왜 배포가 느린가?
- 키 관리 복잡 (ZSK rotation, KSK rollover는 parent와 조율 필요).
- 잘못 설정하면 SERVFAIL → 사이트 완전 다운.
- 대형 CA가 이미 MITM 보호 제공 (HTTPS 인증서) → 필요성 약해 보임.
그럼에도 DNSSEC는 인증서 생태계와 다른 가치를 준다. 인증서는 TLS 핸드셰이크 후에 검증. DNSSEC는 그 전 단계에서 이름 해결의 무결성을 보장. DANE(DNS-based Authentication of Named Entities, RFC 6698) 같은 기술은 DNSSEC 위에 인증서 고정을 올림. 완전한 보안은 두 계층의 결합.
8. DoH / DoT / DoQ — 암호화 DNS
8.1 문제
기본 DNS는 UDP/TCP 53번 포트, 평문. 누구나 감청 가능:
- ISP가 어느 사이트에 접속하는지 봄.
- 공공 Wi-Fi의 스니퍼.
- 국가 단위 DNS 기반 차단.
8.2 DoT (DNS over TLS) — RFC 7858
DNS 쿼리를 TLS로 감쌈. 포트 853. 간단하고 방화벽 친화적.
Client → (TLS) → DoT Resolver → (plain) → Authoritative
클라이언트와 recursive 사이만 암호화. 그 뒤는 여전히 평문.
8.3 DoH (DNS over HTTPS) — RFC 8484
더 과격한 접근. DNS 쿼리를 HTTP POST/GET으로 감쌈. 포트 443. 일반 웹 트래픽과 구분 불가.
POST /dns-query HTTP/2
Host: cloudflare-dns.com
Content-Type: application/dns-message
장점:
- 방화벽이 웹 트래픽과 구분 못 함 → 차단 우회.
- HTTP/2, HTTP/3 장점 그대로 (multiplexing, 1-RTT).
- 브라우저가 직접 구현 가능.
단점:
- 운영자가 내부 DNS 정책을 실행 어려움 (DoH가 enterprise DNS를 우회).
- 프라이버시는 resolver에 의존 — Cloudflare/Google에 모든 쿼리 몰림.
Firefox가 2019년 TRR (Trusted Recursive Resolver)로 미국에서 Cloudflare DoH를 기본 활성화. 논쟁이 있었지만 프라이버시 관점에서 진전.
8.4 DoQ (DNS over QUIC) — RFC 9250
2022년 표준화. QUIC 위에 DNS를 얹음. DoT보다:
- 0-RTT 재개로 더 빠름.
- Connection migration 지원.
- Head-of-line blocking 없음.
모바일과 Cloudflare 1.1.1.1이 지원. 아직 배포는 제한적이지만 방향성은 명확하다.
8.5 ODoH (Oblivious DoH) — 극한의 프라이버시
Cloudflare + Apple이 개발. DoH resolver가 내용은 복호화하지만 누가 물어봤는지 모름:
Client → Proxy (TLS) → Target Resolver
↑ ↑
IP 보임 내용 보임
(내용 모름) (IP 모름)
Client가 쿼리를 target resolver의 공개키로 암호화 → proxy를 통해 전달. Proxy는 client IP를 보지만 내용은 모름. Target resolver는 내용을 풀지만 client IP를 모름.
Apple Private Relay, Cloudflare ODoH가 실전 배포 중. 국가 단위 감시에 강한 저항.
8.6 Encrypted Client Hello (ECH)와의 조합
TLS/QUIC 글에서 다뤘던 ECH는 TLS 핸드셰이크의 SNI를 숨긴다. DoH + ECH 조합이면:
- DNS 조회가 암호화 → ISP는 어느 도메인 조회하는지 모름.
- TLS 핸드셰이크의 SNI 암호화 → ISP는 연결 중 어느 사이트인지 모름.
ISP에게는 "어딘가에 HTTPS 연결 중"만 보임. 진정한 프라이버시의 한 단계.
9. Anycast — 왜 CDN의 DNS가 빠른가
9.1 Unicast vs Anycast
- Unicast: IP 주소 하나 = 서버 하나. 평범한 모델.
- Anycast: 같은 IP 주소를 여러 지역의 서버가 광고. BGP 라우팅이 "가장 가까운" 서버로 연결.
Cloudflare 1.1.1.1
├── Tokyo PoP
├── Frankfurt PoP
├── Sao Paulo PoP
├── ... 300+ cities
Tokyo 사용자가 1.1.1.1에 쿼리 → BGP가 Tokyo PoP로 라우팅 → 수 ms 응답. 동시에 독일 사용자가 같은 IP에 쿼리 → Frankfurt PoP로 라우팅.
9.2 DNS에 적합한 이유
DNS 쿼리/응답은 매우 작고(UDP ~100B), 상태 없음(stateless). 어느 PoP에 도착해도 같은 답 제공 가능. Anycast의 "어느 노드든 답할 수 있음" 특성과 완벽한 궁합.
TCP connection-oriented 서비스는 anycast가 까다롭다 (route flap이 연결 끊김). 하지만 요청-응답 한 쌍이 UDP면 문제 없음.
9.3 13 Root Server — 안에 수천 개
"root server는 13개"라는 말은 논리적 이름 기준. 각 a~m root server는 실제로 anycast로 수백 개 PoP에 분산. 전체 물리 서버는 2000개 넘음.
그래서 어느 ISP에서도 root 쿼리가 수 ms에 해결. DNS의 전역 가용성과 성능이 anycast에 빚지고 있다.
9.4 Route 53, Cloudflare DNS의 현실
AWS Route 53는 전 세계 100+ locations에 anycast. 한 도메인의 NS 레코드 4개가 서로 다른 자리 조합 → 한 지역 장애에도 다른 지역으로 자동 페일오버.
Cloudflare는 이를 극단으로 밀어 300+ PoP. 도메인을 Cloudflare DNS로 옮기면 TTFB (Time To First Byte)가 체감상 달라진다.
9.5 ECS (EDNS Client Subnet)
Authoritative가 "요청자가 실제 어디 있는지"를 알면 지리 최적 라우팅 가능. Recursive resolver가 client IP의 /24를 authoritative에 전달하는 것이 ECS.
Recursive → Authoritative
"who's www.example.com, for client in 192.0.2.0/24?"
Authoritative가 해당 지역 CDN PoP의 IP를 반환. DNS 기반 지오 라우팅이 이걸로 동작.
프라이버시 trade-off: client IP의 일부가 authoritative까지 전달됨. 완전 프라이버시 원하면 ECS 비활성. 대신 라우팅 정확도 떨어짐.
10. CoreDNS — Kubernetes의 이름 서비스
10.1 CoreDNS의 위치
Kubernetes 안에서 my-service.my-namespace.svc.cluster.local 같은 이름을 해석하는 게 CoreDNS. kube-dns의 후계자이자 CNCF 졸업 프로젝트.
Pod의 /etc/resolv.conf:
search my-namespace.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10 # kube-dns ClusterIP
options ndots:5
my-service만 쿼리해도 search domain 덕에 my-service.my-namespace.svc.cluster.local이 해석됨.
10.2 플러그인 기반 아키텍처
CoreDNS는 Caddy 웹서버와 같은 저자(Miek Gieben)가 설계. 플러그인 체인 방식.
# Corefile
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
각 플러그인이 쿼리 처리 파이프라인의 한 단계. 필요한 것만 켜서 사용. kubernetes 플러그인이 Kubernetes API 서버에서 Service/Endpoint 정보를 받아 DNS 응답 생성.
10.3 ndots의 함정
ndots:5는 "이름에 점이 5개 미만이면 search 먼저". 그래서 google.com (점 1개)도 search domain 시도부터 한다:
google.com.my-namespace.svc.cluster.local → NXDOMAIN
google.com.svc.cluster.local → NXDOMAIN
google.com.cluster.local → NXDOMAIN
google.com → 실제 답
외부 호스트 조회마다 3–4번의 추가 쿼리. 대용량 트래픽에서 CoreDNS 부하 폭증. 개선책:
- Pod의
dnsConfig로ndots: 2(일반적으로 충분). - NodeLocal DNSCache (노드별 캐시 daemon).
- Stub domains로 외부 네임만 직접 외부로.
NodeLocal DNSCache는 최근 쿠버 설치의 기본. CoreDNS 부하를 노드별로 분산 + 캐시 → 효과가 극적.
10.4 Service의 종류와 DNS
- ClusterIP Service:
my-service.ns.svc.cluster.local→ ClusterIP A 레코드. - Headless Service (clusterIP=None): A 레코드가 Pod IP들을 직접 반환. StatefulSet에 자주 씀.
- ExternalName: CNAME 리턴. 외부 FQDN으로 매핑.
StatefulSet은 각 Pod에 고유 DNS 이름도 할당: pod-0.my-service.ns.svc.cluster.local.
10.5 DNS 디버깅
# Pod 안에서
nslookup my-service
nslookup my-service.other-namespace.svc.cluster.local
dig @10.96.0.10 my-service.ns.svc.cluster.local
# CoreDNS 로그 체크
kubectl logs -n kube-system deploy/coredns
"내 서비스가 안 보여요" 인시던트의 90%는:
- Service 이름/namespace 오타.
- Selector가 Pod의 label과 안 맞음.
- Endpoint가 비어있음 (Pod가 Ready 아님).
11. DNS 공격과 방어
11.1 Cache Poisoning
1990년대 고전 공격. Recursive resolver의 캐시에 위조 응답을 넣으면, 그 뒤로 모든 쿼리가 가짜 답으로 간다.
Kaminsky 공격 (2008): DNS 트랜잭션 ID는 16비트 → 공격자가 많은 가짜 응답을 보내면 타이밍 맞추기 가능. 공격 대상: recursive resolver.
완화책:
- Source port randomization (트랜잭션 ID 16-bit + src port 16-bit = 32-bit 추측 필요).
- DNSSEC 검증.
- 0x20 encoding (query name의 대소문자를 랜덤화 → 응답이 일치해야 유효).
11.2 DNS Amplification DDoS
공격자가 작은 쿼리로 큰 응답을 유발. 대상 IP로 응답이 쏟아짐.
공격자 → (spoofed src IP = 피해자) → DNS 서버
↓
피해자 ← (큰 응답)
증폭률 50–100배도 흔함. DNSSEC 응답이 특히 크다.
완화:
- Open resolver 제거 (authoritative만 외부에 노출).
- RRL (Response Rate Limiting): 같은 IP에서의 반복 쿼리 제한.
- BCP 38 (Ingress filtering): ISP가 spoofed src IP 필터링.
- DDoS scrubbing service (Cloudflare, Akamai 등).
11.3 Subdomain Takeover
예전에 CNAME으로 app.example.com → myapp.herokuapp.com을 설정했다가 Heroku 앱을 삭제. 공격자가 myapp을 다시 등록하면 app.example.com 트래픽을 가로챔.
예방:
- 사용 안 하는 CNAME은 삭제.
- Monitoring: 가리키는 target이 존재하는지 주기 체크.
- CAA 레코드로 인증서 발급 제한.
11.4 DNS Rebinding
브라우저의 same-origin policy 우회 공격. 공격자가 자기 도메인의 DNS A 레코드를 피해자 내부 IP(예: 192.168.1.1)로 빠르게 변경 → 브라우저가 공격자 도메인으로 페치한 JS가 피해자 내부 네트워크에 접근.
완화:
- 공공 recursive resolver의 "private IP 필터링" (1.1.1.1, 8.8.8.8 기본).
- 방화벽에서 DNS 응답의 private IP 필터링.
- 웹 앱에서 Host 헤더 검증.
11.5 DNS Tunneling
DNS 쿼리의 페이로드로 임의 데이터 전송. 원래는 방화벽 우회의 수단 (DNS는 거의 항상 열림).
Malware가 C2(Command & Control) 채널로 활용. 이상한 TXT 쿼리가 대량 발생하면 의심.
방어:
- DNS 트래픽 분석 (패킷 크기, 쿼리 패턴).
- 블랙리스트 기반 필터링.
- Cisco Umbrella, Cloudflare Gateway 같은 DNS 필터링 서비스.
12. 실전 운영
12.1 좋은 DNS 설계
- 여러 NS: 최소 2개, 다른 지리/네트워크. Route 53이나 Cloudflare 같은 전역 anycast 추천.
- 적절한 TTL: 일반 3600, 변경 전 낮춤.
- Wildcard 신중히:
*.example.com은 의도치 않은 이름도 매칭. - SPF/DKIM/DMARC: 이메일 인증. 없으면 스팸 등급 악화.
- CAA 레코드:
example.com. CAA 0 issue "letsencrypt.org"로 인증서 발급 제한. - DNSSEC: 가능하면 배포, 자동 서명 툴 활용.
12.2 모니터링
- NS 서버 가용성: 외부 probe로 다수 지역에서 체크.
- SOA serial: secondary가 primary와 일치하는지.
- 응답 시간: p99 체크. 비정상 시 anycast 라우팅 이슈 의심.
- NXDOMAIN 증가: 오타/공격/만료.
- DNSSEC 서명 만료: 유효기간 끝나면 SERVFAIL → 전체 다운.
12.3 주요 사고 패턴
- DNSSEC 키 롤오버 실수: parent의 DS와 child의 KSK가 어긋나면 모든 리졸버가 SERVFAIL. 사이트 완전 다운. KeyTrap (2024년 발견된 공격), KSK rollover 2018년 지연 등 실제 사고 많음.
- TTL 너무 큼 + 변경 필요: 마이그레이션 전날에 TTL 낮춰야 함. 새 IP로 72시간이 지나야 모두 반영.
- CNAME chain 너무 길거나 순환.
- Wildcard 중복:
*.example.com과*.a.example.com둘 다 있으면 의도치 않은 매칭. - apex CNAME 시도: 표준 아님. CNAME flattening 되는 제공자 사용 필수.
- Private IP leak: 내부용 도메인의 A 레코드가 public DNS에 실수로 공개.
12.4 도구들
- dig: 표준 도구.
dig example.com
dig +trace example.com # root부터 추적
dig +dnssec example.com # DNSSEC 레코드 포함
dig @1.1.1.1 example.com ANY # 특정 resolver 지정
dig -x 192.0.2.1 # reverse lookup
- drill: dig의 대안, NLnet Labs 것.
- kdig (Knot Resolver의 툴): 현대적 출력 포맷.
- mtr / traceroute: DNS와 함께 네트워크 경로 디버그.
- dnsperf: authoritative 부하 테스트.
12.5 마이그레이션 전략
DNS 제공자 이전 체크리스트:
- 현 TTL 파악. NS 레코드는 보통 2일.
- 몇 주 전 TTL 낮춤 (300–600초).
- 새 제공자에 모든 zone 복제.
- 외부에서 새 NS 응답 검증.
- Registrar에서 NS 변경.
- 이전 NS를 TTL * 2 이상 유지 (혹시 caching).
- 최종 확인 후 이전 NS 내림.
대형 마이그레이션은 한달 걸리는 게 정상. 조급함이 가장 큰 적.
13. 재미있는 일화들
13.1 Facebook 2021년 6시간 다운
2021년 10월 4일, Facebook/Instagram/WhatsApp이 6시간 다운. 원인: BGP 광고가 잘못 업데이트 → authoritative NS 서버가 인터넷에서 사라짐 → Facebook 전체 DNS가 해석 안 됨.
더 나쁜 건: 내부 복구 도구도 DNS에 의존했다. 엔지니어가 물리적으로 데이터센터에 가서 문제를 고쳐야 했다. 심지어 건물 출입 배지도 DNS에 의존해서 들어가지 못했다는 얘기까지 나왔다.
교훈: 복구 도구는 DNS에 의존하지 말 것.
13.2 DYN 2016
Mirai 봇넷이 Dyn(주요 DNS 제공자)을 1Tbps로 DDoS → GitHub, Twitter, Netflix, Reddit 등 수시간 다운. 하나의 DNS 제공자 의존이 단일 실패 지점.
그 이후 대형 기업들이 multi-DNS 전략 (Dyn + Route 53 + Cloudflare 병렬)을 채택.
13.3 Cloudflare 1.1.1.1의 사연
Cloudflare가 2018년 April Fool's Day에 1.1.1.1 발표. 이 IP의 기상천외한 역사:
- 너무 간단해서 예전에 많은 기업이 내부/테스트 용도로 사용.
- 실제로 Cloudflare가 공식 resolver로 만들기 전에 인터넷 곳곳에서 이상한 트래픽이 이미 이 IP로 향하고 있었음.
- Cloudflare가 APNIC과 파트너십으로 쓰게 됐고, 트래픽 패턴을 연구에 활용.
지금은 무료 공공 DoH/DoT resolver의 대표. 1.1.1.1 (primary), 1.0.0.1 (secondary).
맺음 — 보이지 않는 인프라
DNS는 인터넷의 가장 기초적이고 가장 보이지 않는 부분이다. 웹이 동작한다는 것의 전제이자, 모든 서비스의 첫 단계. 그러면서도 40년간 기본 설계를 바꾸지 않고 지금도 작동한다는 점에서 경이롭다.
운영자로서 본능적으로 체크할 세 가지:
- TTL을 계획의 일부로 두라. 변경은 TTL 시간에 반영된다. 마이그레이션 전 낮추고, 안정 후 올려라.
- DNS 서비스를 이중화하라. 단일 제공자 의존은 단일 실패 지점. Multi-NS, multi-provider.
- "It's always DNS"를 부정하지 말라. 문제 날 때 DNS부터 의심하라.
dig +trace가 첫 단계.
DNS를 이해하면 보이지 않던 인프라가 보인다. 그러면 인시던트 콜에서 먼저 답을 내는 사람이 된다.
다음 글은 CDN의 내부 구조 — Cache, Edge Compute, Origin Shield, Cache Key를 다룬다. DNS가 사용자를 어느 edge PoP으로 라우팅한 다음에 일어나는 일. Edge로 밀려 나가는 현대 웹의 아키텍처를 바이트 수준에서 그린다.
현재 단락 (1/423)
SRE 커뮤니티에는 오래된 격언이 있다: