Debian에서 Node 앱이 외부 API(예: api.stripe.com)나 DB 호스트를 호출할 때 갑자기 ENOTFOUND, EAI_AGAIN, 타임아웃이 늘었다면, 의외로 “DNS 설정 실수”가 원인인 경우가 많습니다. 특히 /etc/resolv.conf가 예상과 다르게 바뀌었거나, systemd-resolved/NetworkManager/클라우드 에이전트가 서로 덮어쓰는 상황이 흔합니다.
아래는 가장 흔한 실수부터 순서대로 좁혀가는 점검/해결 흐름입니다.
1) 증상 정리: DNS 문제인지 먼저 구분하기
DNS 이슈는 “네트워크가 완전히 죽음”이 아니라, 특정 도메인만 또는 간헐적으로 실패하는 형태로 나타나는 경우가 많습니다. Node에서는 다음 로그가 단서가 됩니다.
- getaddrinfo ENOTFOUND: 도메인 자체를 못 찾음(대개 DNS 서버/도메인 오타/검색 도메인 영향)
- getaddrinfo EAI_AGAIN: 일시적 DNS 실패(타임아웃/UDP 막힘/리졸버 불안정/캐시 문제)
- 같은 호스트에 대해 성공/실패가 번갈아 발생: 여러 DNS 서버 중 하나가 불량이거나, split-horizon/사설 DNS가 섞였을 가능성
빠르게 구분하려면 “IP로 붙으면 되는데 도메인으로만 실패”하는지 확인합니다. 예를 들어 외부 API가 IP로 접근 가능한 구조는 드물지만, 내부 서비스(사설 DB 등)는 IP로 테스트가 가능합니다.
2) 점검 순서(체크리스트): 가장 흔한 설정 실수부터
아래 체크리스트를 위에서부터 순서대로 진행하면, 불필요한 삽질을 줄일 수 있습니다.
- /etc/resolv.conf가 실제로 어떤 파일(실파일/심볼릭 링크)인지 확인
- nameserver가 올바른지(사내 DNS, 8.8.8.8/1.1.1.1 등 정책에 맞게) 확인
- systemd-resolved 사용 여부 확인(127.0.0.53을 쓰는지)
- VPN/클라우드 에이전트/NetworkManager가 DNS를 덮어쓰는지 확인
- 도메인 오타/불필요한 search 설정으로 엉뚱한 질의가 추가되지 않는지 확인
- 방화벽/보안그룹에서 UDP/TCP 53이 막히지 않았는지 확인
- 캐시(nscd/dnsmasq/systemd-resolved)가 꼬였을 가능성 점검
3) 1차 확인: resolv.conf와 systemd-resolved 상태 보기
먼저 현재 리졸버 구성이 무엇인지 “사실 확인”부터 합니다.
- /etc/resolv.conf 내용 확인(현재 nameserver가 무엇인지)
- /etc/resolv.conf가 심볼릭 링크인지 확인(예: systemd-resolved가 관리)
- systemd-resolved를 쓴다면 “서버가 어떤 DNS 서버를 업스트림으로 쓰는지” 확인
자주 보는 패턴은 다음 둘입니다.
- nameserver 127.0.0.53: 로컬 stub(systemd-resolved)로 보내고, 실제 업스트림 DNS는 별도 설정
- 직접 IP가 박혀 있음(예: nameserver 10.x.x.x 또는 공용 DNS): 외부 환경에서 덮어쓰기 당했는지 의심
여기서 흔한 실수는, systemd-resolved 환경인데 운영자가 /etc/resolv.conf를 직접 편집했다가 이후 재부팅/네트워크 갱신 때 원복되며 장애가 “간헐적”으로 보이는 경우입니다.
4) 재현 테스트: dig/nslookup로 “어느 DNS에서” 실패하는지 분리
DNS 문제는 “어느 리졸버로 질의했는지”를 분리하면 원인 범위가 확 줄어듭니다. 핵심은 같은 도메인을 기본 리졸버와 특정 DNS 서버로 각각 질의해 비교하는 것입니다.
- 기본 리졸버로 조회했을 때 응답이 느리거나 실패하는지
- 특정 DNS(예: 사내 DNS, 1.1.1.1, 8.8.8.8)로 강제 조회하면 정상인지
- 응답이 오더라도 결과가 다른지(split-horizon, 내부/외부 레코드 충돌)
또 하나 자주 놓치는 부분이 IPv6(AAAA)입니다. AAAA 응답이 애매하게 구성되어 있거나, IPv6 라우팅이 막혀 있는데도 Node가 IPv6로 먼저 시도하면서 체감 장애가 생길 수 있습니다(환경에 따라).
5) 해결 단계: 흔한 원인별로 안전하게 정리
아래는 “가장 흔한 설정 실수” 순서대로, 리스크가 낮은 것부터 정리한 해결 단계입니다.
- 오타/잘못된 DNS 서버 IP: nameserver를 정책에 맞는 값으로 수정(사내 DNS 우선, 필요 시 보조 DNS 추가)
- /etc/resolv.conf 덮어쓰기: systemd-resolved/NetworkManager 중 실제 관리 주체를 하나로 정리(수동 편집을 반복하지 않도록)
- search 도메인 과다/오염: 불필요한 search를 제거(질의가 늘어 타임아웃이 증가하는 원인)
- 방화벽/보안그룹 이슈: DNS 서버로의 UDP 53이 막히면 타임아웃 형태로 나타남. 정책상 TCP 53도 필요할 수 있음(응답이 큰 경우)
- 캐시 꼬임: systemd-resolved 캐시 플러시 또는 관련 데몬 재시작으로 즉시 복구되는지 확인
- IPv6 우선 시도 문제: 네트워크가 IPv6를 제대로 지원하지 않으면, OS/애플리케이션 레벨에서 우선순위 조정 검토
운영 팁으로는, “임시로 공용 DNS를 넣어 바로 복구”는 가능하지만, 사내 망/프라이빗 존을 쓰는 환경에서는 오히려 내부 도메인이 깨질 수 있습니다. 따라서 외부 도메인과 내부 도메인 둘 다 테스트하면서 바꾸는 게 안전합니다.
6) Node 관점 확인: 앱 설정/런타임이 DNS 실패를 키우는 경우
DNS가 완전히 정상이어도, Node 앱에서 다음 조건이 겹치면 장애가 과장되어 보일 수 있습니다.
- 재시도 로직이 없거나, 재시도 간격이 너무 짧아 한꺼번에 폭주
- DNS 실패(EAI_AGAIN)를 즉시 “치명적 오류”로 처리
- 외부 API 호출이 동기적으로 몰려서, 실패 시 대량 타임아웃이 누적
가능하면 외부 호출에는 타임아웃과 지수 백오프 재시도를 두고, DNS가 잠깐 흔들려도 서비스 전체가 멈추지 않게 완충을 두는 편이 좋습니다. 다만 이건 “근본 원인 해결 후” 안정성을 보강하는 성격으로 접근하는 게 좋습니다.
마무리
Debian에서 DNS 장애는 대부분 “/etc/resolv.conf가 누가 관리하는지 모르는 상태”에서 시작합니다. 먼저 현재 구성을 확정하고(실제 nameserver, systemd-resolved 여부), dig/nslookup로 어느 단계에서 실패하는지 분리하면 빠르게 좁혀집니다.
체크리스트대로 한 번만 정리해두면, 다음에 같은 증상이 와도 원인 파악이 훨씬 빨라집니다.