infra
Platform

모듈 맵

[Network] DNS 동작 과정과 A, CNAME, TXT 레코드 분석

0 / 35 완료

펼치기
0 / 35 완료0%

네트워크 트러블슈팅 · 04 / 35

[Network] DNS 동작 과정과 A, CNAME, TXT 레코드 분석

nslookup, dig로 DNS 질의를 추적하고 A/CNAME/TXT 레코드를 이해합니다

🚨INCIDENT ALERT
HIGH

서비스 도메인을 새로 연결했는데 어떤 사람은 접속되고 어떤 사람은 오래된 서버로 갑니다. IP는 바뀌었지만 DNS 레코드와 캐시가 어떻게 퍼지는지 몰라 원인 설명이 어렵습니다.

DNS는 단순한 이름표가 아니라 운영 장애의 흔한 출발점입니다. 레코드 타입과 질의 흐름을 직접 확인해야 합니다.

DNS 동작 원리와 레코드 타입

인터넷에서 google.com을 입력했을 때 실제 서버의 IP 주소로 연결되는 과정에는 **DNS(Domain Name System)**가 있습니다. DNS는 사람이 읽기 쉬운 도메인 이름을 컴퓨터가 사용하는 IP 주소로 변환하는 분산 계층 데이터베이스입니다. 이 챕터에서는 DNS의 동작 원리, 레코드 타입, 그리고 nslookupdig 도구로 직접 DNS 질의를 추적하는 방법을 배웁니다.


이번 챕터에서 배울 것
  • 1DNS 계층구조(Root → TLD → Authoritative NS)와 위임 원리를 설명할 수 있다
  • 2재귀(Recursive) 질의와 반복(Iterative) 질의의 차이를 구분할 수 있다
  • 3TTL(Time To Live)과 DNS 캐싱 및 전파 지연 원인을 이해할 수 있다
  • 4A, CNAME, MX, TXT, NS 등 핵심 DNS 레코드 타입을 구분할 수 있다
  • 5nslookup과 dig 명령어로 DNS 질의를 추적하고 디버깅할 수 있다
  • 6/etc/hosts와 /etc/resolv.conf로 로컬 DNS를 오버라이드할 수 있다
실습 환경 준비
dig 설치 확인 또는 설치 (DNS 질의 도구)
dig -v 2>&1 | head -1 || sudo apt-get install -y dnsutils
nslookup 사용 가능 여부 확인
nslookup google.com 2>&1 | head -3
현재 DNS 서버 설정 확인
cat /etc/resolv.conf
로컬 DNS 오버라이드 파일 확인
cat /etc/hosts
💡개념

DNS 계층구조와 재귀 질의 원리

새 도메인에 A 레코드를 추가했는데, 내 컴퓨터에서는 접속이 되지만 다른 사람 컴퓨터에서는 "서버를 찾을 수 없다"는 오류가 납니다. DNS 전파 지연 문제인데, 어디서 지연이 생기는지, 얼마나 기다려야 하는지 알 수가 없습니다. DNS가 계층 구조로 동작하는 원리를 모르면 레코드를 바꿀 때마다 이 불확실성을 그냥 감수해야 합니다.

DNS 계층구조와 재귀 질의 원리 — 도메인 조회는 Root → TLD(.com) → 권한 네임서버의 계층을 따라 내려감. 클라이언트의 리졸버(캐시 DNS)가 이 과정을 대신 밟아(재귀 질의) 최종 IP를 받아 캐시하고 응답. 각 단계 캐시(TTL)로 반복 조회를 줄임확대

DNS는 왜 계층 구조인가

전 세계 수십억 개의 도메인 정보를 한 서버에 저장하는 것은 불가능합니다. DNS는 이 문제를 계층적 위임(Hierarchical Delegation) 구조로 해결합니다.

                    [. (Root)]
                   /    |    \
             [.com]  [.org]  [.kr]   ← TLD (Top-Level Domain)
               /
        [example.com]               ← Authoritative NS
           /     \
       [www]    [mail]              ← 실제 호스트 레코드
  • Root NS: 전 세계 13개(실제로는 anycast로 수백 개) 서버. TLD를 어디서 관리하는지 알고 있음
  • TLD NS: .com, .kr, .org 등 최상위 도메인 관리. Authoritative NS 위치를 알고 있음
  • Authoritative NS: 특정 도메인(example.com)의 실제 레코드를 보유하는 서버

재귀 질의 과정 (www.example.com 조회 시)

브라우저에서 www.example.com을 입력하는 순간, 화면 뒤에서는 다음과 같은 대화가 진행됩니다. 당신의 컴퓨터는 답을 모르니 ISP가 제공하는 로컬 DNS 리졸버에게 물어봅니다. 리졸버도 캐시에 없으면 Root NS부터 차례로 위임 정보를 따라가며 최종 답을 찾아옵니다. 이 모든 과정이 보통 수십 밀리초 안에 완료됩니다.

DNS 재귀 질의 과정 — 클라이언트→Local Resolver→Root NS→.com TLD NS→Authoritative NS를 따라가며 위임을 받아 최종 IP를 찾는 흐름확대

Recursive vs Iterative 질의

