{"id":"IL-0277","org":"Elastic","date":"2019-02-04","docs":[],"title":{"en":"Patching Elastic Cloud's ZooKeeper ensemble in us-east-1 lost quorum, and hosted clusters and Kibana went unreachable","ko":"Elastic Cloud us-east-1 의 ZooKeeper 조정 계층이 쿼럼을 잃어 Elasticsearch 배포에 6시간 넘게 접속하지 못하고 Kibana 는 18:44 UTC 까지 불안정했다"},"domain":["database","aws"],"impact":{"scope":"Elastic Cloud in AWS us-east-1: Elasticsearch deployments partially or completely unavailable from 02:50 to 09:28 UTC, cluster create, modify and delete unavailable while ZooKeeper was down from 02:50 to 07:17 UTC, User Console degraded until 07:17 UTC, Kibana disrupted until 18:44 UTC","users":"100% of customers in us-east-1","region":"us-east-1"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Service metrics that reported the ZooKeeper hosts healthy were not enough to show the state of individual hosts or of the ensemble, so they gave a false signal that maintenance could continue. Because the platform depended on the coordination layer, losing its quorum caused a major service disruption in which every Elasticsearch cluster in the region saw some failures, and fixes that worked one by one fell short at the scale of the failure.","ko":"서비스 지표가 호스트를 정상으로 보고해 유지보수를 이어 가도 된다는 신호를 줬지만, 그 지표로는 개별 호스트와 조정 계층 전체의 상태를 충분히 알 수 없었다. 다른 플랫폼 구성 요소가 조정 계층에 의존해서 조정 계층의 불안정과 쿼럼 상실이 큰 서비스 중단으로 번졌다. 원인을 찾는 동안 관련 없는 문제들을 만나 해결이 더 늦어졌고, 하나하나는 성공한 완화 조치도 장애의 규모 때문에 한동안 효과가 없었다."},"related":[],"sources":[{"url":"https://www.elastic.co/blog/elastic-cloud-incident-report-feburary-4-2019","kind":"blog","archived":"https://web.archive.org/web/20261031143317/https://www.elastic.co/blog/elastic-cloud-incident-report-feburary-4-2019"}],"summary":{"en":"In each Elastic Cloud region an allocator layer hosts the Elasticsearch and Kibana instances, a coordination layer on a three-node Apache ZooKeeper ensemble holds the system state and the location of every cluster's nodes, and a proxy layer behind load balancers routes traffic by each cluster's virtual host name, reading from a ZooKeeper observer layer that replicates the coordination layer. An ancillary TLS proxying container on every allocator host lets services such as Kibana reach their Elasticsearch cluster securely. During routine maintenance in us-east-1 an engineer replaced ZooKeeper hosts following the documented procedure, met an unfamiliar error, and proceeded because service metrics reported the hosts healthy; those metrics did not show the real state of individual hosts or of the ensemble. Under the load of reconnecting clients the leader shut down at 02:50, and the ensemble could not keep quorum. Because other platform components depend on the coordination layer, losing quorum caused a major disruption, and the proxy layer was impacted as well.\n\nResponders restarted internal services at 03:22 and 06:00 to reduce client connection problems, rolled forward the last ensemble member at 04:07, then cut off client connections at 04:39 and rolled back the third and second members as their hosts became unstable. A runc bug that triggered a kernel OOPS and CPU softlocks when entering the network namespace of the TLS proxying container made two members unresponsive, and their hosts had to be rebooted. At 07:10 a stable quorum formed once client load and operator debugging on the hosts were reduced, and traffic was reintroduced at 07:17. The flood of external reconnections then overloaded the proxy layer and its observer layer; increasing the observers' initLimit setting, which controls how long followers may take to connect and sync to a leader, stabilized them, and at 09:19 the proxy fleet was scaled up and out. Elasticsearch access was restored at 09:28 UTC and the error rate returned to pre-incident levels.\n\nKibana stayed down longer. The internal TLS proxying service on Kibana hosts could not forward requests, and previously unknown Kibana bugs leaked connections, filling the nf_conn_track table, and amplified HTTP requests back to the load balancing layer. At 12:30 a runbook restarted the internal proxying containers and Kibana instances and applied sysctl limits, and Kibana access was fully restored at 18:44 UTC. Elastic notes that unrelated issues found during the investigation delayed resolution, and that individually successful mitigations were temporarily ineffective at the scale of the failure.","ko":"Elastic Cloud 백엔드는 세 계층으로 이루어진다. allocator 계층은 리전마다 Elasticsearch 와 Kibana 인스턴스를 올린다. 조정 계층은 세 노드짜리 Apache ZooKeeper 앙상블로, 시스템 상태와 각 클러스터 노드의 위치를 담는다. 나머지 하나가 프록시 계층이다. 프록시 계층은 클러스터의 가상 호스트 이름으로 트래픽을 보내는 프록시·라우팅 계층과, 조정 계층의 부하를 덜려고 그 복제본을 두는 ZooKeeper observer 계층으로 나뉜다. Kibana 같은 서비스는 allocator 호스트마다 도는 보조 컨테이너로 TLS 를 써서 Elasticsearch 클러스터와 통신한다.\n\n2월 4일 us-east-1 에서 조정 계층을 정기 패치했다. 다른 12개 리전과 같은 문서화된 절차였다. 서비스 지표도 호스트가 정상이라고 보고했지만, 그 지표로는 개별 호스트와 조정 계층 전체의 상태를 알 수 없었다. 멤버 두 대를 바꾼 뒤 앙상블에 부하가 걸렸고, 02:50 에 리더가 들어오는 연결을 감당하지 못하고 종료되면서 앙상블이 쿼럼을 잃었다. 다른 플랫폼 구성 요소가 조정 계층에 의존하므로 이 일은 큰 서비스 중단으로 번졌다. 팀은 내부 서비스를 일괄 재시작했고, 03:31 에 쿼럼이 안정되자 마지막 멤버 패치를 이어 갔다. 그러나 호스트가 불안정해져 앙상블을 클라이언트 연결에서 떼고 두 멤버를 롤백했다. 이 과정에서 TLS 프록시 컨테이너의 네트워크 네임스페이스에 들어갈 때 runc 가 kernel OOPS 와 CPU softlock 을 일으키는 처음 보는 버그를 만나 호스트를 재부팅해야 했다. 07:10 에 클라이언트 부하와 운영자의 디버깅 작업을 줄여 쿼럼을 안정시키고 07:17 에 트래픽과 고객 컨트롤 플레인을 되살렸다.\n\n그 뒤 외부 재연결이 몰려 프록시 계층과 observer 계층이 불안정해졌다. 팀은 팔로워가 리더에 연결해 동기화할 수 있는 시간을 정하는 initLimit 설정을 늘려 observer 계층을 안정시켰다. 09:19 에는 프록시를 늘려 3배로 늘어난 요청을 받았다. 09:28 에 Elasticsearch 배포 접속이 돌아왔다. Kibana 는 더 오래 망가져 있었다. 내부 TLS 프록시가 요청을 넘기지 못했고, Kibana 가 실패한 소켓을 닫지 않아 nf_conn_track 테이블이 가득 찼다. 12:30 에 내부 프록시 컨테이너와 Kibana 인스턴스를 재시작하고 sysctl 한도를 거는 런북을 만들어 적용했고, 18:44 에 Kibana 접속이 완전히 돌아왔다. 조사에서 Kibana 의 연결 누수와, 로드 밸런싱 계층으로 HTTP 요청을 몇 배로 늘려 보내는 버그들이 새로 드러났다. us-east-1 고객 100% 가 영향을 받았다."},"trigger":{"en":"A routine patching procedure in AWS us-east-1 replaced the first and second members of the three-node ZooKeeper ensemble with patched hosts between 02:25 and 02:45 UTC, following the same documented process used in the other 12 regions.","ko":"2019년 2월 4일 AWS us-east-1 리전에서 조정 계층의 ZooKeeper 를 정기 패치하면서 02:25~02:45 에 앙상블 멤버 두 대를 패치한 호스트로 바꿨다."},"severity":"unknown","symptoms":["high-error-rate","service-unavailable","intermittent-timeouts","node-kernel-panic"],"timeline":{"detected_after":"0m","resolved_after":"15h 54m"},"date_kind":"incident","detection":{"en":"An increase in HTTP 5xx errors on the load balancer was identified at 02:50 UTC, the time the ZooKeeper ensemble lost its leader.","ko":"Elastic 은 02:50 UTC 에 로드 밸런서에서 HTTP 5xx 오류가 늘어난 것을 확인했다."},"components":["coordination-store","container-runtime","linux-kernel","elasticsearch-opensearch","conntrack"],"root_cause":"software-bug","remediation":[{"text":{"en":"Restarted internal services in sweeping restarts at 03:22 and 06:00 to reduce client connection problems","ko":"03:22 와 06:00 에 클라이언트 연결 문제를 줄이려고 내부 서비스를 일괄 재시작했다."}},{"text":{"en":"Isolated the ZooKeeper ensemble by cutting off client connections","ko":"04:39 에 클라이언트 연결을 끊어 앙상블을 떼어 놓고 시스템을 안정시켰다."}},{"text":{"en":"Rolled back the remaining ensemble member after its host became unstable","ko":"04:46 에 호스트가 불안정해지자 남은 앙상블 멤버의 롤백을 시작했다."}},{"text":{"en":"Rolled back the second ensemble member after its host became unstable","ko":"05:25 에 호스트가 불안정해 두 번째 앙상블 멤버도 롤백하기 시작했다."}},{"text":{"en":"Rebooted the ZooKeeper hosts made unresponsive by the runc CPU softlocks","ko":"runc 버그로 응답하지 않게 된 ZooKeeper 앙상블 멤버 두 대를 재부팅했다."}},{"text":{"en":"Re-established ZooKeeper quorum by removing client load and operator debugging, then reintroduced client load gradually","ko":"가능한 곳에서 클라이언트 부하를 걷어 내 앙상블이 안정될 시간을 준 뒤, 클라이언트 부하를 조금씩 다시 들여 쿼럼을 다시 세웠다."}},{"text":{"en":"Increased the initLimit setting of the ZooKeeper observer layer used by the proxies","ko":"프록시가 쓰는 ZooKeeper observer 계층의 initLimit 설정을 늘려 observer 계층을 안정시켰다."}},{"text":{"en":"Scaled the proxy fleet up and out to handle the increased traffic","ko":"늘어난 Elasticsearch 클러스터 트래픽을 받도록 프록시를 키우고 대수를 늘렸다."}},{"text":{"en":"Applied a runbook that restarted the internal proxying containers and Kibana instances and set sysctl limits","ko":"내부 프록시 컨테이너와 Kibana 인스턴스를 재시작하고 sysctl 한도를 거는 런북을 만들어 적용했다."}}],"action_items":[{"text":{"en":"Reduce the dataset size carried by the ZooKeeper ensembles by over 60%","ko":"ZooKeeper 앙상블이 담는 데이터셋을 60% 넘게 줄였다."}},{"text":{"en":"Reduce the number of health checks and intervals needed before a healthy proxy node joins the load balancing layer","ko":"정상으로 표시된 프록시 노드가 로드 밸런싱 대상에 들어가는 데 필요한 헬스 체크 횟수와 간격을 줄였다."}},{"text":{"en":"Stop counting ZooKeeper connectivity in the proxy health checks once a proxy has its configuration and routes","ko":"프록시 헬스 체크에서 ZooKeeper 연결 여부를 빼, 초기화를 마친 프록시가 ZooKeeper 상태 때문에 비정상이 되지 않게 한다."}},{"text":{"en":"Canary the rewritten proxy v2 architecture, which decouples the proxy service from ZooKeeper state, in production regions","ko":"프록시 서비스를 ZooKeeper 상태에서 떼어 낸 v2 구조의 프록시 계층을 운영 리전에서 카나리로 시험한다."}},{"text":{"en":"Improve visibility into ZooKeeper with better logging and metrics for the ZooKeeper processes, the JVM and the container and host systems","ko":"내부 로깅을 늘리고 ZooKeeper 프로세스, JVM, 컨테이너와 호스트 시스템의 지표를 더해 ZooKeeper 의 관측성을 높인다."}},{"text":{"en":"Make Kibana route discovery more resilient","ko":"Kibana 의 경로 탐색 방식을 더 튼튼하게 만든다."}},{"text":{"en":"Fix the Kibana issues that amplify internal HTTP requests during availability events","ko":"가용성 사고 중에 Kibana 가 내부 HTTP 요청을 몇 배로 늘려 보내는 문제를 찾아 고친다."}},{"text":{"en":"Fix the Kibana issues that leak connections","ko":"Kibana 의 연결 누수 문제를 찾아 고친다."}},{"text":{"en":"Make internal monitoring agents use backoff with jitter","ko":"내부 모니터링 에이전트가 지터를 넣은 백오프를 제대로 쓰지 않던 문제를 찾아 완화했다."}},{"text":{"en":"Formalize a maintenance matrix with set timeframes per region, so failures of critical services fall in non-critical times","ko":"문제가 생겨도 덜 중요한 시간대에 드러나도록, 유지보수 일정표를 정식으로 만들어 리전마다 정한 시간대에만 작업하게 했다."}},{"text":{"en":"Give runbooks for coordination layer maintenance a better decision criteria matrix","ko":"조정 계층의 정기 유지보수를 다루는 런북에 더 나은 판단 기준표를 넣는다."}},{"text":{"en":"Set up a feedback loop with support leadership for incident communications through support and the status page","ko":"지원 창구와 상태 페이지로 나가는 안내를 두고 지원 리더십 팀과 피드백 루프를 만들었다."}}],"contributing":["monitoring-blind-spot","retry-storm","probe-depends-on-dependency","overlapping-failures","manual-action-worsened"],"recurrence_of":null,"symptoms_text":{"en":"On 4 February 2019 at about 02:50 UTC, Elastic Cloud customers with deployments in AWS us-east-1 lost stable access to their Elasticsearch clusters, and an increase in HTTP 5xx errors showed on the load balancer. Deployments were partially or completely unavailable until 09:28 UTC, Kibana access was disrupted for most customers in the region, and the Elastic Cloud User Console timed out when customers tried to view their deployments.\n\nThe ZooKeeper ensemble that forms Elastic Cloud's coordination layer was the source. At 02:45, after two of its three members had been replaced with patched hosts, it came under high CPU, IO and memory load with many client connection failures and reconnects, then seemed to settle. At 02:50 it held a leader election; the logs showed the leader overwhelmed by incoming connections and shutting down, and the ensemble could not regain and keep quorum under the request load. Rising latency inside the ensemble caused more client reconnections and made the ZooKeeper observer layer used by the proxies unstable. Customers could not create, modify or delete clusters, and every Elasticsearch cluster in the region saw some failures.\n\nQuorum seemed stable at 03:31 and the replacement of the last member went ahead at 04:07, but at 04:15 that host became unresponsive at the system level. Hosts kept becoming unstable during the rollbacks, and at 05:48 two ensemble members were unresponsive from CPU softlocks caused by runc bugs nobody had seen before. At 06:45 the cause of the host instability was found: when runc entered the network namespace of a TLS proxying service container, it triggered a kernel OOPS and CPU softlocks lasting minutes. Service metrics had reported the hosts as healthy before the maintenance went ahead.\n\nAfter traffic came back at 07:17, a large wave of external reconnections put high load on the proxy layer and destabilized the proxy ZooKeeper observer layer, so customers still could not connect to their clusters; requests to the proxy layer rose three-fold. Kibana instances then showed much higher CPU than usual from the internal TLS proxying service, which could not forward requests to the proxy layer. Kibana did not close sockets after a failed attempt, so open sockets grew until they filled the nf_conn_track table.","ko":"2019년 2월 4일 02:50 UTC 무렵부터 AWS us-east-1 의 Elastic Cloud 고객이 클러스터에 접속하기 어려워졌고, 로드 밸런서에서 HTTP 5xx 오류가 늘었다. 고객 대부분은 Kibana 에도 접속하지 못했고, Elastic Cloud User Console 은 배포 목록을 볼 때 타임아웃이 늘었다.\n\n조정 계층의 ZooKeeper 앙상블 멤버 두 대를 패치한 호스트로 바꾼 직후인 02:45 에 앙상블에 CPU, IO, 메모리 부하가 크게 걸렸고 클라이언트 연결 실패와 재연결이 많이 일어났다. 몇 분 뒤 부하, 미처리 요청 수, 연결 실패가 줄어 안정된 것처럼 보였다.\n\n02:50 에 앙상블이 리더 선출을 했다. 로그에는 리더가 들어오는 연결을 감당하지 못하고 종료됐다고 남았다. 앙상블은 들어오는 요청 부하 때문에 쿼럼을 빨리 다시 세우지도, 유지하지도 못했다. 앙상블 내부의 지연이 늘자 클라이언트가 더 재연결했고 프록시용 ZooKeeper observer 계층도 불안정해졌다.\n\n03:31 에 쿼럼이 안정되자 팀은 마지막 멤버의 패치를 이어 가기로 했지만, 04:15 에 그 멤버가 시스템 수준에서 응답하지 않았다. 05:48 에는 처음 보는 runc 버그로 앙상블 멤버 두 대에 CPU softlock 이 생겨 시스템이 응답하지 않았다. 팀은 06:45 에 TLS 프록시 서비스 컨테이너의 네트워크 네임스페이스에 들어갈 때 runc 가 kernel OOPS 와 몇 분씩 이어지는 CPU softlock 을 일으킨다는 것을 찾았다.\n\n07:17 에 트래픽을 다시 받은 뒤에는 외부 재연결이 몰려 프록시 계층 부하가 높아졌다. 그 때문에 프록시용 ZooKeeper observer 계층이 불안정해져 고객은 여전히 클러스터에 접속하지 못했다. 프록시 계층으로 오는 요청은 3배로 늘었다.\n\n09:28 에 Elasticsearch 배포 접속이 돌아온 뒤에도 Kibana 호스트의 CPU 사용량이 평소보다 훨씬 높았고, 주된 원인은 내부 TLS 프록시 서비스였다. 내부 TLS 프록시가 요청을 프록시 계층으로 넘기지 못해 Kibana 가 Elasticsearch 클러스터에 연결하지 못했다. Kibana 는 연결에 실패한 뒤 소켓을 닫지 않아 열린 소켓이 한없이 늘었다. 그 때문에 nf_conn_track 테이블이 가득 차 내부 프록시 컨테이너에 큰 부담이 됐다."},"updated_at":"2026-11-14T12:33:52.874979Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0277","org_slug":"elastic"}