Skip to content
Published on

DNS 내부 구조 완전 해부 — 재귀/권한, EDNS0, DNSSEC, DoH/DoT/DoQ, CoreDNS, Happy Eyeballs까지

Authors

들어가며 — "DNS 문제"라는 말의 무게

장애 회고에서 가장 자주 등장하는 문장:

"처음엔 앱 문제인 줄 알았는데, DNS였어요."

농담처럼 돌아다니는 말: "It's always DNS." 실제로 AWS 2019년 EC2 장애, Akamai 2021년 인터넷 셧다운, Facebook 2021년 6시간 다운 — 전부 DNS가 얽혀 있다.

왜 이렇게 자주 터지는가. 왜 Kubernetes 개발자는 "DNS가 느려요"라는 말을 한 달에 한 번은 듣는가. 이 글에서 해체해본다.

  • DNS 조회 한 번의 풀 경로 — 재귀 리졸버부터 13개 루트까지
  • CoreDNS 내부 — Kubernetes의 숨은 심장
  • Happy Eyeballs — IPv4/IPv6 경쟁의 예술
  • DNSSEC/DoT/DoH/DoQ — 보안과 프라이버시의 진화
  • 퍼블릭 리졸버 대전 — 1.1.1.1 vs 8.8.8.8 vs 9.9.9.9
  • 실전 장애 사례 3개
  • ndots=5 함정 — Kubernetes의 성능 killer

이전 글 OpenTelemetry 완전 해부에서 관측성의 신호를 이야기했다. DNS 지연은 그 신호로 포착하기 가장 어려운 영역 중 하나 — 대부분의 APM이 "getaddrinfo"를 애플리케이션 span으로 잡지 못하기 때문이다.


1. DNS의 본질 — 1983년의 타협

왜 이런 계층 구조인가

1983년 Paul Mockapetris가 DNS를 설계할 때의 환경: ARPANET의 모든 이름-IP 매핑이 HOSTS.TXT라는 한 파일에 있었다. SRI에서 관리하며 FTP로 배포. 호스트가 늘어나자 완전 불가능해짐.

해법: 분산된 계층 트리.

                          . (root)
                         / | \ ...
                       .com .org .kr
                       /    |      \
                  example  mozilla  co
                   / | \              \
                 www mail api         naver

각 노드가 자기 밑의 네임을 위임받은 **권한 서버(authoritative server)**를 가진다.

두 종류의 서버

  • 권한 서버 (Authoritative) — 특정 영역(zone)의 레코드를 가진 서버. 예: Cloudflare가 example.com의 권한 서버
  • 재귀 리졸버 (Recursive Resolver) — 클라이언트 대신 여러 권한 서버를 돌아다니며 답을 찾는 서버. 예: 8.8.8.8

클라이언트가 직접 루트 서버에 질의하지 않는 이유 — 너무 많은 질의가 루트에 몰릴 것. 재귀 리졸버가 캐싱하며 부하를 분산한다.

13개의 루트 서버 (진짜는 수천 개)

루트 서버는 A.root-servers.net ~ M.root-servers.net까지 13개다. 이건 이름이 13개라는 뜻이지, 서버가 13대라는 게 아니다. 각 네임 뒤에 Anycast로 수백 개의 실제 인스턴스가 전 세계에 분산돼 있다.

"왜 13개?" DNS 패킷의 UDP 최대 크기 512바이트에 모든 루트 서버 IP를 담으려면 13개가 한계였다. 지금은 EDNS0로 더 큰 패킷을 쓸 수 있지만 13개 숫자는 관례로 유지.


2. 조회 한 번의 풀 여정 — www.example.com

curl https://www.example.com을 쳤을 때 밀리초 단위로:

