Published on

EKS CoreDNS DNS timeout·SERVFAIL 10분 진단

Authors

서버리스나 애플리케이션 문제가 아닌데도, 어느 순간부터 Pod 내부에서 DNS 질의가 timeout 또는 SERVFAIL로 터지면 장애 범위가 급격히 커집니다. 특히 EKS에서는 CoreDNS가 클러스터의 단일 병목 지점이 되기 쉬워, "일부 Pod만 간헐적으로 실패" 같은 애매한 증상이 자주 나옵니다.

이 글은 EKS에서 CoreDNS 관련 DNS 장애를 10분 내에 진단 방향을 결정하는 것을 목표로 합니다. 핵심은 (1) 실패가 CoreDNS까지인지, (2) CoreDNS에서 업스트림으로 나가는 구간인지, (3) CoreDNS 자체 리소스/설정 문제인지 빠르게 분리하는 것입니다.

관련해서 네트워크 타임아웃을 빠르게 좁히는 접근은 EKS Pod→RDS 504 타임아웃 - SG·NACL·NAT 10분 진단도 함께 참고하면 진단 속도가 올라갑니다.

0. 먼저 증상을 정확히 분류하기

DNS 장애는 크게 두 부류로 나뉩니다.

  • timeout: 질의가 응답을 못 받고 시간 초과
    • 네트워크 단절, 업스트림 DNS 응답 지연, CoreDNS 과부하, conntrack 포화 등
  • SERVFAIL: DNS 서버가 "처리 실패"를 반환
    • 업스트림에서 실패를 반환, CoreDNS 플러그인/캐시/포워딩 문제, DNSSEC/EDNS 관련 이슈 등

그리고 대상도 나뉩니다.

  • 클러스터 내부 도메인 예: kubernetes.default.svc.cluster.local
  • 외부 도메인 예: api.github.com, sts.amazonaws.com

이 분류가 중요한 이유는 내부 도메인 실패는 CoreDNS 또는 kube-apiserver/서비스 디스커버리 쪽을, 외부 도메인 실패는 CoreDNS의 forward 경로(업스트림 DNS, VPC Resolver, NAT/엔드포인트)를 우선 의심해야 하기 때문입니다.

1. 1분 진단: 실패 지점이 Pod인지 CoreDNS인지

1-1. 문제 Pod에서 resolv.conf 확인

Pod가 어떤 DNS 서버를 보고 있는지부터 확인합니다.

kubectl exec -n <namespace> <pod> -- cat /etc/resolv.conf

확인 포인트:

  • nameserver가 보통 ClusterIP(예: 10.100.0.10)로 찍히는지
  • search 도메인이 과도하게 길지 않은지
  • options ndots:5 같은 기본값이 트래픽을 과도하게 만들 여지가 있는지

search가 길고 ndots가 높으면, 짧은 호스트명 질의가 여러 번 재시도되며 CoreDNS 부하를 키우는 경우가 있습니다.

1-2. 같은 노드/다른 노드에서 재현되는지

특정 노드에서만 재현되면 네트워크/conntrack/노드 리소스 이슈 가능성이 커집니다.

kubectl get pod -n <namespace> -o wide

노드 편향이 보이면, CoreDNS가 해당 노드에 붙어 있는지(혹은 해당 노드의 egress가 꼬였는지)를 같이 봐야 합니다.

2. 3분 진단: CoreDNS 상태와 로그에서 단서 찾기

2-1. CoreDNS Pod 상태, 재시작, 스케줄링 확인

kubectl -n kube-system get deploy,cfgmap,po -l k8s-app=kube-dns -o wide
kubectl -n kube-system describe pod -l k8s-app=kube-dns | sed -n '1,200p'

확인 포인트:

  • RESTARTS가 증가 중인지
  • Ready가 간헐적으로 내려가는지
  • 특정 노드에만 몰려 있는지

CoreDNS가 CrashLoopBackOff라면 원인 패턴이 다양합니다. 즉시 진단 프레임은 K8s CrashLoopBackOff 원인 10가지·즉시 진단법도 유용합니다.

2-2. CoreDNS 로그에서 timeout/SERVFAIL 패턴 확인

kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200

자주 보이는 힌트:

  • plugin/errors: 어떤 도메인에서 실패하는지
  • i/o timeout: 업스트림으로 나가다 타임아웃
  • no route to host 또는 connection refused: 네트워크/업스트림 문제

가능하면 CoreDNS에 log 플러그인을 잠깐 켜서 어떤 질의가 폭증하는지도 확인합니다(장애 상황에서 장시간 켜두면 로그 폭주 위험).

3. 5분 진단: CoreDNS가 업스트림 DNS로 나갈 수 있나

EKS 기본 구성에서 CoreDNS는 보통 VPC Resolver(예: 169.254.169.253 또는 VPC의 .2)로 포워딩합니다. 여기서 막히면 외부 도메인이 대량으로 timeout이 납니다.

3-1. CoreDNS Pod 내부에서 업스트림 테스트

CoreDNS 컨테이너에는 진단 도구가 부족할 수 있어, 같은 네임스페이스에 임시 디버그 Pod를 띄우는 편이 빠릅니다.

kubectl -n kube-system run dns-debug \
  --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 \
  --restart=Never -- sleep 3600

kubectl -n kube-system exec -it dns-debug -- sh

디버그 Pod에서 테스트:

# 클러스터 DNS로 질의
nslookup kubernetes.default.svc.cluster.local 10.100.0.10

# 외부 도메인
nslookup api.github.com 10.100.0.10

해석:

  • 내부 도메인도 실패하면 CoreDNS 또는 kube-apiserver/서비스 디스커버리 경로 문제
  • 내부는 되는데 외부만 실패하면 forward 경로(업스트림 DNS, VPC, NAT, 엔드포인트) 문제

3-2. VPC Resolver 직접 질의

클러스터 DNS를 거치지 않고 VPC Resolver로 직접 질의해 봅니다.

# 예시: VPC Resolver 주소는 환경마다 다를 수 있음
nslookup api.github.com 169.254.169.253
  • 여기서도 실패하면 CoreDNS 이전이 아니라 VPC DNS/네트워크 레벨 문제일 가능성이 큽니다.
  • 여기서는 성공인데 CoreDNS를 거치면 실패하면 CoreDNS 설정/리소스/연결 추적 한계 쪽을 봅니다.

4. 7분 진단: CoreDNS 리소스 병목과 쿼리 폭증

CoreDNS는 작은 리소스로도 잘 돌아가지만, 트래픽이 순간적으로 튀면 timeout이 발생합니다. 특히 다음 상황에서 폭증이 흔합니다.

  • 애플리케이션이 짧은 호스트명으로 자주 질의하고 search 도메인 확장으로 여러 번 재시도
  • 서비스 디스커버리 루프(실패 시 즉시 재시도)
  • 특정 라이브러리가 매 요청마다 DNS 재조회

4-1. CoreDNS CPU/메모리 사용량 확인

kubectl -n kube-system top pod -l k8s-app=kube-dns

CPU가 지속적으로 높거나 스파이크가 심하면:

  • CoreDNS replica 수를 늘리거나
  • resources.requests/limits를 상향하거나
  • 캐시 튜닝을 고려합니다.

4-2. HPA/스케일링 여부

CoreDNS는 기본적으로 HPA가 없을 수 있습니다. 트래픽이 많은 클러스터라면 HPA를 붙이는 것도 방법입니다.

kubectl -n kube-system get hpa

5. 10분 진단: CoreDNS ConfigMap에서 흔한 함정 찾기

CoreDNS 동작은 coredns ConfigMap의 Corefile에 의해 결정됩니다.

kubectl -n kube-system get configmap coredns -o yaml

대표 체크 포인트:

5-1. forward 대상이 올바른가

예시(Corefile 일부):

.:53 {
    errors
    health
    ready
    kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        fallthrough in-addr.arpa ip6.arpa
    }
    prometheus :9153
    forward . /etc/resolv.conf
    cache 30
    loop
    reload
    loadbalance
}
  • forward . /etc/resolv.conf는 CoreDNS Pod의 resolv.conf를 따라가는데, 노드/환경에 따라 의도치 않은 DNS로 향할 수 있습니다.
  • 운영에서는 VPC Resolver를 명시적으로 박는 구성이 문제를 줄이는 경우가 있습니다.