DNS 질의 방식에는 두 종류가 있습니다. "끝까지 답을 찾아와 주는" 재귀 방식과, "저기 가서 물어봐"라며 다음 단계만 알려주는 반복 방식입니다. 실제 DNS 통신에서 이 두 방식이 역할을 나눠 사용됩니다.

방식설명사용 주체
Recursive (재귀)최종 답을 찾아올 때까지 대신 질의클라이언트 → Local Resolver
Iterative (반복)"저기에 물어봐"라고 위임 정보만 반환Local Resolver → Root/TLD/Authoritative

클라이언트는 Local Resolver에게 재귀 질의를 합니다. Local Resolver가 실제로 Root → TLD → Authoritative를 순차적으로(Iterative) 방문하여 답을 가져옵니다.

DNS 캐싱과 TTL

응답받은 레코드는 TTL(Time To Live) 동안 캐시에 저장됩니다. 캐시된 동안은 동일 질의에 대해 전체 계층 탐색 없이 즉시 응답합니다.

www.example.com A 93.184.216.34 TTL 3600
                               ↑
                         3600초(1시간) 동안 캐시 유지

DNS 전파 지연의 원인: 도메인 IP를 변경해도 이전 TTL 동안은 구 IP가 캐시에 남아 있습니다. TTL을 300(5분)으로 낮춘 후 변경하면 더 빠르게 전파됩니다.


이름 하나를 해석하는 전체 흐름 — 앱에서 A 레코드까지 8단계

💡개념

DNS 이름 해석 전체 흐름 — 앱의 getaddrinfo에서 권한 NS까지 8단계

앞에서 리졸버가 위임 체인을 밟는 원리를 봤다면, 이번엔 curl https://www.example.com 한 줄이 IP를 얻기까지 내 컴퓨터 안쪽에서 무슨 일이 먼저 일어나는지부터 끝까지 따라갑니다. 실제로 이름 해석은 리졸버로 나가기 전에 /etc/hosts·/etc/nsswitch.conf·/etc/resolv.conf라는 로컬 관문을 먼저 지납니다. 이 관문을 모르면 "dig는 되는데 앱은 안 되는" 증상 앞에서 원인을 못 찾습니다. 아래 8단계를 알면 어디서 막혔는지 한 단계로 좁힐 수 있습니다.

TEXT
[앱]  curl https://www.example.com
   │
   ① getaddrinfo("www.example.com") 호출        (앱은 libc 리졸버에 이름 해석을 맡김)
   │
   ② /etc/nsswitch.conf의 hosts: 순서 결정        (hosts: files dns → files 먼저)
   │
   ③ /etc/hosts 확인 (files 조회원)               (매치되면 즉시 반환, DNS로 안 감)
   │    미스 ↓
   ④ stub resolver가 /etc/resolv.conf의 nameserver로 재귀 질의 (RD=1)
   │
   ⑤ 리커시브 리졸버 캐시 확인                     (히트면 즉답 — 두 번째 조회가 빠른 이유)
   │    미스 ↓ (리졸버가 반복 질의로 위임 체인을 대신 밟음)
   ⑥ Root(.) NS  → ".com은 이 TLD 서버들"          (NS 리퍼럴)
   │
   ⑦ .com TLD NS → "example.com은 이 권한 NS"      (NS 리퍼럴)
   │
   ⑧ example.com 권한 NS → A 레코드 권위 응답       (authoritative answer)
   ▼
[리졸버]  A 93.184.216.34 를 앱에 반환 + TTL 동안 캐시 (다음 조회는 ⑤에서 즉답)

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① getaddrinfo앱은 DNS를 직접 말하지 않고 libc 리졸버에 이름 해석을 위임앱 자체 DNS 캐시(JVM 등)가 옛 IP를 물고 있으면 시스템은 정상인데 그 앱만 옛 IP
② nsswitch/etc/nsswitch.confhosts: 줄 순서로 어디부터 볼지 결정dns files 순서면 /etc/hosts가 무시됨 — 로컬 오버라이드가 안 먹음
③ /etc/hostsfiles 조회원. 매치되면 DNS로 가지 않고 즉시 그 IP 반환옛 테스트 항목이 남으면 실제 DNS와 다른 IP로 고정 — nslookup은 되는데 엉뚱한 IP
④ stub→resolv.conf/etc/resolv.confnameserver로 재귀 질의(RD=1) 전송resolv.conf가 비었거나 틀린 리졸버면 전부 실패 / systemd-resolved가 다른 DNS를 씀
⑤ 리졸버 캐시리커시브 리졸버가 히트면 즉답, 미스면 위임 체인을 대신 순회옛 A(양성 TTL)나 옛 NXDOMAIN(부정 캐시)이 남아 전파 지연·미반영
⑥ Root NS.에 물어 .com TLD NS 목록을 위임(리퍼럴)받음리졸버가 root hints를 못 읽거나 53 차단 → 위임 시작 불가
⑦ TLD NS.com에 물어 example.com의 권한 NS를 위임받음등록대행사 NS 위임이 틀리면 엉뚱한 권한 NS로 감
⑧ 권한 NS존을 보유한 권한 NS가 A 레코드를 권위 있게 응답레코드 없음 → NXDOMAIN(SOA minimum만큼 부정 캐시) / 존 오설정 → SERVFAIL