T+0ms — 브라우저/OS 캐시

  1. 브라우저 자체 캐시 확인 (Chrome은 chrome://net-internals/#dns)
  2. 없으면 OS 리졸버 (systemd-resolved, nscd 등) 캐시
  3. 없으면 /etc/hosts 확인
  4. 없으면 OS가 getaddrinfo 시스템 콜

T+0~5ms — /etc/resolv.conf 기반 재귀 리졸버로

nameserver 8.8.8.8
options ndots:5
search default.svc.cluster.local svc.cluster.local cluster.local

OS가 nameserver에 지정된 재귀 리졸버에 UDP/53으로 질의.

T+5~200ms — 재귀 리졸버의 일

리졸버에 www.example.com의 A 레코드가 캐시에 없다면:

  1. 루트 서버에 질의
    • ".com은 누구 담당?"
    • 루트 서버 응답: "com의 권한 서버는 a.gtld-servers.net 등"
  2. .com 권한 서버에 질의
    • "example.com은 누구 담당?"
    • 응답: "example.com의 권한 서버는 ns1.example.com 등"
  3. example.com 권한 서버에 질의
    • "www.example.com의 A는?"
    • 응답: "93.184.216.34"
  4. 리졸버가 클라이언트에게 답변 + TTL만큼 캐시

TTL이 300이면 5분간 같은 재귀 리졸버에 오는 질의는 캐시로 응답. 이게 루트 부하 관리의 비결이다.

T+200ms~ — TCP 연결

IP 받았으니 이제 TCP SYN부터 시작.


3. 레코드 타입 — 단순한 A 레코드만이 아니다

모던 DNS에는 수십 개의 타입이 있다. 중요한 것들:

타입용도예시
AIPv4 주소93.184.216.34
AAAAIPv6 주소2606:2800:220:1::...
CNAME다른 이름으로의 별칭www.example.com → example.com
MX메일 서버mail.example.com 우선순위 10
TXT임의 텍스트 (SPF, DKIM, 소유권 검증)v=spf1 include:_spf.google.com ~all
NS해당 영역의 네임서버ns1.example.com
SOA영역 시작serial, refresh, retry, expire
SRV서비스 위치 + 포트_sip._tcp.example.com
CAA인증서 발급 허가letsencrypt.org만 허용
HTTPS/SVCBHTTPS 설정 (ALPN, ECH)h3 ALPN, ech
PTRIP → 이름 (역방향)34.216.184.93.in-addr.arpa → ...
DNSKEY/DS/RRSIGDNSSEC 서명...

HTTPS/SVCB (2023 표준화)는 브라우저가 HTTPS 연결 세부사항을 DNS로 미리 받는 신규 표준. HTTP/3와 ECH(Encrypted Client Hello)를 배포하기 위한 핵심 인프라.


4. EDNS0 — 512바이트의 족쇄를 풀다

원래 DNS UDP는 512바이트 제한

RFC 1035(1987) 규정. 그 이상이면 TCP fallback. 느리고 방화벽에서 자주 막힘.

EDNS0 (RFC 6891, 2013 완성)

OPT 레코드로 확장. 클라이언트가 "나 4096바이트 UDP 받을 수 있어"라고 알림. 지금은 대부분의 리졸버가 기본 1232바이트(MTU 고려).

EDNS0가 중요한 이유:

  • DNSSEC 서명이 들어가면 응답이 커져서 필수
  • ECS (EDNS Client Subnet) — 리졸버가 클라이언트 네트워크를 권한 서버에 힌트로 전달. CDN이 정확한 지리적 응답 가능

5. DNSSEC — 답변을 위조할 수 없게

문제: 평문 DNS는 위조 가능

전통 DNS는 서명이 없다. 중간자가 "www.google.com은 실은 1.2.3.4야"라고 응답을 주입할 수 있다 (DNS Cache Poisoning, Kaminsky 공격 2008).

DNSSEC의 아이디어

각 영역이 공개키 서명으로 레코드를 보증. 체인 오브 트러스트:

Root key → .com key → example.com key → 레코드 서명
 (DS)       (DS)         (RRSIG)
  • DNSKEY — 공개키
  • RRSIG — 레코드 서명
  • DS — 부모가 자식의 키를 해시로 보증
  • NSEC/NSEC3 — "이 이름 존재 안 함"의 부정 증명

왜 널리 못 퍼졌나

  • 키 관리 복잡도
  • 레코드 크기 증가 (UDP fragmentation 이슈)
  • 리졸버 캐시와의 상호작용
  • TLD 가입 속도: .gov는 높지만 .com은 약 5%

2024년 기준 도메인의 약 10%만 DNSSEC 서명. 그래서 최근 관심은 DNSSEC보다 DoH/DoT 쪽으로 이동.


6. DoT / DoH / DoQ — 전송 계층의 혁명

왜 암호화가 필요한가

평문 DNS는:

  • ISP가 어떤 사이트를 방문했는지 훔쳐봄
  • 국가 차원의 검열(예: DNS 차단)
  • 광고 회사의 프로파일링

세 가지 진화

DoT (DNS over TLS) — RFC 7858, 2016

  • 포트 853
  • TCP + TLS
  • 전용 포트라 방화벽이 차단 가능

DoH (DNS over HTTPS) — RFC 8484, 2018

  • 포트 443 HTTPS
  • HTTP/2 또는 HTTP/3
  • 다른 HTTPS 트래픽과 구분 불가 → 검열 회피에 강함
  • 그러나 ISP 입장에서 가시성 상실 → 논란

DoQ (DNS over QUIC) — RFC 9250, 2022

  • QUIC (UDP 기반)
  • DoT의 지연 + DoH의 은닉성을 둘 다 해결
  • 모바일에서 연결 마이그레이션 이득

브라우저와 OS 지원

  • Firefox: DoH 기본 ON (미국) via Cloudflare
  • Chrome: Secure DNS 설정 ON 가능
  • Windows 11: DoH OS 레벨 지원
  • Android 9+: DoT 자동
  • iOS 14+: DoH/DoT profile

논란 — "중앙집권화"

DoH가 도입되면 ISP가 아닌 Cloudflare/Google에 DNS를 중앙 집중. 프라이버시냐 의존성이냐의 딜레마. Firefox는 "TRR (Trusted Recursive Resolver)" 정책으로 파트너 리졸버들을 필터링.


7. CoreDNS — Kubernetes의 DNS

왜 kube-dns에서 CoreDNS로?

Kubernetes 1.11(2018)부터 기본 DNS가 CoreDNS. 이유:

  • kube-dns: dnsmasq + kubedns + sidecar 3개 프로세스
  • CoreDNS: Go 기반 단일 바이너리, 플러그인 구조, 메모리 적음

플러그인 체인

CoreDNS의 설정 Corefile:

.:53 {
    errors
    health
    kubernetes cluster.local in-addr.arpa ip6.arpa {
      pods insecure
      fallthrough in-addr.arpa ip6.arpa
    }
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}

요청이 들어오면 플러그인을 위에서 아래로 통과. kubernetes 플러그인이 답을 못 주면 forward가 업스트림 DNS(예: 1.1.1.1)로 전달.

Kubernetes 내부에서 어떻게 동작하나

  • Pod가 /etc/resolv.confCoreDNS의 Service IP(예: 10.96.0.10)를 갖는다
  • Pod가 mysvc.default.svc.cluster.local을 질의하면 CoreDNS가 kubernetes 플러그인을 통해 API Server에서 Service IP를 조회 (실제론 informer 캐시)
  • 외부 도메인은 forward로 노드 resolv.conf의 리졸버로 전달

NodeLocal DNSCache — 필수 성능 튜닝

기본 구성은 매 Pod의 DNS 질의가 CoreDNS Pod로 NAT됨. kube-proxy의 conntrack 테이블이 터지거나, 질의가 많은 앱은 CoreDNS가 포화.

해결: NodeLocal DNSCache. 각 Node에 DNS 캐시 DaemonSet을 띄워 Pod가 127.0.0.1로 질의. 로컬 히트 시 CoreDNS까지 안 감. NAT 우회. conntrack 절약.

Kubernetes 1.18+ 권장 구성. 설치 한 번으로 DNS 지연 P99가 수십 ms에서 1ms로 떨어지는 경우가 흔하다.


8. ndots=5 — Kubernetes의 DNS 성능 함정

증상

Python 앱이 external-api.com을 호출. 대부분은 잘 되는데 가끔 지연 + 타임아웃.

원인

Pod의 /etc/resolv.conf:

search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

ndots:5"이름에 점(.)이 5개 미만이면, search 도메인을 먼저 붙여 시도".

external-api.com은 점이 1개. ndots 5 미만이므로 다음 순서로 질의:

  1. external-api.com.default.svc.cluster.local → NXDOMAIN
  2. external-api.com.svc.cluster.local → NXDOMAIN
  3. external-api.com.cluster.local → NXDOMAIN
  4. external-api.com → 성공

질의가 4배로 늘어난다. CoreDNS 부하, 지연 증가.

해결

방법 1: FQDN 사용 — 끝에 점을 찍기

urlopen("https://external-api.com.")  # 마지막 점이 FQDN 표시

방법 2: ndots 낮추기

spec:
  dnsConfig:
    options:
    - name: ndots
      value: "1"

방법 3: Pod의 dnsPolicy: None + 수동 구성

어느 방법이든 명시적으로 해야 한다. 기본값은 Kubernetes DNS 내부 조회를 위한 설계로, 외부 API 호출이 많은 앱에서는 역효과다.


9. Happy Eyeballs — IPv4 vs IPv6의 미학

문제: 이중 스택 지연

IPv4/IPv6 이중 스택 환경에서 브라우저가 IPv6를 먼저 시도했는데 IPv6 경로가 막힘 → 타임아웃까지 수 초 대기 → IPv4 재시도 → UX 악화.

해답: Happy Eyeballs (RFC 8305, 2017)

  1. A와 AAAA를 동시에 질의
  2. AAAA가 먼저 오면 IPv6 연결 시도
  3. 50ms 이내에 IPv6 연결이 안 되면 IPv4도 병렬로 시도
  4. 먼저 성립하는 걸 사용, 다른 건 폐기

구현은 curl, Chrome, Firefox, macOS, iOS 등 거의 모든 현대 스택에 들어있다.

v2의 개선 (2017)

  • 주소 정렬: GUA(Global) 우선, 그다음 IPv4, 그 다음 ULA
  • 캐시된 "최근 성공한 연결"을 기억해 다음번엔 바로 시작

이 알고리즘 덕에 IPv6 배포가 UX 손실 없이 진행될 수 있었다.


10. 퍼블릭 리졸버 대전

2018년 이후 "개인용 빠른 DNS" 시장이 열렸다.

주요 플레이어

리졸버특징
1.1.1.1 (Cloudflare + APNIC)속도 최강, 24시간 로그 삭제, DoH/DoT 공식
8.8.8.8 (Google)지연 안정적, 로그 48시간 보관
9.9.9.9 (Quad9)악성 도메인 차단, IBM + 글로벌 사이버 협회
208.67.222.222 (OpenDNS/Cisco)필터링 기반 (무료/유료 가족 모드)
94.140.14.14 (AdGuard)광고/추적 차단

선택 기준

  • 지연: 일반적으로 1.1.1.1이 가장 빠르지만 지역에 따라 다름. dnsperf로 실측 권장
  • 프라이버시: Cloudflare가 공개 감사 보고서로 가장 투명. Google은 역사적으로 로그 분석 이슈
  • 필터링: 멀웨어 차단이 필요하면 9.9.9.9

ISP vs 퍼블릭

많은 ISP가 DNS를 광고에 활용한다 (예: NXDOMAIN을 자기 검색 페이지로 리다이렉트). 퍼블릭 리졸버는 기술적으로 중립적이지만, 중앙집중의 우려가 있다.


11. 실전 장애 사례 3개

사례 1: AWS 2019년 Route 53 이벤트 (DNSSEC 관련)

Route 53이 일시적으로 DNSSEC 서명 체인 오류 → 특정 영역 조회 실패 → 거기 의존하던 서비스 연쇄 장애. 교훈: DNSSEC 활성화는 모니터링과 함께.

사례 2: Facebook 2021년 10월 6시간 다운

BGP 라우팅 실수로 페이스북의 권한 DNS 서버가 인터넷에서 사라짐. 외부에서 facebook.com을 조회하면 "권한 서버 응답 없음". 내부 직원도 페이스북 내부 도구가 DNS에 의존해서 복구도 지연.

교훈:

  • DNS 서버를 주력 제품과 같은 네트워크에 두지 말기
  • 비상 접근 경로 준비
  • 장애 복구 체계에 DNS 독립성 고려

사례 3: Akamai 2021년 7월 1시간 셧다운

Akamai Edge DNS의 소프트웨어 버그로 전 세계 DNS 조회 실패. 뉴욕증권거래소, 맥도날드, 영국항공 등 1000+개 고객사 장애. 교훈: DNS 공급자 다중화 (Route 53 + Cloudflare + NS1 등 2~3개 병행)를 대규모 서비스는 고려한다.


12. Zone 파일과 동적 DNS

Zone 파일의 고전 포맷

$TTL 3600
$ORIGIN example.com.
@      IN SOA ns1.example.com. admin.example.com. (
                2026041501 ; serial
                3600       ; refresh
                600        ; retry
                604800     ; expire
                3600 )     ; minimum
@      IN NS  ns1.example.com.
@      IN A   93.184.216.34
www    IN A   93.184.216.34
mail   IN A   93.184.216.35
@      IN MX  10 mail.example.com.

여전히 많은 권한 서버가 이 포맷으로 영역을 로드.

동적 DNS (DDNS)

  • nsupdate (RFC 2136) — DNS 메시지로 레코드 추가/삭제
  • API 기반 — Route 53, Cloudflare API
  • 동적 IP를 가진 홈서버/IoT 기기에서 활용

ExternalDNS (Kubernetes)

Kubernetes Service/Ingress의 외부 호스트를 자동으로 DNS 공급자에 레코드로 생성. Route 53/Cloudflare/GCP Cloud DNS 지원. GitOps 환경에서 Ingress hostname 관리의 정석.


13. 모니터링 — DNS의 보이지 않는 지연 잡기

메트릭

  • Query rate (QPS)
  • 응답 코드별 분포 (NOERROR, NXDOMAIN, SERVFAIL, REFUSED)
  • 지연 히스토그램 (P50, P99)
  • 캐시 hit ratio

로그

CoreDNS의 log 플러그인:

[INFO] 10.244.0.5:37321 - 12345 "A IN example.com.default.svc.cluster.local. udp 65 false 512" NXDOMAIN qr,aa,rd 158 0.000234s

NXDOMAIN이 많은 이름 → ndots 함정 의심.

도구

  • dig — 표준. dig +trace로 루트부터 경로 보기
  • dnsperf — 벤치마크
  • dnstap — 구조화된 DNS 로그 포맷
  • dnsviz.net — DNSSEC 체인 시각화

14. DNS 설계 실무 체크리스트 12가지

  1. TTL을 설계하라 — 짧으면 부하, 길면 배포 시 스위칭 지연. 배포 전 TTL 미리 줄이기
  2. NS 레코드 공급자 2개 이상 — 단일 공급자 장애 대비
  3. DNSSEC를 켤 거면 모니터링 필수 — 키 롤오버 실수가 전체 다운
  4. CNAME 루프 조심 — CNAME 체인이 깊으면 타임아웃
  5. SPF/DKIM/DMARC — 이메일 스푸핑 방지. 요즘 TXT 레코드 필수
  6. CAA 레코드 — 인증서 발급 가능한 CA 제한
  7. Wildcard의 위험*.example.com이 예상 외 서브도메인까지 잡음
  8. ECS(EDNS Client Subnet) 정책 — 리졸버가 클라이언트 IP 힌트를 권한 서버로 전달. CDN엔 유익, 프라이버시엔 논란
  9. Kubernetes는 NodeLocal DNSCache 필수 — 대규모에서 특히
  10. 앱에서 외부 도메인은 FQDN(뒤에 점) 쓰기 — ndots 함정 회피
  11. DoH/DoT 도입 전 내부 관측성 계획 — 평문 DNS에 의존하던 IDS가 먹통
  12. /etc/hosts 의존 지양 — 배포 환경마다 다른 예외의 원흉

다음 글 예고 — HTTP/3와 QUIC의 세계

DNS는 UDP 기반이지만 전통적으로 간단한 UDP만 썼다. 지금 웹의 최전선은 UDP 위에 쌓은 새 전송 프로토콜 QUIC이다. 다음 글에서는:

  • 왜 HTTP/2가 아직 한계인가 — HOL blocking의 그림자
  • QUIC의 0-RTT와 1-RTT 핸드셰이크
  • Stream multiplexing — TCP에서 불가능했던 것
  • Connection Migration — 와이파이에서 LTE로 끊김 없이
  • HTTP/3의 채택률 — Cloudflare, Google, Facebook의 실측
  • WebTransport — QUIC 위의 양방향 메시징
  • TCP BBR과 QUIC 혼잡 제어의 관계
  • QUIC의 비용 — 암호화 부담과 중간 장비 문제

1996년 HTTP/1.0 이후 30년, 전송 계층의 근본적 전환점을 짚어본다.

"QUIC은 TCP+TLS+HTTP/2의 장점만 추리고 TCP의 근본 문제를 우회한 사실상의 TCP 후계자다."