- Published on
EKS에서 Pod DNS 실패 - CoreDNS·VPC CNI 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스나 애플리케이션 코드 문제처럼 보이지만, EKS에서 Pod DNS 장애는 대부분 클러스터 네트워킹(aws-node/VPC CNI) 또는 CoreDNS의 업스트림 경로에서 터집니다. 증상은 비슷해도 원인은 다르므로, “Pod → CoreDNS → 업스트림(VPC DNS/Route53 Resolver) → 목적지” 경로를 단계적으로 검증하는 게 가장 빠릅니다.
이 글은 다음과 같은 상황을 전제로 합니다.
- Pod에서
nslookup/kdig가 타임아웃 또는SERVFAIL/NXDOMAIN을 반환 - 특정 노드/특정 AZ에서만 재현되거나, 트래픽 피크 때만 간헐적으로 발생
- CoreDNS는 Running인데도 DNS가 불안정
아래 절차대로 진행하면 CoreDNS 설정 문제인지, VPC CNI(IP/ENI/SG/NACL) 문제인지, 노드 로컬 DNS/iptables 문제인지를 비교적 짧은 시간에 분리할 수 있습니다.
1) 장애 패턴 먼저 분류하기
DNS 장애는 “어디까지 패킷이 가는지”로 나누면 빠릅니다.
1-1. 흔한 에러 메시지
i/o timeout/no servers could be reached- 대개 CoreDNS까지 못 감(Pod→kube-dns Service/Endpoint 경로) 또는 CoreDNS가 업스트림으로 못 나감(CoreDNS→VPC DNS)
SERVFAIL- CoreDNS가 응답은 했지만 업스트림 실패(VPC DNS/Resolver, 네트워크, CoreDNS upstream 설정)
NXDOMAIN- 진짜로 레코드가 없거나, search domain/ndots로 인해 엉뚱한 FQDN을 조회 중
1-2. 재현을 위한 표준 테스트 Pod
문제 재현/진단은 항상 “클러스터 내부 DNS”와 “외부 도메인”을 나눠 테스트합니다.
kubectl run -it --rm dns-debug \
--image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 \
--restart=Never -- sh
# 클러스터 내부
nslookup kubernetes.default.svc.cluster.local
# 외부 도메인
nslookup amazon.com
# CoreDNS 서비스 IP 확인
cat /etc/resolv.conf
- 내부 서비스(
kubernetes.default...)도 실패하면 CoreDNS/Service/Endpoint/iptables 쪽 가능성이 큽니다. - 내부는 되는데 외부만 실패하면 CoreDNS 업스트림(VPC DNS/egress) 가능성이 큽니다.
2) Pod의 resolv.conf부터 확인(의외로 자주 원인)
Pod의 /etc/resolv.conf는 “DNS 서버가 누구인지, search/ndots가 어떤지”를 보여줍니다.
kubectl exec -it dns-debug -- cat /etc/resolv.conf
정상 예시는 대략 다음과 같습니다.
nameserver가 kube-dns Service IP(보통172.20.0.10같은 ClusterIP)search에default.svc.cluster.local등options ndots:5(기본)
2-1. ndots/search로 인한 지연·타임아웃
ndots:5 환경에서 api.mycorp.com 같은 도메인을 조회하면, 먼저 다음처럼 여러 번 “붙여서” 질의합니다.
api.mycorp.com.default.svc.cluster.localapi.mycorp.com.svc.cluster.local- ...
- 마지막에
api.mycorp.com
업스트림이 느리거나 CoreDNS가 바쁘면 이 과정이 누적되어 간헐적 타임아웃으로 보일 수 있습니다. 이때는:
- 애플리케이션에서 가능한 FQDN을 명확히 사용
- 필요 시 Pod 단위로
dnsConfig조정(무분별한 변경은 권장하지 않음)
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
dnsConfig:
options:
- name: ndots
value: "2"
3) CoreDNS 상태 점검: “Running”은 의미가 약하다
CoreDNS가 Running이어도, 업스트림 타임아웃/conntrack 고갈/CPU throttling 등으로 질의가 누락될 수 있습니다.
3-1. CoreDNS 로그/지표 확인
kubectl -n kube-system get deploy coredns
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=200
로그에서 자주 보이는 힌트:
plugin/errors: 업스트림 실패timeout: 업스트림 또는 네트워크 지연SERVFAIL: upstream 응답 실패
가능하면 CoreDNS에 log 플러그인을 잠시 활성화해 “질의가 들어오는지/어디서 막히는지”를 확인합니다(장애 시 임시로만).
3-2. Corefile에서 upstream 확인
kubectl -n kube-system get configmap coredns -o yaml
EKS 기본은 대개 다음 형태입니다.
.: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의
/etc/resolv.conf는 보통 노드/VPC DNS(AmazonProvidedDNS, 예:VPC CIDR + 2)로 향합니다. - 즉, CoreDNS → VPC DNS 경로가 막히면 외부 도메인 해석이 실패합니다.
3-3. CoreDNS Pod에서 업스트림 직접 테스트
CoreDNS Pod에 들어가서 업스트림이 살아있는지 확인합니다.
COREDNS_POD=$(kubectl -n kube-system get pod -l k8s-app=kube-dns -o jsonpath='{.items[0].metadata.name}')
kubectl -n kube-system exec -it "$COREDNS_POD" -- sh -c "cat /etc/resolv.conf; nslookup amazon.com"
- 여기서도 실패하면 CoreDNS→업스트림 또는 노드 네트워크 문제 가능성이 큽니다.
- 여기서는 되는데 일반 Pod에서만 실패하면 Pod→CoreDNS(Service/Endpoint/iptables) 쪽을 봐야 합니다.
4) kube-dns Service/Endpoint 경로 확인(“CoreDNS까지 못 간다”)
Pod는 보통 kube-dns Service(ClusterIP)로 질의합니다. 이 Service가 올바른 Endpoint로 라우팅되는지 확인합니다.
kubectl -n kube-system get svc kube-dns -o wide
kubectl -n kube-system get endpoints kube-dns -o wide
- Endpoint가 비어 있으면 Selector/라벨 문제 또는 CoreDNS Pod 준비 실패
- Endpoint가 특정 노드에만 몰려 있으면(스케줄링/anti-affinity 부재) 해당 노드 장애 시 DNS 전체가 흔들립니다.
권장:
- CoreDNS replica를 2개 이상
- 가능하면 AZ/노드 분산(anti-affinity)
5) AWS VPC CNI(aws-node) 관점: IP/ENI/보안정책 문제
EKS DNS 장애의 “진짜” 원인은 CoreDNS가 아니라 CNI가 만든 네트워크 조건인 경우가 많습니다.
5-1. 우선 aws-node DaemonSet 상태 확인
kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200
로그에서 체크할 키워드:
- IP 할당 실패, ENI 할당 실패
InsufficientCidrBlocks,PrivateIpAddressLimitExceeded- 노드별 IP 부족(=Pod IP 부족)으로 인한 이상 동작
5-2. 노드의 Pod IP 고갈은 DNS를 “간헐적으로” 깨뜨린다
Pod DNS가 실패하는데 다른 네트워크도 간헐적으로 끊긴다면, 노드가 IP 포화 상태일 수 있습니다.
- 새 Pod가 뜨면서 IP 할당이 지연/실패
- 기존 Pod의 네트워크도 비정상(특히 conntrack/iptables가 함께 압박받을 때)
대응:
- 인스턴스 타입별 최대 ENI/IP 확인 후 노드 스케일
- 서브넷 CIDR 확장 또는 서브넷 추가
- Prefix Delegation 사용(환경에 따라)
5-3. Security Group / NACL: DNS(53)와 CoreDNS(UDP/TCP) 경로
DNS는 기본적으로 UDP 53을 쓰지만, 응답이 크거나 특정 상황에서는 TCP 53으로 전환됩니다. 따라서 UDP만 열어두면 간헐 장애가 날 수 있습니다.
점검 포인트:
- 노드 SG/NACL에서 노드↔노드 트래픽이 충분히 허용되는지(특히 CoreDNS Pod가 있는 노드로)
- 노드에서 VPC DNS(AmazonProvidedDNS)로 UDP/TCP 53이 허용되는지
- NetworkPolicy를 사용한다면 CoreDNS로의 egress/ingress가 막히지 않는지
5-4. kube-proxy/iptables/conntrack 병목
kube-dns는 ClusterIP(Service)이므로, Pod의 DNS 질의는 iptables/nftables 규칙과 conntrack을 거칩니다.
- 트래픽이 많은 클러스터에서 conntrack 테이블이 부족하면 UDP 드롭이 늘고 DNS가 먼저 체감됩니다.
- 증상: 피크 타임에만
i/o timeout증가
노드에서 확인(권한 필요):
# 노드에 접속 후
sudo sysctl net.netfilter.nf_conntrack_max
sudo cat /proc/sys/net/netfilter/nf_conntrack_count
# 드롭/에러 카운터(환경에 따라 명령 상이)
sudo conntrack -S || true
대응은 워크로드 특성에 따라 다르지만, 일반적으로:
- 노드 스케일 아웃(트래픽 분산)
- conntrack 튜닝(운영 정책에 따라 신중히)
- CoreDNS 수평 확장 및 리소스 상향
6) CoreDNS 스케일/리소스/캐시 전략
DNS 문제는 “네트워크”만큼이나 “리소스 부족”도 흔합니다.
6-1. CoreDNS 리소스 요청/제한 확인
kubectl -n kube-system get deploy coredns -o yaml | sed -n '/resources:/,/imagePullPolicy/p'
CPU limit이 너무 낮으면 throttling으로 타임아웃이 늘 수 있습니다. 트래픽이 많다면:
- replica 증가
- CPU request/limit 상향
cacheTTL 조정(너무 길면 변경 반영 지연, 너무 짧으면 upstream 부하)
6-2. 간단한 HPA 예시(환경에 맞게 조정)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: coredns
namespace: kube-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: coredns
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
(메트릭 수집이 되어 있어야 하며, 운영 환경에서는 DNS QPS 기반 지표가 더 유용할 수 있습니다.)
7) 장애를 더 빨리 좁히는 “체크리스트”
아래 순서대로 보면 보통 10~20분 내로 범위를 좁힙니다.
- Pod에서
/etc/resolv.conf확인: nameserver가 kube-dns인지 - 내부 도메인 vs 외부 도메인 분리 테스트
kube-dnsService/Endpoints 확인- CoreDNS 로그에서 timeout/SERVFAIL 확인
- CoreDNS Pod에서 업스트림(nslookup) 테스트
aws-node로그/상태 확인(IP/ENI 문제)- SG/NACL/NetworkPolicy에서 UDP+TCP 53 및 노드 간 통신 확인
- 피크 타임이면 conntrack/리소스 병목 의심 → CoreDNS 스케일/노드 스케일
8) 자주 같이 터지는 EKS 네트워크 이슈
DNS가 흔들릴 때는 “다른 제어 플레인 경로도 같이” 문제가 보이곤 합니다. 예를 들어 kubectl logs/exec가 갑자기 불안정해지면 노드 네트워크/보안그룹/프록시 경로를 함께 의심해야 합니다. 이 경우는 아래 글의 점검 루틴이 바로 도움이 됩니다.
또한 DNS 자체 문제로 보였는데 실제로는 Pod의 AWS API 호출이 시간/네트워크 문제로 실패하는 케이스도 있습니다(예: S3 업로드 오류가 DNS/시간 동기화 문제와 함께 관측). 네트워크 이상 징후를 함께 볼 때 참고할 만합니다.
클러스터 오토스케일링(Karpenter) 도입 후 특정 노드군에서만 DNS가 실패한다면, 노드가 충분히 늘지 않아 과밀(리소스/conntrack/IP) 상태가 되었을 가능성도 큽니다.
9) 결론: “CoreDNS 문제처럼 보여도 CNI부터 의심하라”
EKS의 Pod DNS 장애는 표면적으로 nslookup timeout 하나로 보이지만, 실제로는 다음 두 축에서 갈립니다.
- CoreDNS 자체(리소스/설정/업스트림 forward)
- AWS VPC CNI(노드 IP/ENI, SG/NACL, conntrack, 노드 간 통신)
가장 효율적인 접근은 “Pod → kube-dns Service → CoreDNS Pod → VPC DNS”를 한 단계씩 끊어서 확인하는 것입니다. 특히 CoreDNS Pod 안에서 업스트림 조회가 되는지는 원인 분리에 결정적입니다.
운영 환경에서는 재발 방지를 위해 아래를 권장합니다.
- CoreDNS replica 2+ 및 AZ 분산
- CoreDNS 리소스 상향 및 필요 시 HPA
- 노드 IP/ENI 여유 확보(서브넷/인스턴스 타입/Prefix Delegation 검토)
- SG/NACL에서 DNS UDP+TCP 53 및 노드 간 통신 명확히 허용
- 피크 트래픽 클러스터는 conntrack/노드 스케일링 계획 포함
이 정도까지 정리해두면, “갑자기 특정 Pod만 DNS가 안 된다” 같은 사건도 로그 몇 줄과 몇 번의 nslookup으로 빠르게 수습할 수 있습니다.