Incident Library

IL-0218 GitLab · 2017-01-31databaseazure

복제를 다시 맞추다 PostgreSQL 프라이머리의 데이터 디렉터리가 지워지고 백업이 작동하지 않아 GitLab.com 이 약 18시간 멈추고 데이터를 잃었다

GitLab.com 은 프라이머리 db1.cluster.gitlab.com 과 핫 스탠바이 세컨더리 db2.cluster.gitlab.com 으로 PostgreSQL 을 운영했고, 세컨더리는 페일오버에만 썼다. 2017년 1월 31일 한 엔지니어가 pgpool-II 를 시험하려고 17:20 UTC 무렵 운영 데이터베이스의 LVM 스냅샷을 떠 스테이징에 올렸다. 19:00 UTC 무렵 스팸으로 의심되는 부하가 올랐고, 어뷰징 신고 때문에 실수로 삭제 예약된 GitLab 직원 계정과 데이터를 지우는 백그라운드 작업도 부하를 보탰다. 23:00 UTC 무렵 세컨더리의 복제가 뒤처지다 멈췄는데, 필요한 WAL 세그먼트를 프라이머리가 이미 지웠고 WAL 아카이빙이 없어 세컨더리를 손으로 다시 맞춰야 했다.

엔지니어는 세컨더리 데이터 디렉터리를 지우고 pg_basebackup 을 돌렸지만 출력 없이 멈췄다. max_wal_senders 를 3 에서 32 로 올렸다가 세마포어 문제로 PostgreSQL 이 재시작을 거부하자 max_connections 를 8000 에서 2000 으로 낮췄다. 그래도 pg_basebackup 이 바로 시작하지 않자 23:30 UTC 무렵 엔지니어는 데이터 디렉터리를 다시 지웠는데, 그곳은 세컨더리가 아니라 프라이머리였다. 1~2초 만에 멈췄지만 약 300 GB 가 지워졌다. 백업을 찾아보니 pg_dump 백업은 9.6 데이터베이스를 pg_dump 9.2 로 뜨다 오류로 끝나 S3 버킷이 비어 있었다. 백업 작업이 데이터 디렉터리가 없는 애플리케이션 서버에서 돌았기 때문에 Omnibus 가 9.2 를 골랐다. 실패 알림 메일은 DMARC 서명이 없어 받는 서버가 거부했다. 데이터베이스 서버에는 Azure 디스크 스냅샷도 켜져 있지 않았다.

GitLab 은 데이터 손실을 줄이려고 6시간 전에 손으로 뜬 LVM 스냅샷을 쓰기로 했다. 스테이징의 PostgreSQL 데이터 디렉터리를 운영으로 복사하는 데 약 60Mbps 로 제한된 느린 디스크 때문에 약 18시간이 걸렸다. 스테이징 복사본에서는 웹훅을 지워 두었으므로 웹훅은 따로 띄운 데이터베이스에서 SQL 덤프로 가져왔고, 이전 ID 를 다시 쓰지 않게 시퀀스를 100,000 씩 올렸다. 2월 1일 17:00 UTC 에 웹훅 없이 데이터베이스를 복원했고, 18:00 UTC 무렵 웹훅 복원과 확인을 마쳤다. 1월 31일 17:20 UTC 이후의 데이터베이스 변경은 잃었다. GitLab 은 이 구간의 끝을 글 첫머리에서는 00:00 UTC 로, 데이터 손실 절에서는 23:30 UTC 로 적었고, 프로젝트 약 5,000개, 댓글 5,000개, 새 사용자 계정 약 700개가 영향을 받은 것으로 추정했다. Git 저장소와 위키는 따로 저장돼 지워지지 않았다. GitLab 은 복구 과정을 공개 Google 문서와 YouTube 생중계, Twitter 로 알렸다.

심각도
–
영향 범위
GitLab.com unavailable for about 18 hours; database changes made after 17:20 UTC on 31 January, such as projects, comments, user accounts, issues and snippets, were lost (GitLab gives the end of the window as 00:00 UTC in its summary and 23:30 UTC in its data loss section); Git repositories and wikis were unavailable but not lost
영향 사용자
GitLab.com users; roughly 5,000 projects, 5,000 comments and 700 new user accounts lost; GitLab Enterprise, GitHost and self-managed GitLab CE users not affected
지역
–
감지까지
–
복구까지
18h

증상

2017년 1월 31일 19:00 UTC 무렵 GitLab.com 의 데이터베이스 부하가 올랐고, 많은 사용자가 이슈와 머지 리퀘스트에 댓글을 달지 못했다. 스팸으로 의심되는 부하였고, 잡는 데 몇 시간이 걸렸다.

23:00 UTC 무렵 부하 때문에 PostgreSQL 세컨더리의 복제가 뒤처지다가 멈췄다. 세컨더리에 필요한 WAL 세그먼트를 프라이머리가 이미 지운 뒤였고, WAL 아카이빙을 쓰지 않았으므로 세컨더리를 손으로 다시 동기화해야 했다.

