← 컴퓨팅 인프라
실전 가이드DEEPER INTO TECHNOLOGY

DNS TTL이 지났는데 왜 옛 주소가 보일까? 캐시와 응답 경로 진단

DNS TTL이 지났는데도 이전 주소나 이름 없음 응답이 보인다면, 어디서 무엇을 비교해야 할까?

먼저 읽는 핵심

DNS TTL은 전 세계 주소가 바뀌는 공통 마감 시각이 아니다. 각 캐시가 받은 응답의 수명이며, 이미 저장된 긴 TTL을 새 설정이 소급해 줄이지 않는다. 이름이 없다는 응답도 캐시되고, 갱신에 실패한 리졸버는 설정에 따라 만료 데이터를 돌려줄 수 있다. 같은 이름·레코드 종류·관측 시각을 고정하고 권한 서버, 사용 리졸버, 앱의 실제 접속을 나눠 비교해야 다음 조치가 보인다.

“5분 기다렸는데 그대로”에서 빠진 정보

API 서버를 이전하면서 DNS의 A 레코드를 새 IP로 바꾸고 TTL을 300초로 설정했다. 10분 뒤에도 일부 사용자는 옛 서버로 접속한다. 이를 “DNS 전파가 느리다”로만 정리하면 무엇을 기다리는지, 어느 팀이 확인할지 모호해진다.

먼저 변경 직전 TTL이 얼마였는지를 묻는다. 기존 TTL이 3,600초였다면 새로 정한 300초만 기다리는 계산은 맞지 않을 수 있다. 새 이름을 만들었다면 그 전에 저장된 “이름 없음” 응답을, 권한 서버에 도달할 수 없었다면 만료 데이터 재사용을 따로 살펴봐야 한다.

이 글은 일반적인 DNS 질의와 캐시의 경계를 설명한다. 아래 시간표·IP·응답 예시은 직접 설계한 가상 사례다. 실제 리졸버의 장애·복구를 재현하거나 제품별 기본 설정을 측정하지 않았다. DNSSEC 전체 검증, 도메인 등록기관 이전, 모든 CDN의 지역별 응답 정책까지 다루지는 않는다.

누구에게 물은 답인지 먼저 나눈다

권한 서버는 자신이 맡은 영역인 존(zone)의 데이터를 제공한다. 재귀 리졸버는 클라이언트 대신 답을 찾아오고 캐시를 사용할 수 있다. 앱에서 이름을 찾는 경로에는 운영체제의 조회 기능이나 별도 DNS 기능도 관여할 수 있다. 이 역할 구분은 RFC 9499의 DNS 용어를 기준으로 한다.

따라서 비교할 것은 IP 하나가 아니라 다음 묶음이다.

관측 시각과 네트워크 위치
질의 이름(QNAME) / 종류(QTYPE) / 클래스(보통 IN)
질의한 서버 / 응답 코드 / AA 여부
응답의 이름·종류·값·TTL / 부정 응답의 SOA / 제공된 EDE
앱이 실제로 접속한 원격 주소와 새 연결 여부

api.example.test.의 A와 AAAA는 별도 질문이다. A는 IPv4 주소, AAAA는 IPv6 주소를 구한다. A만 새 값인지 확인해 놓고 IPv6 접속까지 전환됐다고 말할 수 없다. CNAME으로 다른 이름을 가리킨다면 별칭과 대상 이름의 주소도 나눠 본다. RFC 1034 §3.6.2는 별칭을 따라 질의를 이어 가는 동작을 설명한다. 각각의 레코드에는 각자의 TTL이 있다.

권한 서버 한 곳의 새 응답은 그 서버와 그 질의 조건에 대한 증거다. 다른 권한 서버, 다른 네트워크에서 같은 답을 받는지는 별도다. 특히 회사 안팎에서 다른 주소를 주는 split DNS나 응답 view를 쓰면, 응답 차이 자체가 의도된 설계일 수 있다. 캐시 오류를 찾기 전에 어느 경로에서 어떤 답이 나와야 하는지부터 정한다.

TTL을 낮추는 일과 주소를 바꾸는 일을 분리한다

TTL은 캐시가 응답을 다시 확인할 시점을 정하는 데 쓰인다. 권한 데이터 자체를 TTL이 지났다는 이유로 지우는 타이머가 아니다. RFC 1034 §3.6은 예상된 변경 전에 TTL을 낮추는 방법을 설명한다. 만료 후 데이터 사용의 예외는 뒤에서 다룰 RFC 8767이 보완한다.