"이름 해석 성공"은 세 관문을 모두 통과했다는 뜻입니다 — 로컬 경로(②③④)에서 DNS까지 갔고, 리졸버(⑤)가 위임 체인(⑥⑦)을 밟아, 권한 NS(⑧)가 권위 응답을 냈습니다. 안 될 때는 이 셋을 각각 다른 도구로 좁힙니다: getent hosts www.example.com은 ①②③(nsswitch·hosts 로컬 경로)을, dig @8.8.8.8과 로컬 dig 비교는 ④⑤(내 리졸버 vs 전체)를, dig +trace는 ⑥⑦⑧(위임 체인)을 검사합니다. 이름→IP를 찾는 것이 정방향(A/AAAA), IP→이름을 찾는 것이 **역방향(PTR, in-addr.arpa)**으로 방향만 반대일 뿐 같은 계층을 탑니다. 그리고 "레코드를 만들었는데 안 보임"은 레코드 자기 TTL이 아니라 **SOA minimum(부정 캐시 TTL)**이 지배한다는 점을 기억하면 ⑧에서 헤매지 않습니다.


💡개념

DNS 레코드 타입 완전 정복

새 서비스를 서브도메인으로 분리하려는데 도메인 관리 패널에 A, CNAME, MX, TXT가 가득합니다. CNAME으로 연결하면 되는 상황인지, A 레코드를 직접 추가해야 하는지, CNAME을 루트 도메인에 쓰면 왜 안 되는지 헷갈립니다. 각 레코드 타입의 용도와 제약을 모르면 도메인 설정 한 번에 삽질을 반복하게 됩니다.

DNS 레코드 타입 — A(도메인→IPv4)·AAAA(→IPv6)·CNAME(별칭→다른 도메인)·MX(메일 서버)·TXT(SPF·검증)·NS(권한 네임서버)·PTR(IP→도메인 역방향). 용도에 맞는 레코드를 골라야 하며, CNAME은 루트 도메인에 못 쓰는 등 타입별 제약이 있음확대

핵심 레코드 타입

DNS 레코드는 타입마다 역할이 다릅니다. 웹 서버 주소를 가리키는 A 레코드부터, 이메일 서버를 지정하는 MX, 도메인 소유권 인증에 쓰는 TXT까지 — 각 타입이 어떤 목적에 쓰이는지 알면 도메인 설정 화면이 더 이상 낯설지 않습니다.

A 레코드 — 도메인 → IPv4

example.com.     3600  IN  A  93.184.216.34
www.example.com. 3600  IN  A  93.184.216.34

가장 기본적인 레코드입니다. 하나의 도메인에 여러 A 레코드를 등록하면 라운드 로빈 DNS 로드밸런싱이 됩니다.

AAAA 레코드 — 도메인 → IPv6

example.com.  3600  IN  AAAA  2606:2800:220:1:248:1893:25c8:1946

CNAME 레코드 — 도메인 별칭

www.example.com.   3600  IN  CNAME  example.com.
blog.example.com.  3600  IN  CNAME  example.com.

www.example.com을 조회하면 → example.com을 다시 조회 → A 레코드로 IP를 반환합니다.

주의: CNAME은 다른 레코드와 공존할 수 없습니다. 루트 도메인(example.com)에는 CNAME을 사용할 수 없고 A 레코드나 ALIAS 레코드를 사용해야 합니다.

MX 레코드 — 메일 서버

example.com.  3600  IN  MX  10  mail1.example.com.
example.com.  3600  IN  MX  20  mail2.example.com.

숫자(Priority)가 낮을수록 우선순위가 높습니다. mail1이 응답 없을 때 mail2로 폴백됩니다.

TXT 레코드 — 텍스트 정보

example.com.  IN  TXT  "v=spf1 include:_spf.google.com ~all"
example.com.  IN  TXT  "google-site-verification=abc123xyz"

주요 용도:

  • SPF: 이메일 발송 서버 인증 (스팸 방지)
  • DKIM: 이메일 서명 키 공개
  • 도메인 소유권 인증: Google Search Console, AWS ACM 인증서 등

NS 레코드 — Authoritative Name Server

example.com.  IN  NS  ns1.example.com.
example.com.  IN  NS  ns2.example.com.

해당 도메인의 Authoritative NS를 지정합니다. 도메인 등록사에서 설정합니다.

SOA 레코드 — Zone의 시작 (Start of Authority)

example.com.  IN  SOA  ns1.example.com. admin.example.com. (
    2024010101  ; Serial
    3600        ; Refresh
    900         ; Retry
    604800      ; Expire
    300         ; Minimum TTL
)

Zone 파일의 첫 번째 레코드로, 해당 도메인의 주 네임서버, 관리자 이메일, Zone 업데이트 정보를 담습니다.

레코드 타입 비교표

자주 마주치는 레코드 타입을 한 표로 정리하면 DNS 설정 화면에서 어떤 타입을 선택해야 할지 빠르게 판단할 수 있습니다.

