DNS — dig 는 되는데 애플리케이션은 실패하는 이유
한 줄 요약
dig 와 애플리케이션은 애초에 다른 경로로 이름을 풀기 때문에, dig 가 성공했다는 사실은 애플리케이션이 성공한다는 근거가 되지 못한다.
왜 이게 필요했나
장애 상황에서 가장 사람을 헷갈리게 하는 조합이 있다. dig +short api.internal.example.com 은 주소를 잘 돌려주는데 같은 호스트의 애플리케이션은 이름을 못 찾는다고 죽는다. 여기서 대부분 "DNS 는 멀쩡한데 애플리케이션이 이상하다"는 결론으로 넘어가 라이브러리를 뒤지기 시작한다. 이 방향은 거의 항상 헛수고다.
Linux에서 hosts: files dns인 구성을 예로 든다. 애플리케이션의 실제 리졸버 구현은 별도 확인해야 한다.
- getent / NSS 경로nsswitch.conf의 hosts 순서에 따라 files, dns 같은 소스를 확인한다.
- dig / DNS 경로DNS 서버에 레코드를 질의한다. /etc/hosts의 항목을 대신 검증하지 않는다.
여기서 구분할 것 브라우저의 보안 DNS나 자체 리졸버를 쓰는 런타임까지 getent와 항상 같다고 가정하지 않는다.
잠깐, 예측해 보세요 dig는 새 IP인데 앱은 옛 IP로 간다. DNS 서버부터 바꿔야 할까?
설명 확인 · 채점 없는 자가 점검
먼저 /etc/hosts, NSS 순서, 앱의 캐시와 리졸버를 비교한다. 같은 이름이 어느 소스에서 달라졌는지 좁히는 것이 우선이다.
어떻게 동작하나
먼저 DNS 자체의 구조다. 리졸버는 루트 서버에게 물어 TLD 서버를 알아내고, TLD 서버에게 물어 권한 서버를 알아내고, 권한 서버에게서 최종 답을 받는다(재귀 질의와 반복 질의의 조합). 각 응답에는 TTL 이 붙어 있어 그 시간만큼 캐시된다.
레코드 종류도 알아 둘 만하다. A 는 IPv4 주소, AAAA 는 IPv6 주소, CNAME 은 다른 이름으로의 별칭, MX 는 메일 서버, NS 는 권한 서버, TXT 는 임의 문자열, SRV 는 서비스의 호스트와 포트다.
이제 핵심이다. 애플리케이션이 실제로 밟는 경로는 다르다. C, 파이썬, 루비, PHP, 그리고 리눅스의 자바까지 대부분 glibc 의 getaddrinfo() 를 호출하고, glibc 는 다음 순서를 따른다.
/etc/nsswitch.conf의 hosts 행을 읽어 어떤 소스를 어떤 순서로 쓸지 정한다.files면/etc/hosts를 본다.dns면/etc/resolv.conf의 nameserver, search, options 를 적용해 질의한다.resolve면 systemd-resolved 에 물어본다.
dig, nslookup, host 는 이 경로를 통째로 건너뛴다. nsswitch.conf 도 /etc/hosts 도 보지 않고 resolv.conf 에서 nameserver 주소만 가져다 UDP 53 으로 질의를 던진다. 그래서 /etc/hosts 에만 있는 이름을 dig 는 못 찾고, 반대로 누군가 디버깅하다 남겨 둔 낡은 /etc/hosts 항목 때문에 애플리케이션만 옛 IP 로 접속하는 상황이 생긴다.
애플리케이션과 같은 경로를 재현하는 도구는 getent hosts 다. getent ahosts 를 쓰면 IPv4 와 IPv6 를 시도 순서대로 보여 준다. 정리하면 이렇다. dig 는 DNS 서버의 답을 보여 주고, getent 는 애플리케이션이 받을 답을 보여 준다. 둘의 결과가 다르면 그 차이 자체가 원인의 위치다.
현장에서 만나는 모습
쿠버네티스 파드의 resolv.conf 에는 options ndots:5 와 네 개의 search 도메인이 들어 있다. ndots 는 "점 개수가 이 값보다 적으면 search 목록을 먼저 붙여 보라"는 뜻이다. 덕분에 파드 안에서 api 나 api.prod 같은 짧은 이름을 쓸 수 있다.
문제는 외부 도메인이다. api.stripe.com 은 점이 2개라 5보다 적으므로 search 도메인 네 개를 먼저 붙여 본다. A 와 AAAA 를 함께 물으므로 외부 도메인 하나를 풀기 위해 질의 10개가 나가고 그중 8개는 NXDOMAIN 을 받으려고 나간 것이다. 파드가 수백 개면 CoreDNS 부하의 대부분이 실패할 것이 확정된 질의로 채워진다.
여기서 흔히 도는 처방을 정정해야 한다. "ndots 를 1로 낮추면 된다"는 조언은 클러스터를 깨뜨린다. ndots 가 1이면 점이 하나인 payments.prod 같은 크로스 네임스페이스 호출이 search 를 거치지 않아 전부 실패한다. 안전한 하한선은 2다. 더 확실한 방법은 외부 도메인 끝에 점을 붙여 절대 이름으로 만드는 것이다. 뒤의 점 하나가 search 도메인 네 개를 없앤다.
응답 코드도 뭉뚱그리면 안 된다. NXDOMAIN 은 그 이름이 없다는 확정이고, SERVFAIL 은 리졸버가 답을 만들지 못했다는 뜻이며, ANSWER 가 0 인 NOERROR 는 이름은 있지만 그 타입의 레코드가 없다는 뜻이다. 마지막 것이 가장 자주 오진된다. A 는 있는데 AAAA 가 없는 경우가 대표적이다.
이름이 안 풀릴 때 어디까지 갔는지 보기
"DNS 문제인 것 같다" 는 말은 흔하지만, 실제로 DNS 인 경우는 절반쯤이다. 어디서 막혔는지는 한 단계씩 물어보면 드러난다.
dig 는 캐시를, dig +trace 는 사슬을 본다.
dig example.com A +short # 지금 내 리졸버가 아는 답
dig example.com A +trace # 루트부터 권한 서버까지 따라간다
dig @8.8.8.8 example.com A # 다른 리졸버는 뭐라고 하는가
dig example.com SOA +short # 이 이름의 주인은 어느 서버인가
+short 로는 되는데 브라우저에서 안 된다면 DNS 가 아니라 그다음 단계다.
@8.8.8.8 로는 되는데 기본 리졸버로는 안 된다면 내 쪽 리졸버나 캐시다.
nslookup/dig 와 애플리케이션이 다른 답을 볼 수 있다. 이 둘은 DNS 를 직접
묻지만, 대부분의 프로그램은 getaddrinfo() 를 거친다. 그 경로에는
/etc/hosts, /etc/nsswitch.conf, mDNS 가 끼어 있다. 프로그램이 실제로 무엇을
보는지는 getent hosts example.com 이 더 정확하다.
TTL 은 "언제부터 되는가" 가 아니라 "언제까지 옛것이 남는가" 다. 레코드를 바꾸기 전에 TTL 을 300초로 줄여 두고, 그 TTL 이 지난 뒤에 바꾸고, 안정되면 다시 올린다. 이 순서를 지키지 않으면 바꾸는 순간이 아니라 옛 TTL 이 다 빠지는 시각까지 사람마다 다른 곳으로 간다.
dig example.com A # ANSWER SECTION 의 두 번째 열이 남은 TTL 이다
없는 이름과 답이 없는 것은 다르다. NXDOMAIN 은 그 이름이 존재하지 않는
것이고, NOERROR 인데 ANSWER 가 0이면 이름은 있지만 그 종류의 레코드가
없는 것이다. AAAA 만 없고 A 는 있는 상황에서 IPv6 를 먼저 시도하는 클라이언트가
느려지는 자리가 여기다.
검색 도메인이 조용히 붙는다. /etc/resolv.conf 의 search 에 도메인이
여럿이면, 짧은 이름 하나를 조회할 때마다 그 수만큼 질의가 나간다. 쿠버네티스
파드의 ndots:5 때문에 외부 도메인 조회가 네 번 실패한 뒤에 성공하는 것이
잘 알려진 사례다. 마지막에 점을 찍어 example.com. 으로 적으면 검색을 건너뛴다.
다른 네임서버를 무작정 지정하기 전에, 문제가 나는 프로세스와 같은 실행 환경에서 증거를 모은다.
- 이름과 환경 고정같은 호스트·컨테이너에서 동일한 이름과 레코드 타입을 비교한다.
- 답의 출처 비교getent와 dig 결과가 다르면 hosts/NSS/캐시 경계를 확인한다.
- 앱의 실제 연결 확인이름 해석이 맞아도 포트·TLS·HTTP 실패는 남을 수 있다.
여기서 구분할 것 dig 성공은 그 질의가 성공했다는 증거다. 서비스 전체가 정상이라는 판정은 아니다.
잠깐, 예측해 보세요 이름이 올바른 IP로 풀리는데 TLS 인증서 오류가 난다. 무엇을 구분해야 할까?
설명 확인 · 채점 없는 자가 점검
이름 해석과 TLS 신원 검증을 분리한다. 목적지 IP만이 아니라 요청 호스트명, 인증서 이름과 신뢰 체인을 확인한다.
이어지는 퀴즈에서 확인할 것
dig 와 getent 의 차이를 설명할 수 있는지, ndots 가 만드는 지연의 메커니즘을 이해했는지 확인한다.