다음은 기존 주소를 192.0.2.10, 새 주소를 192.0.2.20으로 둔 시간표다. 두 주소는 문서용 예시이며 접속 대상이 아니다. 캐시가 조기 퇴출·미리 갱신·TTL 보정·serve-stale를 하지 않고, 권한 서버들이 즉시 같은 설정을 제공한다고 가정한다.

시각 사건 리졸버 R1에 남은 값
09:59 R1이 옛 주소와 TTL 3,600초를 받음 옛 주소, 만료 예정 10:59
10:00 권한 서버의 TTL을 300초로 낮춤 기존 저장본의 만료는 여전히 10:59
10:05 권한 서버의 A를 새 주소로 변경 R1에는 옛 주소의 수명 3,240초가 남음
10:10 R1에 다시 질의 옛 주소, 남은 TTL 2,940초
10:59 이후 다음 질의에서 갱신에 성공 새 주소와 당시의 TTL을 받음

R1의 만료는 09:59 + 3,600초 = 10:59다. 10:00의 설정 변경은 그 캐시로 새 응답을 밀어 보내지 않는다. 반면 10:01에 처음 옛 주소를 받은 R2는 이미 낮아진 TTL 300초를 적용하므로 10:06에 만료된다. 같은 주소를 캐시했어도 언제 어떤 TTL로 받았는지가 다르다. 대표 그림은 이 두 만료 시점을 비교한 설명용 도식이다.

전환 계획에는 다음 두 시점을 따로 적는 편이 낫다.

  1. 관련 권한 응답에서 낮은 TTL이 제공되는 것을 확인한 시점.
  2. 기존 높은 TTL이 남을 수 있는 기간을 고려한 뒤 실제 주소를 바꾸는 시점.

기존 TTL이 1시간인 위 가정에서는 TTL을 낮추기 직전에 응답을 받은 캐시도 고려해 준비 시간을 잡는다. 이는 캐시 모델에 따른 계획 기준이지 “1시간 뒤 모든 사용자가 전환된다”는 보증이 아니다. 별칭 대상, 부정 캐시, 권한 서버의 설정 반영, 앱 연결 수명이 별도 경계를 만든다. 계속 낮은 TTL을 쓰는 것 역시 질의량과 권한 서버 의존도를 고려해 결정할 일이다.

새 이름은 옛 IP 대신 “없음”이 남을 수 있다

새 서비스 이름을 만든 뒤 “어떤 PC에서는 도메인이 없다고 나온다”면 양성 응답의 A 레코드 TTL만 봐서는 설명이 안 된다. DNS는 부정 응답도 캐시한다.

RFC 2308 §§2–5는 이름 자체가 없다는 NXDOMAIN과, 이름은 있으나 요청한 종류의 데이터가 없는 NODATA를 구분한다. NODATA는 별도의 응답 코드 이름이 아니라 응답 내용을 해석한 상태다. 예를 들어 AAAA가 없는 정상적인 이름에 대해 NOERROR이면서 해당 답이 없는 경우를 생각할 수 있다. 빈 Answer만 보고 NXDOMAIN이라고 부르면 다른 문제를 조사하게 된다.

부정 응답을 만드는 권한 서버는 Authority 영역의 SOA 레코드에 캐시 수명을 담는다. 기본 계산은 다음과 같다.

부정 응답의 초기 TTL = min(SOA 레코드의 TTL, SOA.MINIMUM)

가령 11:00에 새 이름을 미리 조회해 NXDOMAIN을 받았고, SOA TTL이 900초, MINIMUM이 300초였다고 하자. 초기 부정 TTL은 300초다. 11:01에 이름을 만들고 A TTL을 60초로 정해도, 이미 부정 응답을 저장한 리졸버가 그 새 A를 즉시 읽는 것은 아니다. 위 캐시 가정에서는 11:04에도 부정 TTL 60초가 남고, 11:05 이후 갱신할 때 새 레코드를 발견할 수 있다.

