IL-0303 GitHub · 2012-09-10database
GitHub 의 새 3노드 MySQL 클러스터에서 자동 페일오버가 잘못 일어나고 클러스터 상태가 갈라져 GitHub.com 이 두 번 멈췄다
2012년 8월 중순 GitHub 운영팀은 DRBD 위에 돌던 오래된 MySQL 서버 한 쌍을 3노드 클러스터로 바꿨다. 클러스터는 애플리케이션에 가상 IP 두 개를 제공했다. 하나는 읽기 쓰기용, 하나는 읽기 전용이었고, 두 가상 IP 는 Pacemaker 와 Heartbeat 가 관리했다. 클러스터 안에서 'active' 와 'standby' 역할을 옮기는 MySQL 복제 조율은 Pacemaker 의 리소스 에이전트인 Percona Replication Manager 가 맡았다. 새 구성에서는 모든 노드에서 MySQL 이 늘 돌고 있어, 페일오버는 트랜잭션을 비우고 read_only 변수를 바꾼 뒤 가상 IP 를 옮기는 일이었다.
9월 10일 월요일에 돌린 두 단계 무중단 스키마 마이그레이션은, 운영팀이 같은 종류의 마이그레이션에서 전에 본 것보다 높은 부하를 데이터베이스에 걸었다. Percona Replication Manager 의 헬스 체크가 마스터에서 실패했다. Percona Replication Manager 는 'active' 역할과 마스터를 다른 노드로 옮긴 뒤, 실패했다고 본 노드의 MySQL 을 멈췄다. 새 노드는 InnoDB 버퍼 풀이 비어 있어 느렸고, 그 부하로 헬스 체크가 다시 실패해 'active' 역할이 원래 노드로 되돌아갔다. 담당 엔지니어는 Pacemaker 의 maintenance-mode 를 켜서 모든 헬스 체크와 자동 페일오버를 멈췄고, 버퍼 풀이 차면서 사이트가 천천히 돌아왔다.
9월 11일 아침, 'standby' 노드가 잘못된 쿼리 결과를 낸다는 신고가 들어왔다. maintenance-mode 때문에 이 노드가 복제를 다시 시작하지 못한 것이었다. maintenance-mode 를 끄려 하자 Pacemaker 에서 segfault 가 나 클러스터 상태가 갈라졌고, 혼자 남은 'c' 노드가 8:19 AM 에 마스터로 뽑혔다. 'c' 는 데이터가 뒤처진 노드였다. 운영팀은 8:26 AM 에 'c' 의 전원을 꺼서 갈라진 상태를 끝냈고, 이 때문에 운영 데이터베이스 접근과 github.com 접근이 모두 끊겼다. 그 7분 동안 MySQL 과 Redis 사이의 id 연결이 어긋나 일부 이벤트가 다른 사용자의 대시보드에 보였고, 비공개 저장소 16개가 협업자가 아닌 사람에게 열려 있었다. 운영팀은 'a' 와 'b' 노드에서 클러스터 상태를 되살리려 했지만 상태 변경이 반영되지 않았다. 결국 Pacemaker 와 Heartbeat 를 두 노드에서 모두 멈춘 뒤 'a' 에서만 다시 띄웠다. 'a' 에서 MySQL 이 'active' 역할을 맡았고 버퍼 풀이 차면서 사이트가 천천히 돌아왔다.
두 번의 장애로 GitHub.com 은 1시간 46분 동안 멈췄고, 그와 별도로 1시간 동안 성능이 크게 떨어졌다. GitHub 은 두 장애의 근본 원인을 주 운영 데이터베이스의 자동 페일오버로 꼽았다.
- 심각도
- –
- 영향 범위
- GitHub.com had one hour and 46 minutes of downtime and another hour of significantly degraded performance over two outages; some events appeared on wrong dashboards and 16 private repositories were reachable by outsiders for seven minutes
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
2012년 9월 10일 월요일, 스키마 마이그레이션이 데이터베이스에 높은 부하를 걸자 Percona Replication Manager 의 헬스 체크가 마스터에서 실패했다. Percona Replication Manager 는 'active' 역할을 다른 노드로 옮겼고, 새 노드가 느려지면서 GitHub.com 이 멈췄다.
새로 'active' 역할을 맡은 노드는 InnoDB 버퍼 풀이 비어 있는 상태였다. 캐시가 빈 채로 사이트의 쿼리 부하를 받자 시스템 부하가 올라 헬스 체크가 다시 실패했고, 'active' 역할은 원래 노드로 되돌아갔다. 헬스 체크를 끈 뒤에는 버퍼 풀이 천천히 정상 수준까지 찼고, 그동안 사이트 성능도 천천히 돌아왔다.
다음 날인 9월 11일 아침, 한 개발자가 'standby' 역할을 맡은 노드가 잘못된 쿼리 결과를 낸다고 운영팀에 알렸다. 전날 클러스터를 maintenance-mode 로 둔 탓에, 'standby' 노드가 복제 마스터를 바꾸고 복제를 시작하는 동작이 일어나지 못한 것이었다.
운영팀이 maintenance-mode 를 끄려 하자 Pacemaker 에서 segfault 가 났고 클러스터 상태가 둘로 갈라졌다. 'a' 와 'b' 두 노드는 'c' 노드의 메시지를 대부분 거부했고, 'c' 노드도 두 노드의 메시지를 대부분 거부했다. 과반이 동의해야 움직이도록 설정했는데도 마스터 선출이 조율 없이 두 곳에서 동시에 일어났다. 두 노드 쪽에서는 선출이 중간에 끊기고 MySQL 이 멈췄다. 혼자 남은 'c' 쪽에서는 8:19 AM 에 'c' 가 마스터로 뽑혔다. 'c' 는 운영팀이 앞서 데이터가 뒤처졌다고 판단한 노드였다.
갈라진 동안 MySQL 과 Redis 사이의 데이터가 어긋났다. GitHub 은 MySQL 이 자동으로 만든 id 로 Redis 에서 대시보드 이벤트 스트림과 저장소 경로를 찾는데, 이 시간에 만든 레코드는 MySQL 의 id 와 Redis 의 데이터가 서로 맞지 않게 되었다. 그 결과 일부 이벤트가 다른 사용자의 대시보드에 보였고, 이 시간에 만든 일부 저장소는 경로가 잘못 잡혔다.
원인
- 촉발
- 무중단 MySQL 스키마 마이그레이션이 이전의 같은 종류 마이그레이션보다 높은 부하를 데이터베이스에 걸었고, 그 부하로 Percona Replication Manager 의 헬스 체크가 마스터에서 실패했다.
감지
9월 11일 아침 한 개발자가 'standby' 역할을 맡은 노드에서 잘못된 쿼리 결과가 나온다고 운영팀에 알렸다. 그 뒤 운영팀은 혼자 마스터로 뽑힌 'c' 노드가 앞서 데이터가 뒤처졌다고 판단한 노드라는 것을 알아챘다.
조치
- 9월 10일 Pacemaker 의 maintenance-mode 를 켜서 모든 헬스 체크와 자동 페일오버 동작을 멈췄다.
- 9월 11일 8:26 AM 에 데이터가 뒤처진 'c' 노드의 전원을 꺼서 갈라진 클러스터 상태를 끝내고 데이터가 더 어긋나지 않게 했다.
- 클러스터 상태를 깨끗하게 되살리려고 'a' 와 'b' 두 노드에서 Pacemaker 와 Heartbeat 프로세스를 모두 멈춘 뒤 'a' 노드에서 다시 띄웠다.
- 'a' 노드에서 MySQL 을 띄워 'active' 역할을 맡게 했다.
- 다른 사용자의 대시보드에 새어 나간 이벤트를 모두 지우고, 그 시간에 경로가 잘못 잡힌 저장소를 모두 점검했다.
- 협업자가 아닌 사람에게 열려 있던 비공개 저장소 16개의 소유자에게 모두 직접 연락했다.
후속 조치
- Pacemaker 설정을 바꿔 'active' 데이터베이스 역할의 페일오버는 운영팀 사람이 시작할 때만 일어나게 한다.
- 비상시나 정기 유지보수 때 페일오버를 해도 성능이 떨어지지 않도록, 복제본 데이터베이스의 InnoDB 버퍼 풀을 미리 데우는 여러 방법을 조사하고 시험한다.
- 화요일에 segfault 를 일으킨 코드 경로를 중심으로 Pacemaker 와 Heartbeat 스택 전체를 점검한다.
- 만일에 대비해 segfault 가 난 서버에서 강도 높은 하드웨어 시험을 한다.
교훈
GitHub 은 이번 장애에 세 가지가 겹쳤다고 밝혔다. 일어나지 말았어야 할 'active' 역할 페일오버가 여러 번 일어났고, 클러스터 상태가 갈라져 클러스터 관리 소프트웨어가 잘못된 동작을 했으며, 그 페일오버들이 필요 이상으로 성능과 가용성을 해쳤다. 두 장애는 모두 자동 페일오버에서 시작했는데, 그 페일오버를 해야 하느냐고 운영팀 누구에게 물었어도 하지 말아야 한다고 답했을 것이다. GitHub 은 자동 페일오버가 서비스 가용성을 지키는 좋은 방법인 경우가 많지만, 주 운영 데이터베이스는 그런 경우가 아니라고 결론 내렸다.
출처
관련 사례
IL-0281PostHog US 리전 Logs 제품의 ClickHouse 클러스터가 S3 에 둔 로그 데이터를 거의 다 지워 3일보다 오래된 로그가 사라졌다PostHog · 2026같은 원인 · 공통 데이터베이스 복제, 데이터베이스
같은 원인 · 공통 데이터베이스 장애 조치 관리자, 데이터베이스
IL-0527OpenStreetMap.org 의 복제 diff 생성이 invalid memory alloc request size 오류로 16시간 멈춤OpenStreetMap Foundation · 2025같은 원인 · 공통 데이터베이스 복제, 데이터베이스
같은 원인 · 공통 데이터베이스 장애 조치 관리자, 데이터베이스
같은 원인 · 공통 데이터베이스 복제, 데이터베이스
IL-0451Aurora MySQL 에서 인덱스를 INVISIBLE 로 바꿀 때마다 Reader 3대의 버퍼풀이 무효화돼 CPU 와 ReadIOPS 가 튀었다Daangn · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0150DynamoDB DNS 자동화의 경쟁 조건이 us-east-1 리전 엔드포인트의 DNS 레코드를 비워 DynamoDB 와 EC2 인스턴스 시작이 실패했고, 그 여파로 EKS, ECS, Fargate 의 컨테이너 시작과 클러스터 스케일링이 14시간 넘게 막혔다Amazon Web Services · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0329Postgres 17 업그레이드 뒤 다시 켠 PGAudit 가 마이그레이션 중 락을 쥔 채 멈춰, 서비스 전반이 느려지고 데이터베이스가 2분 동안 내려갔다incident.io · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스 복제
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0024무거운 API 요청이 MongoDB 에 과부하를 걸고 취소되지 않은 요청이 쌓여 Hugging Face Hub 가 사흘 동안 세 번 멈췄다Hugging Face · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스 장애 조치 관리자
IL-0385큰 MySQL 테이블의 스키마 마이그레이션 마지막 단계에서 읽기 복제본 다수가 semaphore deadlock 에 걸려 GitHub 핵심 서비스가 2시간 50분 저하됐다GitHub · 2021같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0196Redis Operator 로 레플리카를 늘리면서 메모리 한도를 낮추자 데이터를 가진 유일한 Redis 파드가 재시작됐고, Sentinel 이 빈 레플리카를 마스터로 올려 데이터가 모두 지워졌다.Palark · 2020같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0388Redis 고가용 클러스터의 primary 노드가 내려간 뒤 Discord API 가 느려지고 길드 일부가 오프라인으로 보이며 접속이 실패했다Discord · 2017같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0303
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0303/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.