레코드역할값 예시
A도메인 → IPv493.184.216.34
AAAA도메인 → IPv62606:2800::1
CNAME도메인 → 도메인 별칭example.com.
MX메일 서버 지정10 mail.example.com.
TXT텍스트 (SPF/인증)"v=spf1 ..."
NSAuthoritative NSns1.example.com.
PTRIP → 도메인 (역방향)example.com.
SRV서비스 위치0 5 443 example.com.
DNS 응답 코드 판단 — NXDOMAIN·SERVFAIL·TIMEOUT 무엇이 다른가
NXDOMAINDNS는 정상 동작했고 '그 이름은 존재하지 않는다'는 권위 있는 답. 오타·미등록 레코드·삭제된 도메인. 서버 문제가 아니라 '이름 자체가 없음' — 레코드 등록 여부부터
SERVFAIL서버가 답을 만들지 못함 — 상위 위임 깨짐, DNSSEC 검증 실패, 권위 서버 다운. dig +trace로 어느 위임 단계에서 막혔는지 추적. '이름은 있을 수 있는데 서버가 못 푼다'
응답 자체가 없음 (timeout)DNS 서버에 도달 못함 — 53번 차단, 리졸버 다운, 네트워크 단절. dig @8.8.8.8로 다른 리졸버 시도해 '내 리졸버 문제 vs 전체 문제' 구분
NOERROR인데 원하는 레코드가 없음이름은 있으나 그 타입 레코드가 없음(예: A는 있는데 AAAA 없음). ANSWER SECTION 비어있으면 타입 확인 — 'NOERROR=성공이지 데이터 있음이 아님'
조회마다 답이 다르다여러 권위 서버 간 불일치(전파 지연) 또는 GeoDNS·라운드로빈. TTL 만료 전 캐시와 신규 답이 섞임 — 'dig +trace로 권위 답'을 기준으로
변경했는데 반영 안 됨TTL만큼 캐시 잔존 — 옛 TTL이 3600이면 최대 1시간. 변경 전 TTL을 미리 낮추는 게 정석. '바꾸고 기다리는 시간 = 직전 TTL'

nslookup으로 DNS 레코드 조회하기

목표

nslookup 명령어로 다양한 DNS 레코드 타입을 조회하고, 응답 구조를 이해합니다.

전제 조건

  • 인터넷 접속 가능한 환경
  • nslookup 설치 (dnsutils 패키지 포함)
로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part1/exam_4 && cd /tmp/networking/part1/exam_4

# 설치 확인
$ which nslookup
# 없으면: sudo apt install dnsutils (Ubuntu)
#         sudo yum install bind-utils (CentOS)
🔍실행 후 확인할 것
  • status 코드 — NOERROR vs NXDOMAINdig 출력 상단 ;; ->>HEADER<<- 줄에서 status 값을 먼저 확인 — NOERROR면 조회 성공, NXDOMAIN이면 도메인 없음(오타 또는 DNS 전파 미완료)
  • ANSWER SECTION 존재 여부ANSWER SECTION이 없고 AUTHORITY SECTION만 있으면 네임서버를 찾았지만 레코드가 없는 것 — 레코드 유형(A/CNAME/MX)이 맞는지 확인
  • Query time 수치 판단Query time이 5ms 이하면 캐시 응답, 100ms 이상이면 네임서버까지 직접 조회한 것 — 반복 조회 시 첫 번째보다 두 번째가 빠르면 캐싱 정상 동작

단계별 실습

1단계: 기본 A 레코드 조회

로컬 터미널
$ nslookup google.com

예상 출력:

Server:   127.0.0.53          ← 질의한 DNS 서버 (로컬 리졸버)
Address:  127.0.0.53#53

Non-authoritative answer:     ← 캐시에서 응답 (Authoritative NS에서 직접 받은 것이 아님)
Name:    google.com
Address: 142.250.196.110      ← A 레코드 (IPv4)

2단계: 특정 레코드 타입 조회

로컬 터미널
# MX 레코드 조회
$ nslookup -type=MX google.com

# 출력:
# google.com  mail exchanger = 10 smtp.google.com.

# TXT 레코드 조회 (SPF, 도메인 인증 등)
$ nslookup -type=TXT google.com

# NS 레코드 조회
$ nslookup -type=NS google.com

# CNAME 조회
$ nslookup -type=CNAME www.github.com

3단계: 특정 DNS 서버 지정 조회

로컬 터미널
# Google DNS(8.8.8.8)에 직접 질의
$ nslookup google.com 8.8.8.8

# Cloudflare DNS(1.1.1.1) 사용
$ nslookup google.com 1.1.1.1

# 회사 내부 DNS 서버에서 내부 도메인 조회
$ nslookup internal-app.company.local 10.0.0.53

4단계: 역방향 DNS 조회 (PTR)

로컬 터미널
# IP → 도메인 역조회
$ nslookup 8.8.8.8

# 출력 예시:
# 8.8.8.8.in-addr.arpa  name = dns.google.

5단계: 응답 해석

로컬 터미널
$ nslookup -type=any github.com 8.8.8.8

Non-authoritative answer: 캐시에서 응답 — TTL이 줄어들고 있는 상태

Authoritative answer: Authoritative NS에서 직접 응답


dig +trace로 DNS 위임 체인 추적하기

목표

dig +trace 명령어로 Root NS → TLD NS → Authoritative NS까지 전체 DNS 질의 경로를 직접 추적하고, 각 단계의 응답을 분석합니다.

전제 조건

로컬 터미널
$ which dig
# 없으면: sudo apt install dnsutils

단계별 실습

1단계: 기본 dig 질의

로컬 터미널
$ dig google.com