이 예시에서 조정할 계획은 “A TTL을 더 줄이자”가 아니라 공개 전에 이름과 관련 레코드를 준비하고, 이전 부정 응답의 수명도 확인하자다. NODATA는 기본적으로 이름·종류·클래스를, NXDOMAIN은 이름·클래스를 기준으로 캐시한다. 여기에 DNSSEC 부재 증명 재사용 같은 확장이 있으므로 모든 구현의 캐시 키를 이 문장만으로 구현해서는 안 된다.

SERVFAIL은 또 다르다. 서버가 질의를 처리하지 못했다는 응답이지 “그 이름은 존재하지 않는다”는 확정이 아니다. RFC 1035 §4.1.1의 응답 코드를 구분하고, 권한 서버 도달성이나 검증 오류 등 실패 이유를 조사해야 한다. 응답 코드에 따라 다음 질문이 달라진다.

만료됐어도 옛 응답을 줄 수 있는 serve-stale

정상적인 갱신 경로가 막히면 “아무 답도 주지 않기”와 “저장된 예전 답을 주기” 중 선택이 생긴다. RFC 8767 §§4–6은 리졸버가 데이터를 권한 서버에서 갱신하지 못했을 때 만료된 데이터를 사용하는 serve-stale를 정의한다. 가용성을 위한 기능이며, 모든 리졸버에서 켜져 있다는 뜻은 아니다.

여기서 헷갈리기 쉬운 점은 stale 응답에도 양수 TTL이 붙는다는 것이다. 이 RFC는 만료 레코드를 반환할 때 0보다 큰 TTL을 요구하고 30초를 권고한다. 따라서 옛 주소의 TTL이 30이라고 해서 “권한 서버가 30초 전에 확인해 준 최신 데이터”라고 해석할 수 없다. 반대로 TTL 30만으로 stale 응답이라고 확정할 수도 없다. 원래부터 짧은 TTL인 응답일 수 있다.

EDE(Extended DNS Errors)가 제공되면 원인을 좁히는 데 도움이 된다. RFC 8914 §4.4와 §4.20은 코드 3의 Stale Answer, 코드 19의 Stale NXDOMAIN Answer를 설명한다. 하지만 EDE 전송은 선택 사항이다. 표시가 없다고 stale이 아니라고 판정하지 않는다. EDE 자체도 단독으로 신뢰할 인증 증거가 되지는 않는다.

운영 판단에는 이 기능의 반대 조건이 중요하다. 권한 DNS만 잠깐 불안하고 옛 서비스가 정상이라면 예전 주소가 연결을 살릴 수 있다. 반대로 옛 서버를 이미 종료했다면 답은 받되 서비스에 접속하지 못할 수 있다. DNS 응답 가용성과 서비스 가용성은 다르다. 옛 IP의 접근 권한을 회수해야 하는 상황에서도 TTL을 보안상 즉시 차단 장치처럼 쓰지 말고 실제 접근 제어를 별도로 수행해야 한다.

serve-stale를 무조건 끄거나 TTL을 0으로 바꾸는 결론은 이 글에서 내리지 않는다. 갱신 실패 원인, 옛 대상의 사용 가능 여부, 해당 리졸버의 설정을 함께 확인할 문제다.

dig로 비교할 때 헤더와 질문을 남긴다

아래는 ISC의 dig 공식 문서를 바탕으로 만든 미실행 명령 템플릿이다. 고정 9.20.0 문서와 확인일의 stable 문서(9.20.29 표기)에서 해당 옵션을 대조했다. 이를 독자의 설치 버전이나 실제 서비스 테스트 결과로 해석하지 않는다.

192.0.2.53, 192.0.2.54와 api.example.test.는 예시다. 소유하거나 조회가 허용된 대상의 권한 서버 IP, 문제가 발생한 경로의 재귀 리졸버 IP, 실제 이름으로 각각 바꿔 실행한다. IP를 직접 지정하면 질의 대상 서버의 이름부터 다시 해석하는 혼동을 줄일 수 있다.

# 권한 서버에 재귀 요청 없이 같은 이름의 A 질의
dig -r @192.0.2.53 api.example.test. A +norecurse \
  +noall +question +answer +authority +additional +comments +stats

# 사용자가 실제 이용하는 재귀 리졸버에 같은 질의
dig -r @192.0.2.54 api.example.test. A \
  +noall +question +answer +authority +additional +comments +stats

