IL-0565 Devsisters · 2022-12-22 (게시일)databaseaws
쿠키런: 킹덤의 CockroachDB 클러스터가 노드 24대 중 16대의 파일 시스템을 잃고 멈춰 게임이 36시간 넘게 점검에 들어갔다
쿠키런: 킹덤을 출시한 뒤 사용자 수가 계속 늘어 CockroachDB의 스토리지 사용량이 빠르게 늘었다. 첫 주말을 넘긴 월요일에 계산해 보니 이대로면 약 36시간 뒤 디스크가 가득 차 쓰기가 멈출 상황이었다. 팀은 CockroachDB 19.1.11 공식 가이드가 권하는 대로 공간을 미리 차지해 두는 더미 파일인 Ballast 파일을 만드는 스크립트를 실행했다. 클러스터는 로컬 스토리지 디바이스가 두 개인 m5d.8xlarge 24대였고, EC2는 둘 중 하나를 임의로 Primary Mount로 정한다. 설정 문제로 지정 경로가 Primary Mount에 해당한 노드 16대에서 스크립트가 파티션을 덮어썼고, 과반 노드를 잃은 클러스터 전체가 멈췄다. 데이터는 7개 복제본으로 저장하도록 설정돼 있었다.
팀은 쿠키런: 킹덤 개발팀과 논의해, 다른 방법이 있는 한 백업 시점 이후의 데이터를 잃는 백업 복원은 쓰지 않기로 했다. 목표는 장애 직전까지의 모든 데이터를 되살리는 것이었고, 팀은 세 방법을 동시에 진행했다. 운영체제 수준에서 노드를 복원하는 Plan A는 실패했다. 최신 백업에 분석용 로그를 다시 적용해 사용자 상태를 재현하는 Plan C는 핵심 API부터 손댔다. 남은 노드 7대의 SST 파일에서 데이터를 직접 꺼내는 Plan B가 성공했다. 팀은 데이터를 완전히 잃을 확률을 약 3.3%로 계산했다. 또 CockroachDB 소스코드에서 SST 파일 내부를 보는 명령을 찾아 파일 구조를 해독했다. Spark로 약 7.5TB의 SST 파일을 CSV로 추출했고, 중복을 없애 하나의 CSV로 병합했다. 새 클러스터에는 유저마다 가장 최근 스냅샷 1개와 저널 300개만 넣었다. 저널과 스냅샷 테이블은 오후 4시 20분과 4시 57분에 복원됐다. 유저 테이블은 오염이 심해 서버 로직으로 다시 만들었다. 복원한 데이터는 백업으로 만든 데이터베이스와 대조했고, 샘플 유저 검증, 전체 유저 스냅샷 리플레이 검증, 사내망 테스트를 거쳤다. 서비스는 이튿날 23시 37분, 30시간 50분 만에 다시 열었다.
오래 기다린 유저들의 요청이 몰리며 DevPlay 플랫폼 데이터베이스가 연쇄적으로 장애를 일으켰고, AWS RDS Failover로 정상화했다. 새로 구성한 킹덤 클러스터는 i3 계열 인스턴스였다. 지표로 보면 이 클러스터에서 Disk Write Ops 부하가 CPU 부하로 이어졌고, 새벽 2시 42분에는 모든 쿼리가 실패해 2차 긴급 점검에 들어갔다. 팀은 공식 백업과 복원 기능으로 데이터를 옮겨 기존 m5d 인스턴스를 더 많은 대수로 쓰는 세 번째 클러스터를 만들었고, 오전 8시 30분에 총 36시간 29분 만에 점검을 끝냈다. 이후 CockroachDB도 안전장치를 개선했고, 21.2 이상 버전은 Ballast 파일을 자동으로 만든다.
- 심각도
- –
- 영향 범위
- Cookie Run: Kingdom was in emergency maintenance for 36 hours 29 minutes in total; the CockroachDB cluster holding user data was lost and rebuilt from data extracted from the surviving nodes
- 영향 사용자
- Cookie Run: Kingdom players; the restored data belonged to users numbering in the millions
- 지역
- AWS Tokyo region
- 감지까지
- –
- 복구까지
- 36h 29m
증상
Ballast 파일을 만드는 스크립트를 실행한 직후 에러율, 데이터베이스 연결, Health Check 알람이 수백 건 한꺼번에 울렸다. 알람 메시지를 읽기도 전에 다음 알람이 쏟아졌고, 정상적인 게임 이용이 어렵다고 본 팀은 16시 52분에 긴급 점검을 시작했다.
커널 작업 화면은 빨간 글씨로 가득했다. 데브옵스 팀 전체가 약 30분 동안 셸 히스토리, CRDB 로그, 커널 로그, CRDB 소스코드를 분석한 뒤에야 무슨 일인지 알았다. 스크립트가 노드 24대 중 16대의 파티션을 덮어써 파일 시스템이 불안정해졌고, 쓰기에 실패한 노드들은 클러스터에서 자동으로 격리됐다. CockroachDB는 Raft 합의 알고리즘으로 동작해 과반 노드가 있어야 하므로, 노드 단위의 장애가 클러스터 전체의 장애로 번졌다.
팀은 먼저 AWS EC2 기술지원에 가상머신 스냅샷이 남아 있는지 물었지만 그런 것은 없었다. 영향받은 머신마다 EBS를 붙이고 dd 명령으로 파티션을 복제한 뒤 fsck를 돌렸지만 오류 항목이 너무 많았다. fsck 결과로 클러스터를 다시 띄워 봐도 특정 SST 파일을 찾을 수 없다는 식의 파일 시스템 오류가 남았다.
복구를 시도하던 23시 02분쯤 영향을 받지 않은 정상 노드 하나가 Host Status Failure로 내려갔다. 이 노드는 EBS 복제도 해 두지 않은 상태여서 남은 노드는 8대에서 7대로 줄었다.
남은 7대만으로 클러스터를 다시 구성하려 했지만, 클러스터가 시작할 때 Raft Quorum을 검증하는 로직이 영향받은 노드들의 기록 때문에 Healthy State로 넘어가지 못했다. 문서화되지 않은 내부 툴까지 써 봤지만 다음날 오전 5시까지 이 단계를 우회하는 방법을 찾지 못했다.
30시간 50분 만에 서비스를 다시 열자, 서버가 언제 열리는지 계속 확인하던 유저들의 요청이 로그인과 인증을 맡는 DevPlay 플랫폼에 몰려 플랫폼 데이터베이스 장애로 이어졌다. 이어 요청이 많은 자정 무렵부터 킹덤 데이터베이스에서 DB Timeout 에러가 관측됐고 일부 노드의 CPU가 100%에 이르렀다. 새벽 2시 42분에는 데이터베이스의 모든 쿼리와 요청이 실패해 2차 긴급 점검에 들어갔다.
원인
- 촉발
- 디스크가 가득 찰 때를 대비해 Ballast 파일을 만드는 스크립트를 실행했는데, 파일 경로가 잘못 설정돼 Block Device를 가리켰다. 이 명령에는 대상 경로에 파일이 이미 있는지 확인하는 안전장치가 없어 파티션을 그대로 덮어썼다.
감지
작업 직후 미리 설정해 둔 메트릭으로 알았다. 에러율, 데이터베이스 연결, Health Check 알람이 수백 건 동시에 울렸다. 2차 장애는 새벽 2시 42분 데이터베이스의 모든 쿼리와 요청이 실패하는 것을 관측하고 알았다.
조치
- 정상적인 게임 이용이 어렵다고 보고 운영팀에 요청해 16시 52분부터 긴급 점검을 시작했다.
- SST 파일의 구조를 해독하고, 장애 다음날 오전 4시쯤 Spark로 SST 파일 내용을 CSV 텍스트 파일로 추출하는 샘플 코드를 작성했다. 예상 처리 시간이 약 24시간으로 너무 길어 CockroachDB CLI 소스코드를 필요한 작업에 맞게 고쳐 10배 이상 빠르게 했다.
- 오전 10시 30분쯤부터 마지막까지 살아남은 노드 7대의 SST 파일 약 7.5TB를 CSV로 변환하며 나오는 에러를 고치는 작업을 반복했다.
- IMPORT INTO는 실행하는 동안 테이블을 오프라인으로 만들고, 키 충돌이 하나라도 나면 전부 Revert한다. 이 제약을 확인한 팀은 이튿날 오후 12시부터 중복을 없애 데이터베이스에 바로 넣을 수 있는 하나의 큰 CSV 파일로 병합하는 Spark 코드를 작성했다.
- 스토리지가 넉넉한 새 데이터베이스 클러스터에 데이터를 넣기 시작했고, 시간이 너무 오래 걸릴 것으로 보여 유저마다 가장 최근 스냅샷 1개와 가장 최근 저널 300개만 복원하기로 범위를 좁혔다.
- 서비스 재개에 쓸 깨끗한 클러스터를 따로 준비해 저널, 스냅샷, 유저 테이블 외의 테이블은 백업에서 복원했다. 저널과 스냅샷 테이블은 전처리한 CSV에서 Import했고, 유저 테이블은 서버 로직으로 다시 만들어 오후 8시에 모든 작업을 마쳤다.
- 일부 유저를 샘플링해 분석 로그에 기록된 상태와 최종값이 같은지 검증했고, 모든 유저의 스냅샷 리플레이가 정상적으로 동작하는지 검증했다.
- 이튿날 23시 37분, 30시간 50분 만에 서비스를 다시 열었다.
- 부하로 장애가 난 DevPlay 플랫폼의 AWS RDS 데이터베이스를 일시적인 순단을 감수하고 Failover해 정상화했다.
- 공식 백업과 복원 기능으로 데이터를 옮겨 기존 m5d 인스턴스를 더 많은 대수로 쓰는 세 번째 데이터베이스 클러스터를 구성했다. CockroachDB 서포트 엔지니어들과 논의해 TPC-C 벤치마크에 쓰는 것과 비슷한 설정값을 적용해 데이터를 옮기는 시간을 줄였다.
- 오전 8시 30분, 총 점검 시간 36시간 29분 만에 임시점검을 끝냈다.
후속 조치
- 프로덕션 인프라 작업은 아무리 사소해도 반드시 작업자 2명 이상이 참여하도록 규칙을 바꿨다.
- 작업에 쓸 스크립트와 작업 시나리오를 엄격하게 작성해 미리 팀 안에 공유하고 리뷰를 받도록 했다.
교훈
숙련된 작업자도 사람인 이상 실수할 수 있으므로, 실수를 막으려면 사람에게 기대지 말고 시스템을 갖춰야 한다. 복구할 수 있었던 바탕은 세 가지였다. 작업자 개인을 탓하지 않고 대응 과정을 기록해 액션 아이템을 정하는 장애 대응 원칙이 있었고, 과거 장애 경험이 기록으로 쌓여 있었으며, 인프라 운영과 데이터 분석을 맡는 팀이 한 조직에 있었다.
출처
관련 사례
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0242primary 와 secondary PostgreSQL 데이터베이스를 모두 잃어 matrix.org homeserver 가 약 24시간 동안 멈췄다Matrix.org Foundation · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0531West US 의 Azure Database for MySQL Single Server 와 MariaDB 일부 서버로 가는 새 연결이 모두 거부됐다Microsoft Azure · 2024같은 원인 · 공통 데이터베이스
IL-0207로컬 개발 환경에서 돌린 작업이 운영 컨텍스트로 넘어가 Kubernetes 클러스터의 Consul 릴리스를 통째로 지웠고, Patroni 가 무너져 GitLab.com 전체가 5XX 오류를 냈다.GitLab · 2022같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0506UC Berkeley DataHub 의 JupyterHub sqlite 데이터베이스가 빈 데이터베이스로 덮어써져 로그인이 실패하고 홈 디렉터리가 비어 보이고 다른 사용자로 로그인되는 문제가 났다UC Berkeley · 2017같은 원인 · 공통 데이터베이스
IL-0218복제를 다시 맞추다 PostgreSQL 프라이머리의 데이터 디렉터리가 지워지고 백업이 작동하지 않아 GitLab.com 이 약 18시간 멈추고 데이터를 잃었다GitLab · 2017같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0319mysql2 를 다시 적재하려고 데이터베이스를 지우는 동안 mysql1 의 데이터베이스도 함께 지워졌다Oregon State University Open Source Lab · 2015같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0565
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0565/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.