주요 섹션 설명:

;; QUESTION SECTION:          ← 질의 내용
;google.com.  IN  A

;; ANSWER SECTION:            ← 최종 응답
google.com.  253  IN  A  142.250.196.110
             ↑
             TTL 남은 시간(초)

;; Query time: 3 msec         ← 응답 시간
;; SERVER: 127.0.0.53#53      ← 응답한 DNS 서버

2단계: dig +trace — 전체 위임 체인 추적

로컬 터미널
$ dig +trace google.com

출력 예시 (단계별 설명):

; <<>> DiG 9.16 <<>> +trace google.com

# Root NS에서 .com TLD NS 목록 받기
.            518400  IN  NS  a.root-servers.net.
.            518400  IN  NS  b.root-servers.net.
;; Received 525 bytes from 127.0.0.53#53

# .com TLD NS에서 google.com Authoritative NS 받기
com.         172800  IN  NS  a.gtld-servers.net.
;; Received 1170 bytes from 198.41.0.4#53(a.root-servers.net)

# google.com Authoritative NS에서 최종 A 레코드 받기
google.com.  300     IN  A  142.250.196.110
;; Received 55 bytes from 216.239.32.10#53(ns1.google.com)

3단계: 특정 레코드 타입 조회

로컬 터미널
# MX 레코드
$ dig MX google.com

# TXT 레코드 (SPF 확인)
$ dig TXT google.com

# NS 레코드
$ dig NS google.com +short   ← +short: 간단한 출력

# CNAME 추적
$ dig CNAME docs.github.com

4단계: TTL 확인

로컬 터미널
# 동일 질의를 2회 실행하여 TTL 감소 확인
$ dig A google.com | grep "ANSWER SECTION" -A 1
# TTL 값 기록

# 30초 후 재조회
$ dig A google.com | grep "ANSWER SECTION" -A 1
# TTL이 약 30 감소했는지 확인

5단계: 응답 시간 비교

로컬 터미널
# 첫 번째 질의 (캐시 없음)
$ dig @8.8.8.8 example.com
# Query time: 50 msec (Authoritative까지 다녀옴)

# 두 번째 질의 (캐시 있음)
$ dig @8.8.8.8 example.com
# Query time: 1 msec (캐시에서 즉시 반환)

/etc/hosts로 로컬 DNS 오버라이드 설정

목표

/etc/hosts 파일을 수정하여 특정 도메인이 DNS 대신 지정한 IP로 해석되도록 설정합니다. 개발 환경 테스트나 임시 우회에 활용됩니다.

시나리오

새로운 서버(192.168.1.100)에 서비스를 배포하기 전, api.myapp.com이 새 서버를 가리키도록 로컬에서 미리 테스트하고 싶습니다.

단계별 실습

1단계: 현재 /etc/hosts 내용 확인

로컬 터미널
$ cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   myserver
::1         localhost ip6-localhost

2단계: 호스트 항목 추가

로컬 터미널
# /etc/hosts에 항목 추가
$ sudo vi /etc/hosts

# 다음 줄 추가:
192.168.1.100   api.myapp.com

또는 명령어로 직접 추가:

로컬 터미널
$ echo "192.168.1.100 api.myapp.com" | sudo tee -a /etc/hosts

3단계: 동작 확인

로컬 터미널
# hosts 파일이 적용되었는지 확인
$ nslookup api.myapp.com
# Server: 127.0.0.53
# Name:   api.myapp.com
# Address: 192.168.1.100   ← /etc/hosts 값이 반환됨

# ping으로 확인
$ ping -c 2 api.myapp.com
# PING api.myapp.com (192.168.1.100)

4단계: nsswitch.conf 조회 순서 확인

로컬 터미널
$ grep hosts /etc/nsswitch.conf
# hosts: files dns
#           ↑    ↑
#         먼저  그다음

# 'files'가 'dns'보다 앞에 있으므로 /etc/hosts가 우선

5단계: 테스트 후 항목 제거

로컬 터미널
$ sudo vi /etc/hosts
# 추가했던 줄 삭제

# 제거 확인
$ nslookup api.myapp.com
# 이제 실제 DNS 결과가 반환됨

활용 사례

  • 개발 환경에서 localhost에 도메인 이름 할당
  • 프로덕션 배포 전 새 서버 검증
  • 특정 사이트 차단 (0.0.0.0 ads.example.com)
  • /etc/hosts를 사용하는 도커 컨테이너 내부 서비스 명 해석

증상

도메인의 A 레코드를 새 IP(203.0.113.50)로 변경했는데, 일부 서버나 클라이언트에서 계속 구 IP(198.51.100.10)로 접속합니다.

로컬 터미널
$ dig A myapp.com @8.8.8.8
# myapp.com. 300 IN A 203.0.113.50  ← 구글 DNS에서는 새 IP

$ nslookup myapp.com
# Address: 198.51.100.10  ← 로컬 리졸버에서는 구 IP!

원인: DNS 캐시

이전 TTL 값이 높았다면 (예: TTL=86400, 24시간), 변경 전에 캐시된 레코드가 최대 24시간 동안 유지됩니다.

[변경 전 레코드]
myapp.com. 86400 IN A 198.51.100.10
           ↑
           86400초 = 24시간 캐시!

해결 방법

1. 로컬 DNS 캐시 초기화