엔지니어가 세컨더리의 데이터 디렉터리를 지우고 pg_basebackup 을 돌렸지만, --verbose 옵션을 켰는데도 아무 의미 있는 출력 없이 멈춰 있었다. 몇 번 다시 시도하자 pg_basebackup 은 max_wal_senders 로 정하는 복제 연결이 master 에 모자라 접속할 수 없다고 알렸다.

max_wal_senders 를 3 에서 32 로 올리자 PostgreSQL 은 세마포어를 너무 많이 만든다며 재시작을 거부했다. max_connections 가 8000 이었고, 2000 으로 낮추자 재시작됐다. 그래도 pg_basebackup 은 바로 복제를 시작하지 않았고, strace 로 보니 poll 호출에서 멈춰 있었을 뿐 다른 단서는 없었다.

23:30 UTC 무렵 엔지니어는 앞선 시도에서 pg_basebackup 이 세컨더리 데이터 디렉터리에 파일을 남겼다고 생각해 디렉터리를 다시 지웠는데, 실제로는 프라이머리에서 지웠다. 1~2초 만에 멈췄지만 약 300 GB 가 이미 지워졌다. pg_basebackup 은 프라이머리가 복제 데이터를 보낼 때까지 조용히 기다리는 것이 정상 동작이었지만, 이 동작은 런북에도 공식 문서에도 분명히 적혀 있지 않았다.

복원하려고 백업을 찾자 pg_dump 백업을 올리는 S3 버킷이 비어 있었고 최근 백업은 어디에도 없었다. 데이터베이스 서버에는 Azure 디스크 스냅샷이 켜져 있지 않았고, 프라이머리와 세컨더리 모두 데이터가 지워져 어느 쪽에서도 복원할 수 없었다. pg_dump 백업은 백업 작업이 PostgreSQL 9.6 에 pg_dump 9.2 를 돌려 오류로 끝나고 있었다. cron 실패 알림 메일은 DMARC 서명이 없어 받는 메일 서버가 거부했다. 쓸 수 있는 사본은 스테이징용 LVM 스냅샷뿐이었다. 하나는 거의 24시간 전 것이었고 하나는 장애 약 6시간 전에 엔지니어가 손으로 뜬 것이었다. 약 60Mbps 로 제한된 스테이징 디스크에서 복사하는 데 약 18시간이 걸렸다.

GitLab.com 은 1월 31일 17:20 UTC 이후의 프로젝트, 댓글, 사용자 계정, 이슈, 스니펫 같은 데이터베이스 변경을 잃었다. GitLab 은 이 구간의 끝을 글 첫머리에서는 00:00 UTC 로, 데이터 손실 절에서는 23:30 UTC 로 적었다. 프로젝트 약 5,000개, 댓글 5,000개, 새 사용자 계정 약 700개가 영향을 받은 것으로 추정했다. Git 저장소와 위키는 장애 중 쓸 수 없었지만 지워지지 않았다.

공개 모니터링 사이트 dashboards.gitlab.com 은 장애 중 몰린 사용자 부하를 견디지 못했다.

증상 데이터베이스 과부하복제 지연데이터 유실서비스 중단

원인

촉발
1월 31일 23:00 UTC 무렵 데이터베이스 부하로 세컨더리의 복제가 멈췄고, 세컨더리를 다시 맞추던 엔지니어가 세컨더리가 아니라 프라이머리에서 PostgreSQL 데이터 디렉터리를 지웠다.

감지

프라이머리에서 데이터 디렉터리를 지운 엔지니어가 스스로 실수를 알아채고 1~2초 만에 삭제를 멈췄다.

조치

후속 조치

교훈

GitLab 은 장애를 GitLab.com 이 약 18시간 멈춘 문제와 복원이 18시간 넘게 걸린 문제로 나눠 5 Whys 로 분석했다. 세컨더리를 다시 만드는 작업은 자동화되지도 제대로 문서화되지도 않아 손으로 해야 했다. 표준 백업은 PostgreSQL 버전이 맞지 않아 조용히 실패했고, 실패 알림 메일은 DMARC 서명이 없어 거부됐다. 다른 백업 절차를 믿고 Azure 디스크 스냅샷을 켜지 않았다. 백업 절차를 맡은 사람이 없어 아무도 정기적으로 시험하지 않았다.

GitLab 은 운영 엔지니어가 특정 명령을 못 쓰게 막는 것보다 재해 복구를 고치고 지금 어느 호스트를 쓰는지 더 분명히 보이게 하는 데 집중하기로 했다. rm 을 더 안전한 별칭으로 바꾸면 실수로 지우는 것은 막지만 디스크 손상처럼 데이터를 잃는 다른 경우는 막지 못한다. 이상적인 환경은 실수해도 영향이 거의 없이 빠르게 복구할 수 있는 곳이고, 그러려면 복구 절차를 정기적으로 해 보고 변경을 쉽게 시험하고 되돌릴 수 있어야 한다.

출처

관련 사례

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

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

AI 도구와 스크립트용

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