{"id":"IL-0072","org":"Nordstrom","date":"2017-12-15","docs":[],"title":{"en":"High etcd write latency made the API servers return stale data, and the production cluster flipped between two views of its state","ko":"etcd 쓰기 지연으로 API 서버가 오래된 데이터를 읽어 클러스터 상태가 두 가지로 번갈아 보였고, 주 운영 클러스터 전체가 약 4시간 멈췄다"},"domain":["kubernetes","aws"],"impact":{"scope":"full outage of the primary production Kubernetes cluster","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"etcd is extremely sensitive to write latency; once the database sync time crosses double digits of milliseconds, stale reads or badly degraded performance follow. Without quorum reads, a consistent store can still hand out stale data.\n\nDocumentation for a fast-moving system changes, so read it again regularly. Moving workloads to a new cluster is hard when they hold persistent volumes or when DNS records point at generated load balancer names.","ko":"etcd 는 쿼럼 읽기를 요청하지 않으면 오래된 데이터로 응답할 수 있고, 이 동작은 문서에 적혀 있다. 쿠버네티스 1.9 부터는 쿼럼 읽기가 기본이므로, 그 전 버전을 쓰면서 API 서버 설정을 직접 관리한다면 설정을 확인해야 한다. 쿼럼 읽기는 성능 걱정 때문에 처음에는 권장되지 않았지만, etcd 3.1 에서는 읽기가 디스크를 거치지 않아 부담이 크게 줄었다. etcd 는 쓰기 지연에 매우 민감하므로 데이터베이스 동기화 지연을 지켜봐야 한다. 고가용성 문서처럼 계속 바뀌는 문서는 꾸준히 다시 읽어야 한다. 로드 밸런서 이름을 직접 가리키는 DNS 레코드와 한 노드에 묶이는 볼륨은 클러스터 교체를 느리게 만든다."},"related":[],"sources":[{"url":"https://www.youtube.com/watch?v=xZO9nx6GBu0","kind":"talk","license":"unknown","archived":null}],"summary":{"en":"Nordstrom ran Kubernetes on AWS with a five-node control plane: five etcd instances, five API servers, and schedulers and controller managers in high-availability mode. In etcd, writes are always consistent, but a read is answered from the local member's data unless the client asks for a quorum read. When members fall far behind each other, those local answers are stale, and long replication lag can also cause repeated leader elections. The Kubernetes API server in its default configuration did not request quorum reads, and the high-availability documentation did not mention the setting until October of the year of the talk.\n\nIn this incident etcd was hit by high write latency. The API servers returned stale data, so kubectl output, the controller manager and service endpoints flipped between two states. The speakers stress that this was stale data, not a breakdown of etcd's consensus.\n\nThe outage on the primary production cluster lasted about four hours, until service was restored on a replacement cluster. Confusion and reluctance to replace the cluster added to the time, because teams used features that are hard to move. Each persistent volume can be attached to only one node, so volumes had to be freed from the old cluster one by one before they could bind in the new one. Some teams had pointed DNS records at the generated names of their Kubernetes load balancers and had to update them by hand to the new load balancers.\n\nThe fix is to make the API server request quorum reads. etcd 3.1 makes quorum reads much cheaper because they no longer hit disk, and quorum reads became the default in Kubernetes 1.9. The incident happened during working hours with the full team present, and the root cause analysis led to changes in the team's code and procedures.","ko":"Nordstrom 은 리뷰, 기프트 카드, 구매 주문 같은 마이크로서비스와 개발 도구, 로깅과 모니터링 스택 일부를 쿠버네티스에서 돌린다. 컨트롤 플레인은 etcd, API 서버, 스케줄러, 컨트롤러 매니저를 다섯 대씩 두는 고가용성 구성이었다. 어느 날 주 운영 클러스터가 전체 장애에 빠졌다.\n\netcd 는 쓰기를 항상 쿼럼을 거쳐 처리하지만, 읽기는 쿼럼 읽기를 명시적으로 요청하지 않으면 질의받은 로컬 멤버만 아는 데이터로 응답할 수 있다. 평소에는 작은 복제 지연 정도로 끝난다. 하지만 쓰기 지연이 커지면 이 차이가 크게 벌어지고, 리더 선출 타임아웃을 거듭 넘겨 선출이 연달아 일어날 수 있다. 당시 쿠버네티스 API 서버는 기본 설정에서 쿼럼 읽기를 요청하지 않았고, 고가용성 문서에도 그 내용이 없었다. etcd 쓰기 지연이 커지자 API 서버가 오래된 데이터를 읽었고 클러스터 상태가 두 가지로 번갈아 보였다. 발표자는 합의 모델 자체가 깨지는 스플릿 브레인은 아니었다고 밝혔다.\n\n팀은 진단에 오래 매달리다가 결국 새 클러스터로 교체해 서비스를 되살렸고, 장애는 약 4시간 이어졌다. 교체도 쉽지 않았다. 볼륨은 한 노드에만 연결될 수 있어서 옛 클러스터에서 하나씩 손으로 풀어 줘야 새 클러스터에서 바인딩할 수 있었다. 로드 밸런서를 쓰던 몇몇 팀은 쿠버네티스가 만든 임시 이름을 DNS 레코드로 가리키고 있어서, 새 클러스터의 로드 밸런서 이름으로 레코드를 손으로 고쳐야 했다.\n\n장애는 업무 시간에 일어나 팀 전체가 대응할 수 있었다. 팀은 근본 원인까지 분석했고, 그 결과 시스템을 더 잘 이해하게 됐고 코드와 절차도 크게 고쳤다."},"trigger":{"en":"etcd write latency rose far above the healthy single-digit milliseconds; the speakers say trouble starts once the database sync time crosses double digits or several hundred milliseconds.","ko":"etcd 의 쓰기 지연이 크게 늘었다. 발표자는 정상인 etcd 의 쓰기 지연이 한 자릿수 밀리초이고, 데이터베이스 동기화 지연이 두 자릿수나 수백 밀리초를 넘으면 문제가 시작된다고 설명했다."},"severity":"unknown","symptoms":["stale-api-reads","stale-service-routing","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":"4h"},"date_kind":"published","detection":{"en":"A user reported pods starting up and vanishing.","ko":"팀은 사용자가 파드가 떴다가 사라진다고 알려 와서 장애를 알게 됐다. 팀이 kubectl 로 확인하자 같은 명령이 실행할 때마다 다른 결과를 냈다."},"components":["etcd","kube-apiserver","ingress","persistent-volume"],"root_cause":"software-bug","remediation":[{"text":{"en":"restored service on a replacement cluster","ko":"새 클러스터를 만들어 애플리케이션을 다시 배포하고 서비스를 복구했다."}},{"text":{"en":"freed persistent volumes from the old cluster one by one so they could bind in the new cluster","ko":"볼륨을 옛 클러스터에서 하나씩 손으로 풀어 새 클러스터에서 바인딩했다."}},{"text":{"en":"updated DNS records that pointed at the old cluster's load balancer names","ko":"로드 밸런서를 쓰던 팀들이 DNS 레코드를 새 클러스터의 로드 밸런서 이름으로 손으로 바꿨다."}}],"action_items":[{"text":{"en":"Make the Kubernetes API server request quorum reads from etcd","ko":"API 서버가 etcd 에서 읽을 때 항상 쿼럼 읽기를 하도록 설정한다."}}],"contributing":["default-config-unchanged"],"recurrence_of":null,"symptoms_text":{"en":"A user reported that pods were starting up and then vanishing. When the cluster operators looked, kubectl get pods and kubectl get nodes returned two different sets of data that alternated back and forth, as if there were two opinions of the cluster's state.\n\nThe control loops acted on the conflicting data. The controller manager, querying API servers and getting alternating answers, kept trying to converge and spun up and terminated thousands of pods. Service endpoints thrashed, and the ingress controller, responding to the conflicting signals, did very bad things to the traffic that should have flowed through it.\n\nIt was a full outage of the primary production cluster: not just down, but violently wrong. The team spent a long time troubleshooting before deciding to replace the cluster.","ko":"사용자가 파드가 떴다가 사라진다고 클러스터 운영 팀에 알렸다.\n\nkubectl get pods 를 실행하면 서로 다른 두 결과가 번갈아 나왔다. kubectl get nodes 도 마찬가지였다. 클러스터 상태를 두고 서로 다른 두 의견이 있는 것처럼 보였다.\n\n제어 루프도 잘못 움직이기 시작했다. 컨트롤러 매니저가 번갈아 받는 데이터에 반응해 수천 개 파드를 띄웠다가 종료했다. 서비스 엔드포인트가 계속 바뀌었다. 인그레스 컨트롤러도 엇갈린 신호에 반응했고, 그 때문에 인그레스를 거쳐 가야 할 트래픽이 크게 망가졌다.\n\n팀은 원인을 몰라 한참 혼란스러웠다. 클러스터를 교체하면 애플리케이션을 새로 배포해야 했기 때문에 교체를 망설이며 진단에 오래 매달렸다."},"updated_at":"2026-11-10T00:00:39.531753Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0072","org_slug":"nordstrom"}