-r은 개인 dig 기본 옵션 파일의 영향을 제외한다. +norecurse는 재귀 요청 비트를 끈다. 이것만으로 상대가 권한 서버임을 보장하지 않으므로 응답의 aa와 데이터를 함께 확인한다. aa 역시 모든 권한 서버의 동기화 완료나 암호학적 진위를 증명하는 표식은 아니다.

문제를 좁히는 첫 조사에서는 +short만 남기지 않는 편이 좋다. 서버·시각·헤더·Authority를 지우면 “어떤 경로에서 무엇이 없었는지”를 놓칠 수 있다. 위 명령에서 이름과 종류를 유지한 채 관련 권한 서버들을 비교하고, 필요하면 A를 AAAA로 바꿔 별도로 기록한다. CNAME이 나왔다면 그 대상 이름도 이어서 확인한다.

dig +trace는 현재 실행 위치에서 위임을 따라가는 별도 진단이다. 사용자의 재귀 리졸버에 저장된 캐시를 그대로 보여 주는 명령이 아니다. 마찬가지로 터미널에서 특정 리졸버에 질의해 새 주소를 받았다는 사실만으로, 별도 DNS 경로를 쓰는 앱이나 이미 맺은 연결이 새 주소를 사용한다고 단정할 수 없다.

결과에 따라 다음 조사 위치를 고른다

다음은 원인 확정표가 아니라 다음 관측을 정하는 표다. 같은 질의 조건을 비교했음을 전제로 하며, 의도된 지역별·내외부별 응답은 먼저 제외한다.

관측 먼저 확인할 경계 다음 행동
권한 서버끼리 옛 값·새 값이 다름 존 배포·권한 응답 정책 각 서버의 실제 답과 설정 반영을 비교한다. SOA serial 하나만으로 성공을 판정하지 않는다.
권한은 새 값, 사용 리졸버는 옛 값 기존 TTL·CNAME 대상·갱신 실패 변경 전 TTL과 시각을 복원하고, EDE와 리졸버 정책·도달성을 조사한다.
권한은 새 이름을 알지만 리졸버는 NXDOMAIN 부정 캐시 부정 응답의 SOA·남은 TTL과 이름 생성 시점을 비교한다.
NOERROR인데 요청한 종류의 답이 없음 종류별 부재·별칭·위임 A/AAAA를 구분하고 Answer와 Authority를 함께 읽는다.
사용 리졸버도 새 값인데 앱은 옛 서버 접속 앱 DNS 경로·별도 캐시·연결 재사용 앱의 실제 원격 주소와 새 연결을 관측한다. HTTP 응답 캐시는 그 뒤에 분리한다.
SERVFAIL 또는 응답 없음 처리 실패·네트워크·검증 제공된 오류 정보와 로그를 확인한다. 단순 TTL 대기로 결론내리지 않는다.

예를 들어 10:10의 R1이 옛 주소와 TTL 2,940을 반환했다면 앞의 3,600초 시간표로 설명할 수 있다. 반면 TTL 30이 반복되고 EDE 3까지 보인다면 갱신 실패와 stale 정책을 조사할 이유가 있다. 두 번째 관측을 첫 번째와 같은 “조금 더 기다리기”로 처리하면 장애 원인을 놓칠 수 있다. EDE가 없거나 TTL이 다시 커졌다면 다른 캐시 노드·갱신·응답 정책 가능성도 남는다.

전환 완료를 판단할 기록지를 남긴다

캐시를 지우거나 리졸버를 바꾸기 전에 변경 전후 응답을 남겨야 원인을 설명할 수 있다. 자신이 관리하는 캐시를 지우는 것은 한 경로의 진단에는 도움이 되지만, 외부 사용자 전체의 저장본을 회수하는 작업은 아니다.

다음 양식을 장애 인수인계나 이전 작업에 복사해 쓸 수 있다. DNS 전환 진단 기록지 내려받기에도 같은 핵심 항목을 담았다. 실제 서비스의 내부 이름과 주소가 들어간 기록은 공개 보고서에 그대로 붙이지 않는다.

기대 결과: 어느 네트워크에서 어떤 이름/종류가 어떤 값이어야 하는가?
변경 이력: 기존 TTL / TTL을 낮춘 시각 / 값을 바꾼 시각
권한 관측: 서버별 시각·답·TTL·AA·RCODE
사용 리졸버 관측: 서버·시각·답·TTL·SOA·EDE
앱 관측: DNS 경로 / 실제 원격 주소 / 새 연결인지
남은 가설: 기존 TTL / 부정 캐시 / 갱신 실패 / 앱 경로 / 기타
다음 확인: 담당자 / 시각 / 성공으로 볼 구체적인 관측
옛 대상 종료: 잔여 접속과 되돌리기 조건을 무엇으로 확인할 것인가?