위험 명령어

이 명령은 프로세스를 종료해 연결 중인 사용자나 배치 작업을 중단시킬 수 있습니다. PID와 프로세스 이름이 목표 서비스인지 확인한 뒤 실행하세요.

로컬 터미널
# Linux (systemd-resolved)
$ sudo systemd-resolve --flush-caches
$ sudo systemd-resolve --statistics   # 캐시 상태 확인

# macOS
$ sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder

# Windows
> ipconfig /flushdns

2. 특정 DNS 서버 우회 질의

로컬 터미널
# Authoritative NS에 직접 질의 (캐시 없음)
$ dig A myapp.com @ns1.myapp.com +short
# 203.0.113.50  ← 최신 값 확인 가능

3. 여러 DNS 서버에서 전파 상태 확인

로컬 터미널
# 다양한 DNS 서버에서 동시 확인
$ dig A myapp.com @8.8.8.8 +short    # Google
$ dig A myapp.com @1.1.1.1 +short    # Cloudflare
$ dig A myapp.com @9.9.9.9 +short    # Quad9
$ dig A myapp.com @208.67.222.222 +short  # OpenDNS

4. 사전 예방: TTL 미리 낮추기

DNS 변경 24~48시간 전에 TTL을 300(5분)으로 낮춰두면 전파 대기 시간을 크게 줄일 수 있습니다:

변경 48시간 전: TTL을 300으로 낮춤
변경 당일: A 레코드 IP 변경 (최대 5분 내 전파)
변경 후: TTL을 다시 3600으로 올림

예방 방법

중요한 서비스 이전 작업 전에는 항상 현재 TTL을 확인하고 충분한 전파 대기 시간을 계획에 포함하세요.


증상

로컬 터미널
$ nslookup myapi.internal.com
# Address: 10.100.5.20  ← DNS 조회 성공

$ curl http://myapi.internal.com
# curl: (6) Could not resolve host: myapi.internal.com
# 또는 연결 타임아웃

원인별 진단

원인 1: /etc/resolv.conf와 실제 리졸버 불일치

로컬 터미널
# /etc/resolv.conf 확인
$ cat /etc/resolv.conf
nameserver 10.0.0.53   ← 내부 DNS

# nslookup이 사용하는 DNS 서버 확인
$ nslookup myapi.internal.com
# Server: 10.0.0.53     ← 내부 DNS 사용 중 → 해석 성공

# 시스템이 실제로 사용하는 DNS 확인 (systemd-resolved 환경)
$ systemd-resolve --status | grep "DNS Servers"
# DNS Servers: 8.8.8.8   ← 외부 DNS! 내부 도메인 해석 불가

systemd-resolved/etc/resolv.conf를 무시하고 DHCP로 받은 DNS를 사용하는 경우입니다.

해결: 아래 순서로 DNS 설정을 확인하고 복구합니다.

위험 명령어

이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.

로컬 터미널
$ sudo vi /etc/systemd/resolved.conf
# DNS=10.0.0.53
# FallbackDNS=8.8.8.8
$ sudo systemctl restart systemd-resolved

원인 2: hosts 파일과 nsswitch 순서 문제

로컬 터미널
$ cat /etc/nsswitch.conf | grep hosts
# hosts: dns files   ← dns가 먼저! hosts 파일이 무시됨

# 정상 순서로 변경
# hosts: files dns

원인 3: 애플리케이션의 별도 DNS 설정

도커 컨테이너, JVM 애플리케이션은 자체 DNS 설정을 가질 수 있습니다:

로컬 터미널
# 도커 컨테이너의 DNS 확인
$ docker exec mycontainer cat /etc/resolv.conf

# JVM DNS 캐시 확인 (Java 애플리케이션)
# JVM은 기본적으로 성공한 DNS 조회를 무기한 캐싱합니다.
# -Dnetworkaddress.cache.ttl=30 JVM 옵션으로 TTL 설정 필요

진단 명령어 모음

로컬 터미널
# 시스템이 실제 사용하는 DNS 확인
$ systemd-resolve --status
$ cat /etc/resolv.conf
$ nmcli dev show | grep DNS

# 특정 DNS 서버로 직접 테스트
$ dig @10.0.0.53 myapi.internal.com
$ dig @8.8.8.8 myapi.internal.com

심화 — DNS가 조용히 실패하는 두 지점: 부정 캐싱과 UDP 512바이트

💡개념

심화: 양성 TTL 뒤에 숨은 함정 — 부정 캐싱과 응답 크기 한계

