{"id":"IL-0001","org":"Zalando","date":"2019-01-07","docs":["https://nodejs.org/docs/latest/api/dns.html#dns_implementation_considerations","https://github.com/kubernetes/dns/issues/159#issuecomment-336449931","https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler","https://github.com/kubernetes/kubernetes/issues/56903","https://www.weave.works/blog/racy-conntrack-and-dns-lookup-timeouts","https://github.com/zalando-incubator/kubernetes-on-aws/pull/1228"],"title":{"en":"Cluster-wide DNS outage after CoreDNS pods were OOM-killed again and again under a burst of DNS queries","ko":"CoreDNS 파드가 메모리 한도 100Mi 에 걸려 OOM 으로 계속 죽으면서 쿠버네티스 클러스터 전체의 DNS 가 멈췄다"},"domain":["kubernetes","aws"],"impact":{"scope":"all web product and outfit pages of the Zalando Fashion Store; DNS for the entire Kubernetes cluster","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"The Kubernetes DNS setup was far from resilient enough: a single application could take down DNS for everything in the cluster. The blast radius of one application sending more DNS queries should be limited to one node at most, ideally to single pods.\n\nKnown Kubernetes DNS problems, such as Linux kernel races in conntrack handling and the ndots 5 problem, were familiar to the team and partly mitigated, but their combined effect on the DNS infrastructure was worse than the team had understood. Monitoring that runs inside the cluster was not robust enough: when the cluster's DNS broke, it could not page anyone, and recovery took longer than it should have.","ko":"쿠버네티스 DNS 구성은 충분히 튼튼하지 않았다. 애플리케이션 하나가 DNS 를 사실상 서비스 거부 공격하듯 몰아붙여 클러스터 전체를 멈추게 할 수 있어서는 안 된다. DNS 쿼리를 늘린 애플리케이션의 영향 범위는 적어도 노드 하나, 가능하면 파드 하나 안에 갇혀야 한다.\n\n팀은 conntrack 을 처리하는 리눅스 커널의 경쟁 상태나 ndots 5 문제 같은 쿠버네티스 DNS 문제를 대부분 알고 있었고, 일부는 완화책도 갖고 있었다. 하지만 이 문제들이 DNS 구성에 얼마나 크게 영향을 주는지는 제대로 알지 못했다.\n\n모니터링도 충분히 튼튼하지 않았다. 일부 알림은 클러스터 밖에서 돌았지만 대부분은 클러스터 안에서 돌았다. 그래서 DNS 가 무너졌을 때 당직 엔지니어가 자동으로 호출되지 않았고 복구 시간이 필요 이상으로 길어졌다."},"related":["IL-0005"],"sources":[{"url":"https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/docs/postmortems/jan-2019-dns-outage.md","kind":"github","license":"unknown","archived":"http://web.archive.org/web/20261004005813/https://github.com/zalando-incubator/kubernetes-on-aws/blob/dev/docs/postmortems/jan-2019-dns-outage.md"}],"summary":{"en":"The CoreDNS pods in the cluster had a memory limit of 100Mi, set from historical resource usage. When a retry spike at the aggregation layer turned into a burst of DNS queries, every CoreDNS pod went over that limit and was OOM-killed at once. Under the continuing load each restarted pod was killed again, and nothing could raise the limit automatically: Zalando had been running the Vertical Pod Autoscaler for some system components for a few months, but not for CoreDNS. The whole cluster lost DNS.\n\nSeveral properties of the aggregation layer multiplied its DNS queries. It is a Node.js application using the default Node.js DNS resolution, which caches no lookups. It ran with the default Kubernetes DNS configuration, so the \"ndots 5 problem\" turned every lookup of a cluster-external name into 10 DNS queries. Its HTTP client connection pool had a short maximum connection lifetime, chosen because some dependencies switch traffic through DNS, so it kept opening new connections and resolving names again.\n\nMost alerting ran inside the cluster and needed DNS, so nobody was paged automatically and the response was slow and manual. Once the Kubernetes on-call engineer was reached by phone, they raised the CoreDNS memory requests and limits by hand from 100Mi to 2000Mi. CoreDNS recovered and handled the load, the aggregation layer closed its circuit breakers, and the Fashion Store recovered completely.","ko":"2019년 1월 7일 월요일, Zalando Fashion Store 의 모든 웹 상품 페이지와 코디 페이지가 한 시간 넘게 오류를 냈다. 오류는 쿠버네티스 클러스터에서 도는 데이터 집계 계층 서비스에서 나왔고, 근본 원인은 클러스터 DNS 인 CoreDNS 의 전면 장애였다.\n\n하위 서비스 하나에서 타임아웃이 나자 클라이언트가 재시도했고, 집계 계층으로 들어오는 요청이 급증했다. 집계 계층은 Node.js 애플리케이션이고 Node.js 의 기본 DNS 해석 방식을 썼다. 이 방식은 DNS 조회 결과를 캐시하지 않는다. 애플리케이션은 쿠버네티스 기본 DNS 설정으로 돌아서 ndots 5 문제를 그대로 안고 있었다. 이 문제 때문에 클러스터 밖 이름을 한 번 조회할 때마다 DNS 쿼리가 10개 나갔다. DNS 를 바꿔 트래픽을 전환하는 서비스들이 있어서, HTTP 클라이언트 커넥션 풀은 연결의 최대 수명도 짧게 잡고 있었다. 연결을 계속 새로 열면서 DNS 요청이 더 늘었다.\n\nCoreDNS 파드의 메모리 한도는 과거 사용량을 근거로 100Mi 로 아주 작게 잡혀 있었다. 쿼리가 치솟자 CoreDNS 파드는 바로 OOM 으로 죽었다. 높은 쿼리 부하가 계속되는 데다 메모리 한도를 자동으로 올릴 방법이 없어 회복하지 못했다. Zalando 는 몇 달 전부터 일부 시스템 구성 요소에 Vertical Pod Autoscaler 를 쓰고 있었지만 CoreDNS 에는 쓰지 않았다. CoreDNS 에도 썼다면 파드가 자동으로 회복했을 수도 있었다.\n\nDNS 가 멈추자 클러스터 내부 모니터링도 멈춰 당직 호출이 늦어졌고, 대응도 느린 데다 사람이 손으로 해야 했다. 연락을 받은 쿠버네티스 당직 엔지니어는 CoreDNS 파드가 OOM 으로 죽고 있는 것을 확인하고 메모리 request 와 limit 을 100Mi 에서 2000Mi 로 직접 올렸다. CoreDNS 파드가 회복하자 집계 계층은 서킷 브레이커를 닫았고 Fashion Store 는 완전히 회복했다."},"trigger":{"en":"Calls from the aggregation layer to a single, not very important downstream service started timing out. The aggregation layer returned 404s for them, clients retried, and the resulting spike in requests became a spike in DNS queries to CoreDNS.","ko":"그리 중요하지 않은 하위 서비스 하나로 가는 호출이 타임아웃 나기 시작했다. 집계 계층이 이 호출에 404 로 응답하자 클라이언트가 재시도했고, 집계 계층으로 가는 요청과 그만큼의 DNS 쿼리가 갑자기 치솟았다."},"severity":"unknown","symptoms":["high-error-rate","dns-resolution-failures","oom-killed","monitoring-down"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"The on-call engineer for the aggregation layer phoned the on-call Kubernetes engineer. Automatic paging did not happen because the cluster's monitoring and alerting ran inside the cluster and failed along with DNS. The Kubernetes engineer then saw that the CoreDNS pods were being OOM-killed.","ko":"DNS 장애로 클러스터 내부 모니터링이 함께 멈춰 당직 호출이 자동으로 가지 않았다. 집계 계층 서비스 당직 엔지니어가 평소처럼 자동 호출에 기대지 못하고 쿠버네티스 담당 당직 엔지니어에게 직접 전화했다. 그래서 쿠버네티스 당직 엔지니어가 장애를 알기까지 예상보다 오래 걸렸다. 연락을 받은 엔지니어가 CoreDNS 파드가 OOM 으로 죽고 있는 것을 찾아냈다."},"components":["coredns","memory-limit","dns-resolver"],"root_cause":"capacity","remediation":[{"text":{"en":"Raised the CoreDNS memory requests and limits by hand from 100Mi to 2000Mi","ko":"쿠버네티스 당직 엔지니어가 CoreDNS 파드의 메모리 request 와 limit 을 100Mi 에서 2000Mi 로 직접 올렸다."}}],"action_items":[{"text":{"en":"Introduce a more resilient DNS setup: CoreDNS fronted by dnsmasq on every node","ko":"노드마다 dnsmasq 를 앞에 둔 CoreDNS 를 띄우는 방식으로 더 튼튼한 DNS 구성을 도입한다. 검토하고 시험한 다른 어떤 구성보다 이 방식의 성능이 좋았다."}},{"text":{"en":"Improve high-level alerting from outside the clusters, so that a cluster problem is known before or at the same time as applications are affected","ko":"클러스터 바깥에서 클러스터 전체 상태를 보는 알림을 강화한다. 팀은 클러스터 안 애플리케이션이 영향을 받기 전에, 늦어도 같은 시점에는 클러스터 이상을 알아야 한다."}},{"text":{"en":"Improve incident response processes and tooling so that any on-call engineer can escalate directly to other teams when automatic paging fails, for example through a chat bot","ko":"장애 대응 절차와 도구를 개선한다. 자동 당직 호출이 실패해도 당직 엔지니어가 다른 도구로 옮겨 가지 않고 다른 팀에 바로 에스컬레이션할 수 있게 한다. 채팅 봇으로 에스컬레이션을 단순하게 만드는 방안을 추진하고 있다."}}],"contributing":["limit-unknown","no-caching","retry-storm","default-config-unchanged","blast-radius-too-wide","monitoring-blind-spot"],"recurrence_of":null,"symptoms_text":{"en":"Customers of the Zalando Fashion Store got a high rate of errors on all web product pages and outfit pages for more than an hour. The errors came from the main data aggregation layer service, which runs in one of Zalando's Kubernetes clusters.\n\nIt started when calls from the aggregation layer to one of its many downstream services began to time out. The aggregation layer answered those timed-out calls with 404 responses, clients retried, and the number of requests to the aggregation layer spiked. The CoreDNS queries per second in the cluster rose by the same amount, and the memory usage of the CoreDNS pods climbed until all of them ran out of memory and were OOM-killed at the same moment.\n\nThe CoreDNS pods did not recover. Each restarted pod met the same constant query load, ran out of memory again and was killed again, so DNS was down for the entire cluster. The aggregation layer could no longer resolve any hostname, and because it had no DNS cache it opened its circuit breakers to its downstream services.\n\nThe cluster's internal monitoring went down at the same time, because pushing metrics and triggering alerts needs to reach external services. No automatic page went out. The on-call engineer for the aggregation layer had to phone the on-call Kubernetes engineer, who then found the CoreDNS pods being OOM-killed.","ko":"Zalando Fashion Store 의 모든 웹 상품 페이지와 코디 페이지가 한 시간 넘게 고객에게 오류를 많이 냈다. 오류는 쿠버네티스 클러스터 하나에서 도는 핵심 데이터 집계 계층 서비스에서 나왔다.\n\n시작은 집계 계층이 호출하는 여러 하위 서비스 가운데 하나에서 난 타임아웃이었다. 집계 계층은 타임아웃 난 호출에 404 로 응답했고, 클라이언트가 재시도하면서 집계 계층으로 들어오는 요청이 치솟았다. 이 요청 급증은 클러스터 DNS 인 CoreDNS 로 가는 DNS 쿼리 급증으로 그대로 이어졌다.\n\nDNS 쿼리가 몰리자 CoreDNS 파드의 메모리 사용량이 치솟았고, CoreDNS 파드들은 한꺼번에 메모리를 다 써서 OOM Killed 상태로 종료됐다. 파드는 첫 OOM 이후 회복하지 못하고 메모리가 바닥나 죽기를 반복했다. 그 결과 클러스터 전체의 DNS 가 완전히 멈췄다. 집계 계층 서비스는 어떤 호스트 이름도 해석하지 못했고 DNS 캐시도 없었기 때문에 하위 서비스로 가는 서킷 브레이커를 열었다.\n\nDNS 가 멈추자 클러스터 내부 모니터링도 함께 완전히 멈췄다. 이 모니터링은 알림을 보내고 지표를 올리려면 외부 서비스와 통신해야 했기 때문이다. 당직 호출이 자동으로 가지 않아, 집계 계층 서비스 당직 엔지니어가 쿠버네티스 담당 당직 엔지니어에게 직접 전화해야 했다."},"updated_at":"2026-11-10T00:00:31.133242Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0001","org_slug":"zalando"}