이 기록을 바탕으로 옛 서버를 언제 종료할지 판단한다. 낮춘 TTL이 적용됐다는 것, 새 권한 응답을 받는다는 것, 대표 사용자 경로에서 새 연결이 새 서버에 도달한다는 것은 각각 별도 관측이다. 비용·보안·지원 범위에 따라 모든 경로를 볼 수 없는 한계도 적는다. TTL 하나로 전 세계 전환을 인증할 수는 없다.

주소 경로를 확인했는데도 옛 화면이 보인다면 HTTP 캐시의 신선도와 304 재검증에서 응답 재사용을 이어 확인할 수 있다. 다음 이전 작업에서 이 진단표를 다시 쓰려면 글 상단의 저장 기능을 이용하고, 관련 주제는 인프라 글 모음에서 찾아볼 수 있다.

다시 읽을 때의 기준
  • TTL을 낮출 때는 기존 긴 TTL이 남은 캐시를 고려한다. 새 TTL을 설정한 순간 모든 캐시의 시간이 다시 계산되지는 않는다.
  • 옛 IP·NXDOMAIN·NODATA·SERVFAIL은 서로 다른 관측이다. 이름과 레코드 종류, 응답 서버, SOA와 EDE를 함께 남긴다.
  • 권한 응답이 새 값인 것과 사용자 앱이 새 서버에 접속하는 것은 별도 확인이다. 전환 종료는 실제 접속 관측과 함께 판단한다.
SOURCES & CONTEXT

근거와 원자료

자료의 발행 시점과 이번 글에서 참고한 범위를 함께 기록합니다.

  1. Domain Names — Concepts and Facilities ↗RFC Editor · P. Mockapetris · 1987

    RFC 1034 §3.6/3.6.2: TTL과 CNAME, 권한·재귀 경로. TTL 만료 후 처리에는 RFC 8767의 후속 정의도 적용. 실제 전환 실험 아님.

  2. Domain Names — Implementation and Specification ↗RFC Editor · P. Mockapetris · 1987

    RFC 1035 §4.1.1/4.1.3: AA, RCODE, 레코드별 TTL. 암호학적 인증이나 전 세계 전환 완료의 증거로 해석하지 않음.

  3. Negative Caching of DNS Queries (DNS NCACHE) ↗RFC Editor · M. Andrews · 1998

    RFC 2308 §§2–5: NXDOMAIN/NODATA와 부정 TTL. DNSSEC 부재 증명 재사용·하위 트리 최적화 전체의 구현 계약은 아님.

  4. Serving Stale Data to Improve DNS Resiliency ↗RFC Editor · Lawrence 외 · 2020

    RFC 8767 §§4–6: 갱신 실패 시 만료 데이터 사용, 양수 응답 TTL과 30초 권고. 제품별 기본값·실측 결과가 아님.

  5. Extended DNS Errors ↗RFC Editor · Kumari 외 · 2020

    RFC 8914 §§3,4.4,4.20,6: 선택적 EDE와 stale 코드, 인증 한계. 코드 부재를 stale 부재로 해석하지 않음.

  6. DNS Terminology ↗RFC Editor · Hoffman, Fujiwara · 2024

    RFC 9499의 권한 서버·리졸버·split DNS/views 용어. 서로 다른 경로의 응답을 무조건 캐시 오류로 판정하지 않음.

  7. BIND 9 Manual Pages — dig ↗ISC · BIND 9 · 상시 갱신 문서

    확인 시 stable 표기는 9.20.29. 고정 9.20.0 문서와 @server, -r, +norecurse, +trace, 출력 옵션을 대조. 명령 템플릿은 미실행이며 사용자 설치 버전을 주장하지 않음.

갱신 기록

첫 원고. DNS RFC와 ISC dig 문서를 대조하고 TTL 시간표·관측자별 진단표·기록지를 작성했다. 사례는 가상이며 실제 DNS 장애 재현은 수행하지 않았다.

오류를 발견했다면 →
S
Starhunter

테크독해 운영·편집. AI·IT의 개념과 기술을 연결해 읽습니다.