레코드의 TTL(양성 캐싱)은 이제 이해했더라도, '레코드를 만들었는데도 안 보이거나' '특정 도메인만 간헐 실패하는' 증상은 전혀 다른 원리에서 나옵니다. 둘 다 dig 한 번은 성공하기에 넘어가기 쉽습니다.

  • 부정 캐싱(negative caching): 리졸버는 '있다'는 답만이 아니라 '없다(NXDOMAIN)'는 답도 캐시합니다. 그 유지 시간은 새로 만든 레코드의 TTL이 아니라, 그 존(zone)이 함께 돌려주는 SOA 레코드의 minimum(negative TTL) 이 지배합니다. 그래서 SOA minimum이 3600이면, 이름이 없던 시절 NXDOMAIN을 받은 리졸버는 레코드를 만든 뒤에도 최대 1시간 '없음'을 계속 답합니다. '레코드를 만들기 전에 미리 조회해 보는' 습관이 이 함정을 스스로 심는 흔한 방법입니다.
  • UDP 512바이트와 TCP 폴백: DNS 질의는 기본이 UDP 53입니다. 전통적으로 응답이 512바이트를 넘으면 서버가 TC(truncated) 비트를 세우고, 클라이언트는 같은 질의를 TCP 53으로 다시 던집니다. EDNS0로 더 큰 UDP 응답을 협상하지만(4096 등), 그 큰 UDP 데이터그램은 경로 MTU를 넘겨 IP 조각화(fragment)될 수 있습니다.
  • 그래서 '레코드가 많은 도메인만' 실패한다: 방화벽이 UDP 53만 열고 TCP 53이나 큰 UDP 조각을 막으면, TXT·DNSSEC가 많아 응답이 큰 도메인만 폴백에 실패해 간헐 장애가 납니다. 짧은 응답 도메인은 폴백이 필요 없어 늘 정상이라 원인을 놓치기 쉽습니다.
  • 진단의 관점 전환: '레코드가 맞는데 왜 클라이언트가 못 받나'를 볼 때는, 레코드 자기 TTL만 보지 말고 SOA minimum(부정 캐싱)과 응답 크기·TC 비트(UDP/TCP)를 함께 봅니다. dig +tcp, dig +bufsize=512, SOA의 마지막 필드가 이때 쓰는 도구입니다.

즉 DNS는 '이름→IP 표'가 아니라 캐시·프로토콜 한계를 가진 시스템이고, 조용한 실패는 대개 이 두 지점에서 납니다.

상황: 사내 리졸버를 거치는 서버들이, 응답이 큰 특정 외부 도메인만 간헐적으로 SERVFAIL이나 timeout을 냅니다. 짧은 응답 도메인(A 한두 개)은 100% 정상이고, 재시도하면 되기도 해서 '네트워크가 가끔 느린가' 정도로 넘어가기 쉽습니다.

원인: 그 도메인의 응답이 UDP 크기 한계(전통적 512바이트, EDNS0로 협상한 크기)를 넘겨 TC 비트로 잘립니다. 그러면 리졸버가 TCP 53으로 폴백해야 하는데, 중간 방화벽이 TCP/53 또는 큰 UDP 데이터그램의 IP 조각을 막고 있어 폴백이 실패합니다. 응답이 작은 도메인은 TCP 폴백이 필요 없어 늘 정상이라, '특정 도메인만'이라는 편향이 생깁니다.

진단: dig +tcp <도메인>은 성공하는데 기본(UDP) 질의가 실패하면 폴백 경로가 막힌 것입니다. dig +bufsize=512 <도메인>으로 응답에 TC 비트(flags에 tc)가 서는지, 응답 크기가 얼마인지 확인합니다. 방화벽 룰에서 DNS를 UDP 53만 열어 둔 오래된 설정이 있는지 점검합니다.

해결: 방화벽에서 DNS는 UDP 53과 TCP 53을 모두 허용합니다(레거시 룰이 UDP만 여는 경우가 많습니다). 큰 UDP 응답의 IP fragment도 통과시키거나, 경로 MTU에 맞춰 리졸버의 EDNS bufsize를 낮춰 조각화를 피합니다. 근본 교정은 'DNS는 UDP만 쓴다'는 오해를 버리는 것입니다(CentOS/Ubuntu 서버 방화벽(firewalld / ufw) 포트 열기에서 두 프로토콜을 함께 여는 법을 확인).


💼
실무 맥락
현업 패턴

서비스 이전 작업의 DNS 전략

신규 서버로 트래픽을 점진적으로 이전할 때 DNS TTL을 활용합니다.

실제 작업 순서: 도메인 전파 작업 시 권장하는 절차입니다.

로컬 터미널
# 1. 현재 TTL 확인
$ dig A myapp.com +short
$ dig A myapp.com | grep "ANSWER SECTION" -A 1
# myapp.com. 3600 IN A 198.51.100.10
#            ↑ TTL=3600 (1시간)

# 2. 작업 24시간 전: TTL을 300으로 낮춤 (DNS 관리 콘솔에서)

# 3. TTL 감소 확인 (24시간 후)
$ dig A myapp.com | grep "ANSWER SECTION" -A 1
# myapp.com. 290 IN A 198.51.100.10
#            ↑ TTL이 줄어들고 있음

# 4. IP 변경 (이제 최대 5분 내 전파)

# 5. 전파 확인
$ dig A myapp.com @8.8.8.8 +short
$ dig A myapp.com @1.1.1.1 +short
# 모두 새 IP 반환 시 작업 완료

TXT 레코드로 도메인 소유권 인증

AWS Certificate Manager에서 SSL 인증서 발급 시:

로컬 터미널
# AWS가 요청하는 TXT 레코드 추가 후 확인
$ dig TXT _acme-challenge.myapp.com
# _acme-challenge.myapp.com. IN TXT "abc123xyz..."

# 레코드가 전파되면 AWS가 자동으로 인증 완료

이 기술이 필요한 직군