예:

forward . 169.254.169.253 {
    max_concurrent 1000
}

주의: max_concurrent를 과도하게 키우면 업스트림을 더 세게 두드려 역효과가 날 수 있어, 장애 양상에 맞춰 조정합니다.

5-2. cache가 너무 짧거나 없는가

캐시가 너무 짧으면 외부 질의가 폭증합니다. 반대로 너무 길면 레코드 변경 반영이 늦을 수 있습니다. 보통은 수십 초 수준에서 시작해 관측하며 조정합니다.

5-3. loop 경고가 있는가

CoreDNS가 자신에게 질의를 다시 보내는 구성(루프)이 생기면 SERVFAIL이 확 늘 수 있습니다. 로그에 loop 관련 메시지가 있는지 확인합니다.

자주 나오는 원인별 빠른 처방

1) 외부 도메인만 timeout: NAT/엔드포인트/라우팅

CoreDNS는 살아있지만 업스트림으로 못 나가는 케이스입니다.

  • 프라이빗 서브넷에서 NAT 장애
  • 라우팅 테이블/네트워크 ACL 변경
  • VPC 엔드포인트 정책/프록시 이슈

네트워크 경로 진단 접근은 EKS Pod→RDS 504 타임아웃 - SG·NACL·NAT 10분 진단 흐름을 그대로 적용할 수 있습니다.

2) 내부 도메인도 timeout 또는 SERVFAIL: CoreDNS 과부하/노드 문제

  • CoreDNS 파드가 특정 노드에 고정되어 있고 그 노드가 불안정
  • conntrack 테이블 포화로 UDP 드랍
  • CoreDNS CPU 부족으로 지연 증가

이 경우는 CoreDNS replica 확장과 함께, 노드 단의 네트워크/리소스 상태를 같이 봐야 합니다.

3) 간헐적 SERVFAIL: 업스트림 실패 전파 또는 설정/플러그인 문제

  • 업스트림 DNS가 간헐적으로 실패를 반환
  • CoreDNS forward가 특정 업스트림을 번갈아 사용하며 일부가 불량
  • loop, cache, reload 조합 문제

업스트림을 2개 이상 두는 경우(예: /etc/resolv.conf에 여러 nameserver) 특정 서버만 불안정하면 간헐 장애가 됩니다. 업스트림을 명시하고, 불량 서버를 제외하는 것이 빠른 해결이 될 수 있습니다.

재현과 관측을 위한 최소 도구 세트

장애 때마다 "감"으로 보지 않으려면, 다음을 최소로 갖추는 것을 권합니다.

  • CoreDNS prometheus 메트릭 수집(예: :9153)
  • 쿼리 수, 응답 코드 비율(SERVFAIL, NXDOMAIN), 레이턴시, 캐시 히트율
  • 노드 conntrack 사용량(가능하면 노드 익스포터)

애플리케이션 레벨에서도 DNS 재시도 폭증이 전체 장애를 키울 수 있으므로, 실패 시 지수 백오프와 타임아웃 상한을 두는 것이 중요합니다.

마무리: 10분 체크리스트 요약

  • 1분: Pod의 resolv.conf 확인, 노드 편향 확인
  • 3분: CoreDNS Pod 상태/재시작/로그에서 timeout vs SERVFAIL 패턴 확인
  • 5분: 디버그 Pod로 클러스터 DNS 및 VPC Resolver 직접 질의
  • 7분: CoreDNS 리소스 사용량과 트래픽 폭증 여부 확인
  • 10분: CoreDNS Corefile의 forward, cache, loop 함정 점검

이 순서대로 보면, 대부분의 케이스에서 "CoreDNS 자체 문제"인지 "업스트림/VPC 네트워크 문제"인지가 빠르게 갈립니다. 이후에는 원인 범주에 맞춰 스케일링, 업스트림 고정, 네트워크 경로 복구, 애플리케이션 DNS 재시도 완화로 해결을 진행하면 됩니다.