{"id":"IL-0027","org":"Backpackr","date":"2025-05-07","docs":["https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/","https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/","https://kubernetes.io/docs/tasks/run-application/configure-pdb/#specifying-a-poddisruptionbudget","https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/","https://istio.io/latest/docs/ops/configuration/traffic-management/dns-proxy/"],"title":{"en":"All CoreDNS pods landed on one node, the node ran out of memory, and DNS for the whole service went down","ko":"CoreDNS 파드가 모두 한 노드에 몰린 채 그 노드의 메모리가 고갈되자 DNS 가 응답하지 못해 아이디어스 서비스 전체가 멈췄다"},"domain":["kubernetes","aws","networking"],"impact":{"scope":"cluster DNS on the production EKS cluster; full outage of the idus service","users":"unknown","region":"ap-northeast-2"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"The CoreDNS setup that EKS creates by default (two pods) can be enough for a small or development cluster but becomes a bottleneck in a large production cluster, and DNS is a single point of failure that needs high availability and room to scale.\n\nTuning the CoreDNS cache and buffer size only relieved the symptoms; the real load came from the extra queries that ndots:5 generates for external names. A CoreDNS pod without a memory limit can share a node's fate with everything else on it.","ko":"쿠버네티스의 ndots:5 기본값은 점이 적은 외부 도메인을 조회할 때마다 질의를 여러 번 만든다. 이 부하는 트래픽이 큰 프로덕션 환경에서야 드러난다. CoreDNS 를 파드 2개의 기본 구성으로 두면 클러스터 전체의 단일 장애점이 된다. CoreDNS 파드는 노드와 가용 영역에 흩어 둔다. 별도 노드 그룹이나 Fargate 로 분리하면 다른 워크로드와 자원을 다투지 않는다. Affinity 와 PriorityClassName 으로 우선순위를 주면 더 안정적이다. CoreDNS 의 캐시와 bufsize 조정은 증상을 잠깐 줄였을 뿐이다. ndots 값은 여러 자료가 1이나 2를 권하지만 각 환경에 맞춰 정해야 한다."},"related":[],"sources":[{"url":"https://aws.amazon.com/ko/blogs/tech/optimizing-amazon-eks-journey-of-backpackr-part1","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Backpackr runs the idus handmade marketplace and the Tumblbug crowdfunding platform on Amazon EKS. CoreDNS on the production cluster still had the setup it got when the cluster was created: two pods, kept on the on-demand node group with a nodeSelector, and no other tuning. Every pod also used the Kubernetes default ndots:5 in /etc/resolv.conf, so any name with fewer than five dots was first tried with each search domain appended. A lookup of idus.com went out as up to five queries (idus.com.idus-backends.svc.cluster.local, idus.com.svc.cluster.local, idus.com.cluster.local, idus.com.ap-northeast-2.compute.internal, and finally idus.com), and only the last one could succeed. Under production traffic this multiplied the load on CoreDNS, which showed up as slow or failed lookups and intermittent connection failures to RDS and Redis. The problem was much more visible in production than in the dev and stage environments.\n\nThe outage came when both CoreDNS pods ended up on one node. The pods had no memory limit and ran in the BestEffort QoS class. When the node's memory usage spiked, the node ran out of memory, the kubelet stopped, and all pods on the node fell into CrashLoopBackOff. Because there was no CoreDNS pod on any other node, the cluster lost DNS and the service went down completely.\n\nBackpackr fixed it by putting NodeLocal DNSCache in front of CoreDNS as a DaemonSet that caches answers on every node, by spreading CoreDNS with a PodDisruptionBudget (maxUnavailable 1) and topologySpreadConstraints across zones (both are now EKS defaults; older EKS versions did not set them), and by lowering ndots from 5 to 2 through dnsConfig in the Deployments. Appending a trailing dot to every domain name was rejected because every reference in code and configuration would have had to change. Istio DNS proxying was tested but not adopted because it overlaps with NodeLocal DNSCache. The write-up also suggests running CoreDNS on its own node group or on Fargate and using affinity and priorityClassName.","ko":"백패커는 아이디어스를 운영하는 EKS 클러스터를 처음 구성할 때 CoreDNS 를 따로 최적화하지 않고 기본 설정 그대로 두었다. CoreDNS 파드는 2개였고, nodeSelector 로 온디맨드 타입의 운영 노드 그룹에만 배치했다. CoreDNS 파드에는 메모리 한도가 없어서 QoS 클래스가 BestEffort 였다.\n\n쿠버네티스는 모든 파드의 /etc/resolv.conf 에 ndots:5 를 넣는다. 리졸버는 점이 다섯 개보다 적은 이름을 불완전한 이름으로 보고 검색 도메인을 하나씩 붙여 먼저 조회한다. 그 조회가 모두 실패한 뒤에야 원래 이름으로 조회한다. 그래서 idus.com 을 한 번 호출하면 idus.com.idus-backends.svc.cluster.local, idus.com.svc.cluster.local, idus.com.cluster.local, idus.com.ap-northeast-2.compute.internal, idus.com 순서로 최대 다섯 번 조회가 나갔고 마지막 조회만 성공했다. 트래픽이 급증하면 이 불필요한 조회가 고스란히 CoreDNS 에 몰렸다.\n\n고가용성 설정도 없었다. 당시 EKS 버전의 CoreDNS 에는 PodDisruptionBudget 과 topologySpreadConstraints 가 기본으로 들어 있지 않았다. 어느 시점에 CoreDNS 파드가 모두 한 노드에 올라갔고, 그 노드의 메모리가 고갈되어 OOM 이 났다. kubelet 이 멈추면서 노드의 파드가 모두 CrashLoopBackOff 에 빠졌고, DNS 가 응답하지 않자 서비스 전체가 멈췄다.\n\n백패커는 NodeLocalDNS Cache 를 도입하고, CoreDNS 에 고가용성 설정을 넣고, 워크로드의 ndots 를 2 로 낮춰 CoreDNS 부하를 나누었다. 도메인 끝에 점을 붙여 FQDN 으로 조회하는 방법은 모든 도메인 참조를 고쳐야 해서 유지보수가 어렵다고 보고 쓰지 않았다. Istio DNS 프록시도 검토했지만 NodeLocalDNS Cache 와 기능이 겹쳐 쓰지 않았다."},"trigger":{"en":"All CoreDNS pods were scheduled onto the same node, and that node's memory usage spiked until it ran out of memory.","ko":"모든 CoreDNS 파드가 같은 노드에 배치되었고, 그 노드의 메모리 사용률이 급증해 메모리가 고갈됐다."},"severity":"unknown","symptoms":["dns-resolution-failures","intermittent-connection-failures","high-latency","oom-killed","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"The write-up does not say how the full outage was noticed. The earlier DNS trouble showed up as intermittent service errors, and investigating failed calls to RDS and Redis led to DNS resolution failures.","ko":"글은 전면 장애를 어떻게 알아챘는지 적지 않았다. 그 전의 DNS 문제는 원인을 알기 어려운 서비스 에러가 간헐적으로 나면서 드러났다. 팀은 RDS 와 Redis 호출이 가끔 실패하는 현상을 추적하다가 DNS 이름 해석 실패가 근본 원인이라는 것을 확인했다."},"components":["coredns","dns-resolver","kubelet","memory-limit","node-pool"],"root_cause":"capacity","remediation":[{"text":{"en":"deployed NodeLocal DNSCache as a DaemonSet in front of CoreDNS","ko":"CoreDNS 앞에서 DNS 질의를 받아 노드마다 캐시하는 NodeLocalDNS Cache 를 데몬셋으로 배포해 CoreDNS 부하를 줄였다."}},{"text":{"en":"added a PodDisruptionBudget (maxUnavailable 1) and topologySpreadConstraints across zones for CoreDNS","ko":"CoreDNS 에 maxUnavailable: 1 인 PodDisruptionBudget 과 topology.kubernetes.io/zone 기준의 topologySpreadConstraints 를 적용해 파드가 한 곳에 몰리지 않게 했다."}},{"text":{"en":"lowered ndots from 5 to 2 with dnsConfig in the Deployments","ko":"Deployment 의 dnsConfig 로 ndots 값을 5 에서 2 로 낮춰 불필요한 DNS 조회를 줄였다."}}],"action_items":[],"contributing":["default-config-unchanged","single-point-of-failure","no-caching"],"recurrence_of":null,"symptoms_text":{"en":"Services on the production Amazon EKS cluster behind the idus marketplace threw intermittent errors whose cause was hard to pin down. Calls to external services such as RDS and Redis failed now and then, and whenever DNS traffic spiked, domain lookups answered slowly or failed outright.\n\nThe team first checked application code, node resources, pod status and container settings. A closer look showed that the failing calls were failed DNS resolutions. Tuning the CoreDNS cache and its bufsize setting eased the symptoms for a while but did not remove them.\n\nThe worst episode was a full outage. At one point every CoreDNS pod was running on the same on-demand node. That node's memory usage jumped until the node ran out of memory, the kubelet stopped working, and every pod on the node went into CrashLoopBackOff. With no CoreDNS pod left to answer, DNS stopped working and the whole service went down.","ko":"프로덕션 EKS 클러스터에서 원인을 바로 알기 어려운 서비스 에러가 간헐적으로 났다. RDS 나 Redis 같은 외부 서비스를 호출할 때 연결이 가끔 실패했다. DNS 요청이 몰릴 때는 도메인 조회 응답이 늦어지거나 조회가 아예 실패했다.\n\n이 증상은 트래픽이 적은 개발 환경과 스테이징 환경에서는 잘 보이지 않았고, 트래픽이 큰 프로덕션 환경에서 두드러졌다.\n\n팀은 처음에 애플리케이션 코드, 노드 리소스, 파드 상태, 컨테이너 설정을 차례로 점검했다. 자세히 조사한 끝에 외부 서비스 연결 실패의 근본 원인이 DNS 이름 해석 실패라는 것을 확인했다. 그다음 CoreDNS 의 캐시 설정과 bufsize 를 조정해 봤지만 증상이 잠깐 줄었을 뿐 근본 해결이 되지 않았다.\n\n어느 시점에는 CoreDNS 파드가 모두 한 노드에 몰려 있었다. 그 노드의 메모리 사용률이 갑자기 치솟았고 노드에서 OOM 이 났다. kubelet 이 멈추면서 그 노드의 모든 파드가 CrashLoopBackOff 에 빠졌다. CoreDNS 도 응답하지 못했고 서비스 전체가 멈췄다."},"updated_at":"2026-11-08T19:10:28.388842Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0027","org_slug":"backpackr"}