직군DNS 활용 상황
DevOps/SRE서비스 이전, 블루/그린 배포 트래픽 전환, 인증서 발급 자동화
백엔드 개발자마이크로서비스 서비스 디스커버리, 내부 도메인 설정
인프라 엔지니어내부 DNS 서버(BIND, CoreDNS) 운영, split-horizon DNS
보안 엔지니어DNS 스푸핑 방지, DNSSEC 설정, DNS 로그 분석

알아두면 차별화되는 지식

쿠버네티스 CoreDNS

쿠버네티스 클러스터 내부 서비스 이름 해석은 CoreDNS가 담당합니다:

myservice.default.svc.cluster.local
   ↑         ↑         ↑
서비스명  네임스페이스  고정 도메인

서비스 간 통신 문제는 CoreDNS 로그와 nslookup으로 진단합니다:

로컬 터미널
# 쿠버네티스 Pod에서 서비스 DNS 확인
$ kubectl exec -it debug-pod -- nslookup myservice.default.svc.cluster.local
$ kubectl exec -it debug-pod -- cat /etc/resolv.conf

Split-Horizon DNS

같은 도메인이 내부 네트워크에서는 사설 IP, 외부에서는 공인 IP로 응답해야 할 때 사용합니다:

외부 클라이언트 → api.myapp.com → 203.0.113.50 (공인 IP)
내부 서버       → api.myapp.com → 10.0.0.100  (사설 IP)

VPN이나 사내망 환경에서 자주 구성하는 패턴입니다.


핵심 명령어 요약

로컬 터미널
# nslookup 기본 조회
nslookup [도메인]
nslookup -type=MX [도메인]
nslookup -type=TXT [도메인]
nslookup [도메인] [DNS서버 IP]   # 특정 DNS 서버 지정

# dig 조회
dig [도메인]                     # A 레코드 (기본)
dig [도메인] +short              # 간단 출력
dig MX [도메인]                  # MX 레코드
dig +trace [도메인]              # 위임 체인 전체 추적
dig @8.8.8.8 [도메인]           # Google DNS로 직접 질의
dig -x [IP주소]                  # 역방향 조회(PTR)

# DNS 캐시 초기화
sudo systemd-resolve --flush-caches    # Linux (systemd)
sudo dscacheutil -flushcache           # macOS

# 로컬 오버라이드
sudo vi /etc/hosts
# 형식: [IP주소]  [도메인명]

체크리스트

DNS 문제 발생 시 진단 순서:

  • dig [도메인] @8.8.8.8로 Authoritative 응답 확인
  • dig [도메인] @1.1.1.1로 다른 DNS 서버 응답 비교
  • cat /etc/resolv.conf로 시스템 DNS 설정 확인
  • sudo systemd-resolve --flush-caches로 캐시 초기화 후 재시도
  • /etc/hosts에 충돌하는 항목이 있는지 확인
  • TTL 값을 확인하여 전파 대기 시간 추산

관련 모듈로 더 깊이:

다음 모듈에서는 /etc/resolv.conf 설정 파일 구조와 재부팅 후 초기화 문제를 해결하는 방법을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

서비스 도메인 IP를 변경했는데 일부 사용자는 새 서버로 가고 일부는 여전히 이전 서버로 갑니다. 원인은 무엇입니까?

Q2

서비스 API 도메인(`api.service.com`)을 AWS ALB 도메인(`alb-xxx.ap-northeast-2.elb.amazonaws.com`)으로 연결하려 합니다. 적절한 DNS 레코드 타입은?

Q3

개발팀이 스테이징 서버를 `api.service.com`으로 테스트하고 싶습니다. DNS 변경 없이 로컬 PC에서만 스테이징 IP로 연결하는 가장 빠른 방법은?

Q4

`dig api.service.com @8.8.8.8`은 정상 응답하는데 서버에서 `curl https://api.service.com`이 실패합니다. 이 경우 가장 먼저 확인해야 할 것은?

Q5

`dig +trace google.com` 명령어를 실행하는 실무 목적은 무엇입니까?

Q6

캐시에 없는 www.example.com을 처음 조회할 때, 재귀 DNS 서버가 IP를 알아내는 계층적 과정으로 올바른 것은?

Q7

[심화] 새 서브도메인의 A 레코드를 방금 만들었는데, 일부 리졸버는 여전히 NXDOMAIN(존재하지 않음)을 응답한다. 새 레코드의 TTL은 60초로 짧게 잡았는데도 그렇다. 이 대기 시간을 지배하는 값은?

Q8

[심화] 레코드가 유난히 많은(TXT·DNSSEC) 특정 도메인만 간헐적으로 해석에 실패하고, 짧은 응답 도메인은 항상 정상이다. dig +tcp로는 잘 되는데 기본 질의는 실패한다. 가장 유력한 원인은?

0 / 8 답변

🧪 실습으로 확인하기

포트는 열렸다는데 왜 안 되지? — ss/netstat/telnet으로 TCP 진단

초급

"포트 8080 열었는데요?"와 "왜 안 돼요?" 사이의 간극을 메우는 실습. ss로 바인딩 상태를 확인하고, telnet/nc으로 원격 연결을 테스트하고, iptables 방화벽을 진단하고, 바인딩 주소(0.0.0.0 vs 127.0.0.1)까지 수정하는 4단계 TCP 포트 진단 플로우를 완성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요