{"id":"IL-0587","org":"Elastic","date":"2019-01-18","docs":[],"title":{"en":"Elastic Cloud proxies in AWS eu-west-1 flooded an overloaded ZooKeeper and ran out of memory, cutting cluster access","ko":"Elastic Cloud AWS eu-west-1 리전의 클러스터 접속이 약 3시간 심하게 저하되고 20분 동안 완전히 끊겼다"},"domain":["database","aws","networking"],"impact":{"scope":"Access to Elastic Cloud deployments (Elasticsearch clusters and Kibana instances): severely degraded for about 3 hours, and all deployments in the region unavailable for about 20 minutes","users":"Elastic Cloud customers with deployments in AWS eu-west-1","region":"AWS eu-west-1 (Ireland)"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://www.elastic.co/blog/elastic-cloud-january-18-2019-incident-report","kind":"blog","archived":"https://web.archive.org/web/20240529175708/https://www.elastic.co/blog/elastic-cloud-january-18-2019-incident-report"}],"summary":{"en":"The Elastic Cloud backend in each region has three layers: allocators that host Elasticsearch and Kibana instances, a coordination layer on a 3-node Apache ZooKeeper ensemble, and a proxy and routing layer fronted by load balancers. Each proxy is a direct ZooKeeper client through the Apache Curator framework and uses Curator's TreeCache to mirror the ZooKeeper subtree that holds a znode for every deployment, its nodes running in Docker containers and their properties, with watches keeping the mirror current. Elastic used only the event listener part of TreeCache to maintain its own application-level mirrors.\n\nWhen a connection is lost and re-established, TreeCache walks the whole mirrored tree and sends one request per znode to reload its data and register a watch, all in one big batch. Some trees have tens of thousands of znodes, and a reconnect during a refresh sends the whole batch again. On 18 January the high CPU on the ZooKeeper leader caused client disconnections, the proxies flooded the servers with refresh batches, filled their incoming request queues and made the GC pauses worse, and the requests waiting in the clients' unbounded outgoing queues led to out of memory errors in the proxies. This chain kept the ensemble from recovering and ended in a complete outage of the region at 12:42 UTC.\n\nDuring the 20-minute full outage the SREs scaled the proxy layer up and out to take the surging client traffic at the load balancers, triggered a ZooKeeper leader election and stabilised the ensemble; availability returned at 13:03 UTC and the incident was closed after a monitoring period. Elastic had been building TreeWatcher, an API-compatible rewrite of TreeCache, for a few months before the incident. TreeWatcher does not refresh the tree when the ZooKeeper session is resumed, walks the tree only as the server answers so the server sets the pace, and cancels a refresh in progress when the session is lost instead of piling up batches. Its gradual rollout had begun in December 2018, and the cutover of the proxy layer was scheduled for the week of January 21, 2019. By January 24, 2019 TreeWatcher had replaced TreeCache across all Elasticsearch Service providers and regional points of presence.","ko":"Elastic Cloud 의 리전마다 백엔드는 세 계층으로 이루어진다. allocator 계층은 Elasticsearch 와 Kibana 인스턴스를 올리는 호스트다. 조정 계층은 3노드 Apache ZooKeeper 앙상블이고, 시스템 상태와 각 클러스터 노드와 Kibana 인스턴스의 실제 위치를 담고 있다. 프록시·라우팅 계층은 클러스터의 가상 호스트 이름을 보고 트래픽을 보내고, 그 앞에는 여러 프록시 인스턴스로 부하를 나누는 로드 밸런서가 있다. 프록시는 Apache Curator 프레임워크로 ZooKeeper 에 직접 붙는 클라이언트다. 프록시는 모든 배포와 그 노드를 나타내는 znode 하위 트리 전체를 TreeCache 로 감시한다.\n\nTreeCache 는 ZooKeeper 와의 연결이 끊겼다가 다시 붙으면 미러 트리 전체를 돌면서 노드마다 데이터를 다시 읽고 watch 를 다시 거는 요청을 한꺼번에 큰 묶음으로 보낸다. TreeCache 하나에 znode 가 수만 개일 수 있고, 새로 고침 도중 연결이 또 끊기면 같은 묶음을 다시 보낸다. 클라이언트는 정해진 시간 안에 heartbeat 를 받지 못하면 연결이 끊긴 것으로 보므로, ZooKeeper 서버가 과부하로 긴 GC 멈춤을 겪어도 연결이 불안정해진다. 그러면 프록시가 요청을 쏟아내 서버의 요청 큐가 차고 GC 가 더 나빠진다. 서버가 응답을 못 보내는 사이에는 클라이언트의 크기 제한 없는 송신 큐에 요청이 쌓여 클라이언트가 메모리 부족에 빠진다. 이번 장애에서는 09:09 UTC 에 리더 서버의 CPU 가 높은 임계치에 닿아 클라이언트 연결이 끊겼고, 09:35 UTC 부터 프록시가 쌓인 새로 고침 요청 때문에 자원이 바닥나 메모리 부족 상태가 됐다. 이 악순환이 12:42 UTC 의 완전 중단으로 이어졌다.\n\nSRE 들은 12:42 UTC 부터 13:02 UTC 까지의 완전 중단 동안 프록시 계층을 수직과 수평으로 늘리고, 리더 선출을 일으켜 ZooKeeper 앙상블을 안정시켰다. 13:03 UTC 에 클러스터 가용성이 돌아왔고, 지켜보는 시간을 거친 뒤 장애를 해결로 닫았다. Elastic 은 사고 몇 달 전부터 TreeCache 와 API 가 호환되도록 새로 쓴 TreeWatcher 를 만들고 있었다. TreeWatcher 는 재개된 세션에서는 트리를 새로 고치지 않고, 부모 노드의 응답을 받은 뒤에만 자식 노드의 요청을 보낸다. 또 새로 고침 도중 세션을 잃으면 진행 중인 새로 고침을 취소하고 새로 시작한다. Elastic 은 TreeWatcher 를 2018년 12월부터 관리 플레인과 컨트롤 플레인, 그다음 데이터 플레인 순서로 배포하고 있었고, 프록시 계층의 전환은 2019년 1월 21일 주로 잡혀 있었다."},"trigger":{"en":"At 09:09 UTC the leader server of the ZooKeeper ensemble in the eu-west-1 coordination layer hit a high CPU threshold, which caused client disconnections.","ko":"09:09 UTC 에 ZooKeeper 앙상블 리더 서버의 CPU 사용량이 높은 임계치에 닿으면서 클라이언트 연결이 끊긴 것이 시작이었다."},"severity":"unknown","symptoms":["database-overloaded","service-unavailable","oom-killed"],"timeline":{"detected_after":"26m","resolved_after":"3h 54m"},"date_kind":"incident","detection":{"en":"The on-call SREs received alerts about service degradation in the coordination layer at 09:35 UTC, after the high CPU on the ZooKeeper leader had caused client disconnections, and started a production incident.","ko":"09:35 UTC 에 당번 SRE 들이 조정 계층의 서비스 저하 알림을 받았고, 내부 절차에 따라 운영 장애를 열었다."},"components":["coordination-store","service-discovery"],"root_cause":"software-bug","remediation":[{"text":{"en":"Scaled the proxy layer up and out to take the surging client traffic hitting the load balancers","ko":"급증한 클라이언트 트래픽을 받도록 프록시 계층을 수직과 수평으로 늘렸다."}},{"text":{"en":"Triggered a leader election in the ZooKeeper ensemble and stabilised it","ko":"리더 선출을 일으켜 조정 계층의 ZooKeeper 앙상블을 안정시켰다."}}],"action_items":[{"text":{"en":"Replace Curator's TreeCache with TreeWatcher across all Elasticsearch Service providers and regions","ko":"모든 Elasticsearch Service 제공자와 리전 거점에서 TreeCache 를 TreeWatcher 로 바꾼다. 2019년 1월 24일에 교체를 마쳤다."}},{"text":{"en":"Contribute TreeWatcher upstream to the Apache Curator project","ko":"운영에서 검증된 TreeWatcher 를 Apache Curator 프로젝트에 기여할 계획이다."}},{"text":{"en":"Roll out a new proxy layer that keeps the routing table in a versioned local file, so it can route traffic while the ZooKeeper connection is lost or flaky","ko":"다음 버전의 프록시 계층은 ZooKeeper 에 덜 의존하고, 라우팅 테이블을 버전을 매긴 로컬 파일에 캐시해 ZooKeeper 연결이 끊기거나 불안정해도 트래픽을 보낼 수 있게 한다. 앞으로 몇 달에 걸쳐 모든 리전에 차례로 배포한다."}}],"contributing":["retry-storm","known-bug-unpatched"],"recurrence_of":null,"symptoms_text":{"en":"On 18 January 2019 from 09:09 UTC the leader of the three-node Apache ZooKeeper ensemble behind Elastic Cloud in AWS eu-west-1 (Ireland) ran at heavy CPU load, and its clients lost their connections. From about 10:08 UTC customers in the region had severely degraded access to their Elasticsearch and Kibana deployments for roughly 3 hours, and for about 20 minutes every deployment in the region was unavailable.\n\nThe on-call SREs received alerts about service degradation in the coordination layer at 09:35 UTC. The coordination layer is the ZooKeeper ensemble that holds the system state and the location of every cluster's nodes and Kibana instances. The proxy and routing layer, which sits behind the load balancers and routes HTTP requests and TCP connections to each deployment's nodes, depends on it to keep its routing table consistent, and it went into a degraded state. The proxies saw flaky connections to ZooKeeper: a proxy treats the connection as lost when heartbeat messages do not arrive in time, which also happens when the ZooKeeper server is overloaded and in long GC pauses.\n\nFrom 09:35 UTC the proxies, which are ZooKeeper clients built on the TreeCache of the Apache Curator framework, ran short of resources as refresh requests piled up, and the proxy layer hit out of memory conditions. The proxy layer deteriorated further after 10:30 UTC, and between 12:42 and 13:02 UTC no cluster in eu-west-1 was available. Cluster availability came back at 13:03 UTC.\n\nMitigating actions, stopping clients from reconnecting and going through an initialisation phase again, did not let the ZooKeeper quorum stabilise, because reconnecting proxies kept sending large batches of refresh requests to the servers.","ko":"2019년 1월 18일 09:35 UTC 에 Elastic Cloud 의 당번 SRE 들이 AWS eu-west-1 리전 조정 계층의 서비스 저하 알림을 받았다. 10:08 UTC 무렵부터 이 리전에 배포를 둔 고객은 약 3시간 동안 자기 클러스터에 접속하기가 매우 어려워졌다.\n\n조정 계층은 3노드 Apache ZooKeeper 앙상블이다. 09:09 UTC 에 이 앙상블의 리더 서버에 CPU 부하가 크게 걸려 높은 CPU 임계치에 닿았고, ZooKeeper 클라이언트들의 연결이 끊기기 시작했다.\n\n트래픽을 각 클러스터 노드로 보내는 프록시·라우팅 계층은 ZooKeeper 와의 연결이 붙었다 끊겼다를 반복했고, 프록시가 쓰는 클라이언트 라이브러리는 이 상황을 제대로 다루지 못했다. 프록시는 연결이 다시 붙을 때마다 큰 묶음의 데이터 새로 고침 요청을 ZooKeeper 로 보냈고, 응답을 받지 못한 요청이 프록시 안에 쌓여 메모리 부족 오류가 났다.\n\n장애는 시간이 갈수록 나빠졌다. 10:08 UTC 에 프록시·라우팅 계층이 저하 상태에 들어갔고 10:30 UTC 부터 더 나빠졌다. 12:42 UTC 부터 13:02 UTC 까지 20분 동안 리전의 모든 배포를 쓸 수 없었고, 13:03 UTC 에 클러스터 가용성이 돌아왔다.\n\nSRE 들은 클라이언트가 다시 연결하지 못하게 막고 초기화 단계를 다시 밟게 했다. 그러나 이 조치로는 ZooKeeper 쿼럼이 안정되지 않았다."},"updated_at":"2026-11-14T12:34:23.321238Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0587","org_slug":"elastic"}