Incident Library

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' 노드가 앞서 데이터가 뒤처졌다고 판단한 노드라는 것을 알아챘다.

조치

후속 조치

교훈

GitHub 은 이번 장애에 세 가지가 겹쳤다고 밝혔다. 일어나지 말았어야 할 'active' 역할 페일오버가 여러 번 일어났고, 클러스터 상태가 갈라져 클러스터 관리 소프트웨어가 잘못된 동작을 했으며, 그 페일오버들이 필요 이상으로 성능과 가용성을 해쳤다. 두 장애는 모두 자동 페일오버에서 시작했는데, 그 페일오버를 해야 하느냐고 운영팀 누구에게 물었어도 하지 말아야 한다고 답했을 것이다. GitHub 은 자동 페일오버가 서비스 가용성을 지키는 좋은 방법인 경우가 많지만, 주 운영 데이터베이스는 그런 경우가 아니라고 결론 내렸다.

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: 데이터베이스, 데이터베이스 장애 조치 관리자, 데이터베이스 복제. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.