- Published on
EKS CoreDNS CrashLoopBackOff - upstream 타임아웃 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS 운영 중 갑자기 서비스들이 DNS를 못 풀기 시작하고, kube-system의 CoreDNS Pod가 CrashLoopBackOff로 떨어지는 상황은 꽤 흔합니다. 특히 로그에 upstream 관련 타임아웃이 반복되면(예: read udp ... i/o timeout), CoreDNS 자체 문제라기보다 CoreDNS가 포워딩하는 상위 DNS(Upstream)까지 네트워크가 닿지 않는 경우가 대부분입니다.
이 글은 “CoreDNS CrashLoopBackOff + upstream 타임아웃” 조합을 재현 가능한 점검 순서로 쪼개고, EKS에서 자주 터지는 원인(노드의 /etc/resolv.conf, VPC DNS, NAT/라우팅, 보안그룹/NACL, NetworkPolicy, Corefile 루프 등)을 증상-원인-해결 형태로 정리합니다.
증상 패턴: 로그로 유형부터 분류
먼저 CoreDNS 로그를 봅니다.
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200
자주 보이는 패턴은 다음과 같습니다.
read udp <coredns-ip>:<port>-><upstream-ip>:53: i/o timeout- CoreDNS가 upstream(대개 노드의 resolv.conf에 있는 DNS, 또는 VPC Resolver
169.254.169.253)로 UDP 53을 쐈는데 응답이 없음
- CoreDNS가 upstream(대개 노드의 resolv.conf에 있는 DNS, 또는 VPC Resolver
plugin/forward: no healthy upstream- forward 플러그인이 upstream을 모두 실패로 판단
plugin/loop: Loop ...또는too many loops- CoreDNS가 자기 자신(또는 클러스터 DNS)으로 포워딩하면서 무한 루프
CrashLoopBackOff는 보통 CoreDNS가 DNS 실패 자체로 죽는다기보다, 설정 오류/루프/헬스체크 실패로 재시작을 반복하는 경우가 많습니다.
1단계: CoreDNS Pod는 살아있나? (리소스/프로브 확인)
우선 Crash 원인이 단순 OOM/CPU 스로틀인지 확인합니다.
kubectl -n kube-system describe pod -l k8s-app=kube-dns | sed -n '/State:/,/Events:/p'
kubectl -n kube-system top pod -l k8s-app=kube-dns
OOMKilled라면 메모리 요청/제한이 너무 낮거나, 질의 폭주(예: 잘못된 앱 리트라이)일 수 있습니다.Liveness probe failed가 반복되면 CoreDNS가 응답을 못 하거나(내부적으로 forward 실패), 루프 감지로 종료되는 케이스가 많습니다.
리소스가 정상인데도 upstream timeout이면 다음 단계로 갑니다.
2단계: “CoreDNS → Upstream” 경로가 막혔는지 확인
EKS에서 CoreDNS는 보통 forward . /etc/resolv.conf로 설정되어 있고, 이는 CoreDNS 컨테이너의 /etc/resolv.conf(대부분 노드 설정을 기반)로 upstream을 찾습니다.
CoreDNS ConfigMap을 확인합니다.
kubectl -n kube-system get cm coredns -o yaml
대표적인 기본값은 대략 이런 형태입니다.
.: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입니다. 즉, upstream 타임아웃은 곧 (1) resolv.conf가 이상하거나 (2) resolv.conf가 가리키는 DNS로 네트워크가 안 나가거나 둘 중 하나입니다.
2-1) CoreDNS 컨테이너의 resolv.conf 확인
# CoreDNS Pod 하나를 집어서 확인
POD=$(kubectl -n kube-system get pod -l k8s-app=kube-dns -o jsonpath='{.items[0].metadata.name}')
kubectl -n kube-system exec -it "$POD" -- cat /etc/resolv.conf
여기서 다음을 봅니다.
nameserver가 무엇인지- EKS/VPC에서는 보통
169.254.169.253(VPC DNS Resolver) 또는 노드가 받은 VPC DNS IP
- EKS/VPC에서는 보통
nameserver 127.0.0.53같은 값이 있는지- systemd-resolved 스텁을 가리키는 값인데, 컨테이너/네임스페이스 환경에서 제대로 동작하지 않아 타임아웃을 유발할 수 있습니다.
2-2) CoreDNS에서 upstream:53로 실제 질의가 되는지 테스트
CoreDNS 컨테이너에는 도구가 없을 수 있으니, 임시 디버그 Pod를 띄워 테스트합니다.
kubectl run -it --rm dns-debug \
--image=public.ecr.aws/docker/library/busybox:1.36 \
--restart=Never \
-- sh
Pod 안에서:
# 1) 클러스터 DNS(CoreDNS Service)로 질의
nslookup kubernetes.default.svc.cluster.local 10.100.0.10
# 2) VPC Resolver로 직접 질의(대표값)
nslookup amazon.com 169.254.169.253
- 2)가 타임아웃이면: VPC DNS/라우팅/보안 정책 문제 가능성이 큽니다.
- 2)는 되는데 1)이 안 되면: CoreDNS 자체 설정/NetworkPolicy/Service 라우팅 문제를 봐야 합니다.
3단계: EKS에서 upstream 타임아웃의 대표 원인 6가지
원인 A) 노드 resolv.conf가 127.0.0.53(systemd-resolved)를 가리킴
증상
- CoreDNS 로그: upstream
127.0.0.53:53로 타임아웃 - CoreDNS
/etc/resolv.conf에nameserver 127.0.0.53
해결 방향
- CoreDNS가
/etc/resolv.conf를 따라가면 안 됩니다. VPC Resolver를 명시하거나, 노드의 resolv.conf가 올바른 nameserver를 갖도록 해야 합니다.
가장 빠른 우회(권장되는 응급처치): CoreDNS forward를 명시적으로 바꿉니다.
kubectl -n kube-system edit cm coredns
예시(환경에 맞게 1~2개 지정):
forward . 169.254.169.253 {
max_concurrent 1000
}
적용 후 롤링 재시작:
kubectl -n kube-system rollout restart deploy coredns
주의
- 장기적으로는 노드 AMI/부트스트랩에서 resolv.conf 구성을 바로잡는 게 좋습니다.
원인 B) 프라이빗 서브넷에서 NAT/라우팅 문제로 외부 DNS가 불가
EKS 노드가 프라이빗 서브넷에 있고, upstream이 퍼블릭 DNS(예: 8.8.8.8)인데 NAT가 없거나 라우팅이 잘못되면 UDP 53이 나가지 못합니다.
확인 포인트
- 노드가 있는 서브넷 라우팅 테이블에
0.0.0.0/0 -> NAT Gateway가 있는지 - NACL에서 ephemeral port(1024-65535) 아웃바운드/인바운드가 막혀 있지 않은지
해결
- upstream을 VPC Resolver(보통
169.254.169.253)로 통일 - NAT Gateway/Route Table/NACL 수정
원인 C) 보안그룹/네트워크 정책이 UDP/TCP 53을 차단
CoreDNS는 upstream으로 UDP 53을 주로 사용하지만, 응답이 크거나 fallback 시 TCP 53을 쓰기도 합니다.
확인
- 노드/ENI 보안그룹 egress가 제한된 환경인지
- Calico/Cilium NetworkPolicy로
kube-system의 CoreDNS egress가 막혔는지
NetworkPolicy 예시(허용 정책이 필요한 환경에서):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: kube-system
spec:
podSelector:
matchLabels:
k8s-app: kube-dns
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 169.254.169.253/32
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
원인 D) CoreDNS loop(자기 자신으로 포워딩)로 Crash
증상
plugin/loop관련 로그- Corefile에서 forward 대상이 결국 CoreDNS Service IP로 돌아오거나,
/etc/resolv.conf가 클러스터 DNS를 가리킴
해결
/etc/resolv.conf에 클러스터 DNS(예:10.100.0.10)가 들어가 있지 않게 구성- forward를 VPC Resolver로 명시
loop플러그인은 원인 파악에 유용하지만, 루프가 해결되기 전까지는 Crash를 유발할 수 있으니 설정 변경 후 재기동
원인 E) VPC DNS 기능 비활성화(EnableDnsSupport/EnableDnsHostnames)
EKS는 VPC DNS에 강하게 의존합니다. VPC 설정에서 DNS 지원이 꺼져 있으면 169.254.169.253이 동작하지 않거나 기대대로 응답하지 않습니다.
확인
- VPC의
EnableDnsSupport = true - VPC의
EnableDnsHostnames = true
해결
- VPC 설정을 활성화한 뒤 CoreDNS 재시작
원인 F) 노드 로컬 DNS(NodeLocal DNSCache) 구성 불일치
NodeLocal DNSCache를 쓰는 환경에서, CoreDNS/NodeLocal의 upstream 체인이 꼬이면 타임아웃이 발생할 수 있습니다.
확인
kube-system에node-local-dnsDaemonSet 존재 여부- CoreDNS가 NodeLocal(예:
169.254.20.10)로 포워딩하는지, 반대로 NodeLocal이 CoreDNS로 포워딩하는지
해결
- 체인을 단순화: CoreDNS → VPC Resolver, 또는 NodeLocal → VPC Resolver / CoreDNS는 클러스터 도메인만 담당
- 구성 변경 후 둘 다 롤아웃
4단계: “CoreDNS는 정상인데 여전히 503/타임아웃”과의 구분
DNS 장애는 애플리케이션에서 503/타임아웃으로 보일 때가 많습니다. CoreDNS가 회복된 뒤에도 서비스가 503이면, 엔드포인트/레디니스/EndpointSlice 문제일 수 있습니다. 이 경우는 DNS가 아니라 트래픽 라우팅 계층을 봐야 합니다.
5단계: 재발 방지 체크리스트(운영 관점)
5-1) CoreDNS에 최소한의 관측성 추가
- CoreDNS 메트릭(9153) 스크랩
- 로그에서
no healthy upstream알람 - HPA는 DNS 장애 상황에서 역효과(스케일아웃이 문제를 해결하지 못함)일 수 있으니, 원인에 따라 신중히 적용
5-2) Corefile 변경은 GitOps로 관리
수동 kubectl edit는 응급처치로만 쓰고, 이후에는 Helm/Kustomize/GitOps로 이력을 남기는 게 안전합니다.
5-3) 노드/클러스터 네트워크 변경 시 DNS 회귀 테스트
배포 파이프라인에 다음을 포함하면 좋습니다.
kubectl run -it --rm dns-smoke \
--image=public.ecr.aws/docker/library/busybox:1.36 \
--restart=Never -- \
sh -c 'nslookup kubernetes.default.svc.cluster.local && nslookup amazon.com 169.254.169.253'
6단계: 자주 묻는 질문(실전에서 많이 헷갈리는 지점)
Q1. upstream을 8.8.8.8로 박아도 되나요?
가능은 하지만 EKS/VPC 환경에서는 권장하지 않습니다.
- 프라이빗 서브넷이면 NAT/라우팅에 의존
- 보안 정책에서 외부 DNS가 차단될 수 있음
- VPC 내부 도메인(Private Hosted Zone, Resolver Rule)과의 일관성이 깨질 수 있음
대부분은 VPC Resolver(169.254.169.253) 를 upstream으로 두는 게 안정적입니다.
Q2. CoreDNS가 죽으면 왜 전체가 느려지나요?
대부분의 서비스는 외부 API 호출, DB 접속, 내부 서비스 디스커버리에 DNS를 씁니다. DNS가 타임아웃 나면 애플리케이션은 재시도/백오프를 하며 스레드/커넥션 풀이 고갈되고, 결국 503/지연이 연쇄적으로 발생합니다.
Q3. 이미지 풀도 같이 실패하는데요?
DNS가 깨지면 ECR/레지스트리 도메인 해석이 실패해 ImagePullBackOff로 이어질 수 있습니다. 다만 이 경우는 DNS 외에도 IAM/ECR 토큰/프록시 이슈가 섞일 수 있어 분리해서 봐야 합니다.
결론
EKS에서 CoreDNS CrashLoopBackOff와 upstream 타임아웃이 함께 보이면, 핵심은 “CoreDNS가 포워딩하는 상위 DNS까지 패킷이 왕복 가능한가”입니다. 가장 빠른 분기점은 다음 두 가지입니다.
- CoreDNS의
/etc/resolv.conf가 무엇을 가리키는지 확인하고, 필요하면forward . 169.254.169.253처럼 upstream을 명시한다. - VPC/서브넷 라우팅, NAT, NACL/SG, NetworkPolicy로 UDP/TCP 53 및 응답 트래픽이 막히지 않았는지 확인한다.
이 순서대로만 점검해도 “원인을 못 찾고 CoreDNS만 재시작하는” 악순환을 크게 줄일 수 있습니다.