{"id":"IL-0172","org":"Devsisters","date":"2021-02-19","docs":[],"title":{"en":"An AWS Tokyo data centre cooling failure took down 6 of 60 CockroachDB nodes on Kubernetes within 22 minutes, and ranges lost quorum because the Helm-deployed cluster had no availability zone locality configured","ko":"AWS 도쿄 리전 한 AZ 의 냉각 장애로 쿠버네티스 위에서 돌던 CockroachDB 노드 60대 중 6대가 22분 만에 죽었고, AZ 토폴로지 설정이 없어 일부 Range 가 정족수를 잃었다."},"domain":["kubernetes","aws"],"impact":{"scope":"Cookie Run: Kingdom game service put into emergency maintenance; 2 data ranges and 32 system ranges of the CockroachDB cluster lost quorum, the last data range affecting an estimated 500 to 2,000 users","users":"unknown","region":"AWS Tokyo region"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"A replication factor sized for random instance retirements does not protect against a whole availability zone failing; for a game of this scale a multi-AZ placement is required, not optional.\n\nKeeping one uniform Helm chart made zone topology hard to set, which is part of why it was left out. A repetitive chart with a values file per zone sets it reliably.","ko":"쿠키런: 킹덤 정도 규모의 게임에서 멀티 AZ 전략은 선택이 아니라 필수다. 분산 DB 라도 레플리카가 어느 AZ 에 놓이는지 정하지 않으면, AZ 하나가 죽을 때 동시에 잃는 노드 수가 안전 마진을 넘을 수 있다. 차트를 깔끔하게 만드는 것보다 반복적이더라도 확실하게 토폴로지를 설정하는 쪽이 낫다. 클라우드 업체를 믿기보다 스스로 할 수 있는 최대한의 대비를 해 두어야 한다."},"related":[],"sources":[{"url":"https://tech.devsisters.com/posts/crk-aws-az-failure-postmortem/","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Devsisters ran the CockroachDB cluster for Cookie Run: Kingdom on Kubernetes from one Helm chart. CockroachDB's Locality feature spreads replicas so that losing a whole zone or region does not lose data, but an operator has to set the topology, and the availability zone of a pod is only known after it is scheduled and is not available through the Kubernetes API. Because working around that would add a lot of complexity, and because Devsisters had not seen an AZ failure on AWS since 2013, the team prepared for the more common case of several instances being retired at once: the replication factor was set to 7 so that three nodes could fail at the same time without data loss.\n\nOn 19 February 2021 a cooling failure in an AWS Tokyo data centre took down 6 of the 60 nodes. The chance that four or more of a range's seven replicas sat on those six nodes was about 0.09% per range; across 25,000 ranges about 23 were expected to be hit, and 2 data ranges and 32 system ranges were lost. Cockroach Labs support said the system ranges could be recovered, so the team concentrated on the two data ranges holding user data.\n\nSince a backup from the surviving 54 nodes failed, the team copied data outside the broken ranges into new tables with statements of the form INSERT INTO new_table SELECT * FROM old_table WHERE {unaffected_ranges}. Restoring from periodic backups was too slow, with no end in sight after 8 hours. In the CockroachDB source the team found the undocumented command debug unsafe-remove-dead-replicas, which tries to recover ranges that lost quorum; run on a screen share with Cockroach Labs engineers, it saved one of the two ranges. The last range, which could affect an estimated 500 to 2,000 users, was restored with the bit-level storage recovery method used in the team's previous incident, and in the end no user data was lost.","ko":"데브시스터즈는 쿠키런: 킹덤의 데이터베이스로 CockroachDB 를 쿠버네티스 위에 Helm 차트 하나로 배포해 운영했다. CockroachDB 에는 AZ 나 리전이 통째로 죽어도 데이터를 잃지 않도록 레플리카를 나눠 두는 Locality 기능이 있다. 다만 운영자가 토폴로지를 직접 설정해야 한다. 파드가 어느 노드와 AZ 에 배치될지는 차트를 작성할 때 알 수 없고 쿠버네티스 API 로도 얻을 수 없어서, 이를 설정하려면 구현이 지나치게 복잡해졌다. 2013년부터 AWS 를 쓰는 동안 AZ 장애가 한 번도 없었기 때문에 이 작업을 미뤘다. 대신 더 흔한 Instance Retirement 에 대비해 Replication Factor 를 7로 잡아, 노드 3대까지 동시에 내려가도 데이터를 잃지 않게 했다.\n\n2021년 2월 19일 AWS 도쿄 리전 데이터센터의 냉각 시스템이 고장 났다. 22분 동안 노드 60대 중 6대가 죽었고, 하필 게임 서버가 아니라 DB 서버였다. 레플리카 7개 중 과반인 4개 이상이 죽은 6대에 몰려 있던 Range 가 정족수를 잃었다. 전체 25,000개 Range 가운데 Data Range 2개와 System Range 32개가 사라졌다.\n\nCockroach Labs 서포트 엔지니어에게 확인해 보니 System Range 는 사라져도 복구할 수 있었다. 그래서 유저 데이터가 들어 있는 Data Range 2개를 살리는 데 집중했다. 살아남은 54대에서 테이블 백업 기능으로 데이터를 꺼내려 했지만 깨진 Range 때문인지 잘 되지 않았다. 그래서 깨진 Range 를 뺀 데이터를 새 테이블로 옮겼다. 백업에서 데이터를 가져오는 방법도 함께 진행했지만 8시간이 지나도 끝날 기미가 없었다. 그러던 중 CockroachDB 소스 코드에서 debug unsafe-remove-dead-replicas 명령을 찾았다. Cockroach Labs 엔지니어와 줌으로 화면을 공유하며 이 명령을 실행해 Range 2개 중 하나를 살렸다. 남은 Range 하나는 1차 장애 때처럼 스토리지 레이어에서 직접 복원했다. 이 Range 에 걸린 유저는 적으면 500명, 많으면 2천 명으로 추정됐고, 결과적으로 유저 데이터를 잃지 않고 모두 복구했다."},"trigger":{"en":"A cooling system failure in an AWS Tokyo region data centre overheated the servers in one availability zone, which held CockroachDB nodes.","ko":"AWS 도쿄 리전 데이터센터의 냉각 시스템이 고장 나 그 범위에 있던 DB 서버들이 과열로 멈췄다."},"severity":"unknown","symptoms":["node-not-ready","data-loss","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"A Slack alert reported that a database node had gone down.","ko":"회식 중에 DB 노드 한 대가 내려갔다는 에러 알람이 슬랙에 올라왔고, 이어서 다른 노드들의 장애 알람이 Ops 채널에 연달아 들어왔다."},"components":["database","helm"],"root_cause":"cloud-provider","remediation":[{"text":{"en":"copied the data outside the broken ranges into new tables with INSERT INTO ... SELECT queries that skipped the affected ranges","ko":"INSERT INTO new_table SELECT * FROM old_table WHERE {unaffected_ranges} 같은 쿼리로 깨진 Range 를 뺀 데이터를 새 테이블로 옮겼다."}},{"text":{"en":"ran debug unsafe-remove-dead-replicas together with Cockroach Labs engineers, recovering one of the two lost data ranges","ko":"Cockroach Labs 엔지니어와 화면을 공유하며 debug unsafe-remove-dead-replicas 명령을 실행해 사라진 Data Range 2개 중 하나를 살렸다."}},{"text":{"en":"restored the last data range with the storage-layer recovery method from the previous incident","ko":"남은 Data Range 하나는 1차 장애 때와 같은 방법으로 스토리지 레이어에서 직접 복원해 유저 데이터를 잃지 않고 복구했다."}}],"action_items":[{"text":{"en":"Configure CockroachDB Locality so data is spread evenly across availability zones","ko":"Locality 를 설정해 데이터를 여러 AZ 에 고르게 나눠 저장했다."}},{"text":{"en":"Deploy with one chart and a separate values file per availability zone to set Locality","ko":"차트 한 벌에 배포할 AZ 마다 다른 values 파일을 쓰는 방식으로 Locality 를 설정했다."}},{"text":{"en":"Replace nodes by adding Locality-configured nodes, confirming replicas in every zone, then retiring the old nodes","ko":"Locality 를 설정한 노드를 기존 클러스터에 넣고 모든 AZ 에 레플리카가 복제된 것을 확인한 뒤, Locality 가 없는 기존 노드를 클러스터에서 뺐다."}}],"contributing":["assumption-unverified","blast-radius-too-wide"],"recurrence_of":null,"symptoms_text":{"en":"During a team dinner, a Slack alert reported that one node of the CockroachDB cluster behind Cookie Run: Kingdom had gone down. The DevOps engineers were not worried at first, because CockroachDB is built to survive the loss of some nodes.\n\nThen more nodes went down: two more three minutes later, two after six minutes, two after twenty minutes, until 6 of the 60 nodes were out within 22 minutes, all with a host status failure. (One sentence of the write-up gives the window as 12 minutes.) Community sites and other known AWS customers showed the problem was not limited to Devsisters, and the cause turned out to be a failed cooling system in an AWS Tokyo region data centre that overheated the servers.\n\nWith six nodes down, more than the three the cluster had been sized to survive, 2 data ranges and 32 system ranges out of about 25,000 ranges lost quorum. A table backup taken from the 54 surviving nodes did not work, which the team suspected was because of the broken ranges.","ko":"2021년 2월 19일, DB 노드 한 대가 내려갔다는 에러 알람이 슬랙에 올라왔다. CockroachDB 는 노드 일부가 죽어도 견디도록 만든 분산 데이터베이스라서 데브옵스 엔지니어들은 처음에는 크게 걱정하지 않았다.\n\n그런데 3분 뒤에 2대, 6분 뒤에 또 2대, 20분 뒤에 다시 2대가 내려갔다는 알람이 Ops 채널에 이어졌다. 22분 만에 전체 60대 가운데 6대가 작동하지 않게 됐다.\n\n애플리케이션의 DB 호출 로직이나 DB 설계에 문제가 없는데 노드가 이렇게 한꺼번에 죽는다면 원인은 다른 곳에 있다고 판단했다. 커뮤니티 사이트와 여러 경로를 살펴보니 다른 AWS 고객사들도 문제를 겪고 있었다. 확인해 보니 AWS 도쿄 리전 데이터센터의 냉각 시스템이 고장 나 서버가 과열돼 있었다.\n\n죽은 6대는 Host Status Failure 상태였다. 안전 마진으로 잡아 둔 3대를 넘었기 때문에 전체 25,000개 Range 가운데 Data Range 2개와 System Range 32개가 사라졌다. 게임은 긴급 점검에 들어갔다."},"updated_at":"2026-11-10T00:00:37.271748Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0172","org_slug":"devsisters"}