그래프
사례4건
4건 중 1~4번째
- 증상
2월 9일 데이터 가용성 문제일 수 있다는 첫 티켓이 들어왔다. 2월 10일 상태 페이지는 쿼리 문제를 조사 중이며 최근 데이터 일부가 쿼리 응답에 나오지 않는다고 알렸다.
- 촉발
2월 9일 아주 큰 파티션의 압축을 개선하려는 코드 변경을 prod101-us-east-1 에 배포했다.
- 원인
- 구성 요소
- 기여 요인
- 감지
2월 9일 데이터 가용성 문제일 수 있다는 첫 티켓이 들어와 조사를 시작했다. 2월 10일 티켓이 더 들어온 뒤 근본 원인을 찾았다.
- 조치
- 2월 10일 압축 변경을 롤백해 더 번지지 않게 했다.
- 최근 데이터를 클러스터의 쓰기를 모두 기록한 Kafka 로그에서 먼저 되살렸다.
- 2월 17일 과거 데이터를 더 효율적으로 복원하도록 복원 절차를 고쳤다.
- 과거 데이터를 TSM 스냅숏에서 복원했다.
- 실시간 쓰기와 쿼리를 방해하지 않도록 클러스터를 켠 채로 조금씩 복원했다.
- 후속 조치
- 압축기처럼 중요한 스토리지 구성 요소를 바꾸는 변경의 테스트 범위를 넓히고, 운영 규모의 작업량을 더 잘 반영하는 테스트 시나리오를 더한다.
- 압축 과정의 안전장치를 강화해 실패를 찾아내고 안전하게 처리하게 한다.
- 대규모 복원 중에 TTBR 이 높아지거나 쿼리가 느려질 가능성을 줄이도록 복원 작업 흐름과 운영 절차를 검토한다.
- 클러스터 전체 복원이 빨라지도록 백업과 복원 전략을 다시 본다.
- 증상
2024년 9월 18일 데이터센터 전환을 앞두고 cookbook 으로 원형 복제를 설정하던 중, 21:28 UTC 쯤 엔지니어가 x1 섹션의 primary master 가 복제를 하지 않는 것을 orchestrator 에서 먼저 알아챘다. es6 섹션도 같은 상태였다.
- 촉발
다음 주 데이터센터 전환을 앞두고 cookbook 이 핵심 데이터베이스를 eqiad 에서 codfw 로 가는 한 방향 복제에서 원형 복제로 바꿨다. ROW 기반 복제를 쓰는 x1 섹션에 codfw 에서 eqiad 로 가는 복제를 설정하고 pt-heartbeat 를 시작하자, eqiad 서버에 없는 heartbeat 행을 갱신하려다 복제가 멈췄다.
- 원인
- 구성 요소
- 기여 요인
- 감지
원형 복제의 일반 모니터링이 아직 켜지지 않아, 엔지니어가 orchestrator 에서 먼저 알아챘다. 복제 자동화가 처음 도는 날이라 평소보다 많은 사람이 데이터베이스 상태를 지켜보고 있었다. 사용자에게 영향을 준 eqiad 에서 codfw 로 가는 방향의 고장은 Icinga 알림이 잡았다. 온콜 호출을 받은 사람은 없었다.
- 조치
- SET GLOBAL Replication_wild_ignore_table='heartbeat.%'; 로 heartbeat 테이블 복제를 건너뛰게 해 복제가 다시 흐르게 했다.
- es 호스트에는 원래 고장이 난 방향에 맞춰, binlog 기록을 끈 채 eqiad 서버마다 빠진 codfw heartbeat 행을 직접 넣었다. 명령은 set sql_log_bin=0; INSERT INTO heartbeat.heartbeat (server_id, shard, datacenter) VALUES (<codfw-server-id>, 'es6' [or 'es7'], 'codfw'); 였다. es6 에서 먼저 시험했고, es7 에는 원형 복제를 설정하기 전에 미리 넣었다.
- x1 codfw 호스트마다 binlog 기록을 끈 채 빠진 heartbeat 행을 넣어, 임시로 걸어 둔 heartbeat 무시 설정을 대신했다. 명령은 set sql_log_bin=0; INSERT INTO heartbeat.heartbeat (server_id, shard, datacenter) VALUES (<eqiad-server-id>, 'x1', 'eqiad'); 였다.
- 후속 조치
- 같은 문제를 다시 겪지 않게 한다. 빠진 행을 찾으면 직접 넣는 방식을 유력하게 본다.
- 유지 보수 뒤 복제가 의도대로 양방향으로 동작하는지 확인하는 상태 점검을 cookbook 에 넣는다.
- 유지 보수를 다른 순서로, 또는 섹션마다 따로 돌릴 수 있는 옵션을 더한다.
- Phabricator 를 마지막에 한 번만 갱신하지 않고 섹션마다 갱신한다.
- 모니터링의 빈틈을 메운다.
- x1 과 ROW 를 쓰는 읽기·쓰기 es 호스트에서 pt-heartbeat-wikimedia 가 자기 세션을 STATEMENT 로 쓰도록 고쳐 배포했다.
- 교훈
원형 복제는 평소 운영 방식이 아니어서 heartbeat 테이블을 지우는 첫 조치가 문제를 eqiad 에서 codfw 로 옮기기만 했다. TRUNCATE 는 DDL 이라 늘 STATEMENT 모드로 전달되므로 이번에는 DELETE 보다 나았겠지만, 더 위험해서 고려하지 않았다. ROW 기반 복제, pt-heartbeat, 서버마다 다른 heartbeat 테이블 행이 겹친 문제는 이전 유지 보수 기록에 없었다. 자동화의 점검은 설정 직후 한 번이 아니라 모든 작업이 끝난 뒤에도 복제 채널을 확인했어야 했다. Wikimedia 는 어느 호스트에서도 sql_skip_slave_counter 를 쓰지 않아 더 큰 문제나 데이터 손실을 피한 것을 잘된 점으로 꼽았다.
- 증상
2023년 9월 7일 TANF-app 의 sprint 80 운영 배포가 실패했고, 9월 20일 sprint 82 배포는 디스크 공간 오류로 실패했다. 그 뒤 11월 9일까지 운영에 배포하지 못했다.
- 촉발
2023년 9월 7일 sprint 80 배포에서 이미 배포된 마이그레이션 search_indexes 0016 을 지웠고, 9월 20일 sprint 82 배포가 이 마이그레이션을 다시 만들어 큰 마이그레이션 0017 과 함께 운영 데이터베이스에서 돌렸다.
- 원인
- 구성 요소
- 기여 요인
- 감지
운영 배포가 실패하면서 드러났다. 9월 7일 sprint 80 배포와 9월 20일 sprint 82 배포가 차례로 실패했고, 9월 20일 배포에서 디스크 공간 오류가 났다.
- 조치
- 11월 8일 운영 데이터베이스 인스턴스 크기를 키우고 마이그레이션을 다시 돌렸다. 이번에는 빈 문자열을 정수로 바꾸는 오류로 마이그레이션이 실패했다.
- 운영 데이터베이스의 백업을 만들었다.
- `search_indexes` 테이블을 모두 비웠다. 팀은 여기서 운영 데이터를 잃는 것을 걱정하지 않았다.
- sprint 82 릴리스를 다시 배포해 운영을 sprint 82 로 되살렸고, 배포가 다시 가능해졌다.
- 빈 문자열 버그를 고쳤다.
- 후속 조치
- 데이터베이스 문제 해결 절차를 기술 문서에 더한다.
- 위키 페이지와 운영 플레이북에 흔한 문제와 대응을 정리한다.
- Failed-Deployment-Troubleshooting 문서에 데이터베이스 문제 절을 만들고 cf revisions 명령을 적는다.
- 운영에 직접 한 변경을 모은 GitHub 레포를 만들어, 비슷한 문제가 다시 날 때 길잡이로 쓴다.
- 운영 디버깅 세션의 터미널 출력을 저장한다.
- 코드 리뷰에서 지킬 Django 마이그레이션 모범 사례를 만든다.
- develop 에 머지된 마이그레이션은 고치지 않고, 그 시점부터 모델을 바꾸는 새 마이그레이션을 쓴다.
- 자동 생성된 마이그레이션 이름을 알아보기 쉬운 이름으로 바꾼다. 이름만 보고 내용을 알 수 없으면 조사하기 어렵다.
- 자동 생성 마이그레이션은 모델 변경 하나에 하나만 두고, PR 하나에서 모델을 여러 번 바꾸면 마이그레이션을 다시 생성한다.
- 시험에 쓸 데이터베이스 시딩과 백업을 어떻게 할지 논의 자리를 만든다.
- 교훈
계획에 없던 일이라 많은 개발자가 손을 보태야 했고, 팀이 작아 분야마다 모든 사람이 필요했다. 팀은 문제와 대응을 일찍 문서로 남겨 더 적은 사람으로 대응할 수 있게 하자고 했다. 작업 중인 코드를 raft, qasp, sandbox 환경에 배포한 뒤 마이그레이션을 바꾸면 그 환경에서 충돌이 난다. 대용량 데이터와 공백 채움이 없는 레코드 같은 예외 상황은 시험하지 않았고, 개인정보를 지운 운영 데이터베이스 사본이 있으면 실제 데이터로 더 많이 시험할 수 있다.
- 증상
2021-11-08 18:50 UTC 배포 직후 오류와 알림이 바로 들어왔고, 모든 executor 에서 고객 작업이 "not running" 상태로 막혔다.
- 촉발
18:50 UTC 에 분배 서비스의 PostgreSQL 데이터베이스에서 필드 타입을 바꾸는 배포를 내보냈다. 이 스키마 변경은 하위 호환되지 않았다.
- 원인
- 구성 요소
- 기여 요인
- 감지
18:50 UTC 배포 직후 분배 실패와 관련된 오류와 알림이 바로 보였다.
- 조치
- 오류와 알림을 보고 곧바로 변경을 롤백했다. 롤백 뒤에는 두 배포 사이에 쓰인 데이터를 읽지 못해 분배가 계속 실패했다.
- 분배 시스템이 그 필드를 무시하게 하는 수정을 빠르게 만들고 빌드해 직접 배포했다.
- 몰려들 작업에 대비해 주 컴퓨트 클러스터를 직접 늘렸다. 이 수동 확장은 CircleCI 장애 대응 절차의 표준 단계다.
- 작업 큐가 줄어 정상 처리가 이어지는 것을 잠시 지켜본 뒤 장애를 해결로 처리했다.
- 후속 조치
- 같은 장애가 다시 나지 않도록 테스트 전략의 빈틈을 찾아 고친다.
- 비슷한 배포가 시스템의 모든 작업에 영향을 주지 않도록, 분배 서비스가 예상하지 못한 데이터를 만났을 때의 동작을 바꾼다.
- 예상하지 못한 장애가 났을 때 더 빨리 복구할 수 있게 한다.
이 4건에서 가장 많이 나오는 구성 요소: 데이터베이스, 데이터베이스 백업과 스냅숏. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.