Incident Library
  • 원인
  • 구성 요소
  • 증상
  • 기여 요인
  • 사례

사례8건

8건 중 6~8번째

  1. 증상

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

    촉발

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

    원인
    구성 요소
    기여 요인
    감지

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

    조치
    • pg_basebackup 이 복제 연결이 모자라다고 알리자 max_wal_senders 를 기본값 3 에서 32 로 잠시 올렸고, PostgreSQL 이 재시작을 거부하자 max_connections 를 8000 에서 2000 으로 낮췄다.
    • 엔지니어가 프라이머리에서 데이터를 지우고 있다는 것을 알아채고 1~2초 만에 삭제 작업을 멈췄지만, 약 300 GB 가 이미 지워졌다.
    • 데이터 손실을 가장 줄일 수 있는 유일한 방법으로 장애 6시간 전에 만든 LVM 스냅샷을 쓰기로 하고, 스테이징의 PostgreSQL 데이터 디렉터리를 약 18시간 동안 운영으로 복사해 복원했다.
    • 웹훅 삭제를 거치지 않은 별도 스테이징 데이터베이스를 LVM 스냅샷으로 띄워 웹훅 테이블의 SQL 덤프를 만들고, 복원한 GitLab.com 데이터베이스에 가져왔다.
    • 장애 전에 쓰였을 수 있는 ID 를 다시 쓰지 않도록 모든 데이터베이스 시퀀스를 100,000 씩 올렸다.
    • GitLab.com 을 차례로 다시 열었다.
    • 17:20 UTC 이후에 만든 프로젝트는 NFS 클러스터에 남은 Git 저장소로 가장 기본적인 형태만 복원했고, 이슈와 머지 리퀘스트 같은 데이터는 되살리지 못했다.
    후속 조치
    • 모든 호스트의 셸 프롬프트인 PS1 을 고쳐 호스트와 환경을 더 분명히 구분한다.
    • 백업을 Prometheus 로 모니터링한다.
    • PostgreSQL 의 max_connections 를 알맞은 값으로 둔다.
    • PostgreSQL 의 시점 복구와 연속 아카이빙을 검토한다.
    • 운영 데이터베이스의 LVM 스냅샷을 매시간 뜬다.
    • 운영 데이터베이스에 Azure 디스크 스냅샷을 켠다.
    • 스테이징을 ARM 환경으로 옮긴다.
    • 운영 복제본을 되살린다.
    • PostgreSQL 백업 복원을 자동으로 시험한다.
    • PostgreSQL 복제 문서와 런북을 고친다.
    • PostgreSQL 백업에 pgbarman 을 쓸지 검토한다.
    • 데이터베이스 백업과 실시간 복제에 WAL-E 를 쓸지 검토한다.
    • 스트리밍 방식의 데이터베이스 복원을 만든다.
    • 데이터 내구성을 책임질 담당자를 정한다.
    • 세컨더리를 여러 대 두고 부하를 나누도록 pgpool-II 3.6.1 을 묶고 PostgreSQL 연결 풀링과 부하 분산을 구성한다.
    • 개발자가 데이터베이스 마이그레이션을 시험할 수 있도록 스테이징에서 Rails 마이그레이션을 실행하고 되돌리는 도구를 만든다.
    • 데이터베이스뿐 아니라 GitLab.com 인프라 전체의 복구 절차를 만들고 그 절차의 담당자를 정한다.
    • 공개 백업 모니터링 대시보드를 만들었고, 지금은 pg_dump 백업 데이터만 있지만 데이터를 더 넣는다.
    • 어뷰징 신고와 대응 시스템을 고쳐 스팸 신고로 사용자를 지울 때 데이터를 바로 영구 삭제하지 않게 한다.
    • 앞으로 공개하는 장애 문서에서는 엔지니어의 이름을 가린다.
    교훈

    GitLab 은 장애를 GitLab.com 이 약 18시간 멈춘 문제와 복원이 18시간 넘게 걸린 문제로 나눠 5 Whys 로 분석했다. 세컨더리를 다시 만드는 작업은 자동화되지도 제대로 문서화되지도 않아 손으로 해야 했다. 표준 백업은 PostgreSQL 버전이 맞지 않아 조용히 실패했고, 실패 알림 메일은 DMARC 서명이 없어 거부됐다. 다른 백업 절차를 믿고 Azure 디스크 스냅샷을 켜지 않았다. 백업 절차를 맡은 사람이 없어 아무도 정기적으로 시험하지 않았다. GitLab 은 운영 엔지니어가 특정 명령을 못 쓰게 막는 것보다 재해 복구를 고치고 지금 어느 호스트를 쓰는지 더 분명히 보이게 하는 데 집중하기로 했다. rm 을 더 안전한 별칭으로 바꾸면 실수로 지우는 것은 막지만 디스크 손상처럼 데이터를 잃는 다른 경우는 막지 못한다. 이상적인 환경은 실수해도 영향이 거의 없이 빠르게 복구할 수 있는 곳이고, 그러려면 복구 절차를 정기적으로 해 보고 변경을 쉽게 시험하고 되돌릴 수 있어야 한다.

  2. 증상

    복제 오류를 고치려고 mysql2 의 데이터베이스를 하나씩 지우던 중, 몇 분 뒤 mysql1 에서도 데이터베이스가 지워지고 있는 것을 알아챘다. 지워진 데이터베이스들은 복원될 때까지 쓸 수 없었다.

    촉발

    7월 30일 오후 4시 PDT 에 팀은 복제 오류를 고치려고 mysql2 를 다시 적재하기로 했다. 슬레이브 복제를 멈춘 뒤 mysql2 의 데이터베이스를 하나 지울 때마다 잠깐씩 쉬면서 지웠다.

    원인
    구성 요소
    기여 요인
    감지

    데이터베이스를 지우는 스크립트가 하나를 지울 때마다 짧게 쉬었기 때문에, 시작하고 몇 분 뒤 mysql1 에서도 데이터베이스가 지워지고 있는 것을 알아챘다.

    조치
    • mysql1 에서도 데이터베이스가 지워지는 것을 보고 스크립트를 멈췄다.
    • 곧바로 지워진 데이터베이스의 복원을 시작했다.
    • 인덱스와 외래 키 문제로 복원이 실패한 데이터베이스는 약 200GB 백업 파일에서 따로 꺼내 복원했고, 아직 복원하지 못한 나머지 데이터베이스도 다시 꺼내 복원했다.
    • 백업은 mysql1 보다 뒤처진 mysql2 에서 뜬 것이어서, 17일부터 30일까지의 바이너리 로그로 대부분의 데이터를 되살렸다. 15일과 17일 사이의 데이터 일부는 되살리지 못했다.
    후속 조치
    • 처음 문제였던 복제 오류를 줄이려고 행 기반 복제를 포함해 더 나은 복제 방식을 검토한다.
    • 바이너리 로그를 별도 서버에 백업으로 보관한다.
    • 전체 복원이 훨씬 빨라지도록 Percona XtraBackup 으로 백업한다.
    • 논리 백업 속도를 높이려고 mysql-zrm 대신 mydumper 를 쓴다.
    • 복잡한 시스템의 문서와 교육을 손본다.
    • 백업 절차의 하나로 예비 서버에서 전체 복원을 정기적으로 시험한다.
    • 복원에 걸리는 예상 시간을 더 정확하게 잡는다.
    • 호스팅하는 데이터베이스를 정기적으로 점검한다. 시험용 데이터베이스와 100GB 넘게 불어난 데이터베이스 여러 개가 이번 복원을 크게 늦췄다.
    • 더 크고 성능이 좋은 MySQL 클러스터로 옮긴다. 이 이전은 장애 전부터 계획돼 있었다.
    • 장애와 유지보수, 자원 사용량과 경고 신호를 대시보드로 커뮤니티에 더 잘 알리는 정책과 수단을 만든다.
    • 핵심 서비스의 용량 한계를 정하고 감시한다.
    • 핵심 서비스에 이중화와 복원 절차를 갖추고 직원 훈련을 한다.
    • 핵심 서비스를 더 많이 Chef 로 옮긴다.
    • 문서, 정책, 성능 지표를 주기적으로 검토한다.
    • 커뮤니티의 전문 지식으로 연구소의 역량을 보완할 더 나은 방법을 찾는다.
    • 가장 낡은 인프라를 교체하고 가장 심한 기술 부채를 해소할 기금을 모은다.
    교훈

    Open Source Lab 은 이번 장애에서 MySQL 클러스터를 운영하는 방식과 연구소 전체가 자원을 계획하고 관리하는 방식 모두에서 교훈을 얻었다고 적었다. 디스크 공간을 아끼려고 백업을 강하게 압축한 선택은 전체 복원이 드물었던 그동안에는 괜찮았지만 이번 복원을 크게 늦췄다. 더 많은 프로젝트를 받으려고 자원을 빠듯하게 나눠 써 왔고, 그 대가로 백업을 압축하고 바라던 것보다 이중화를 덜 갖췄다. 장애가 길었던 것과 진행 상황을 알리지 못한 것은 받아들일 수 없는 일이었다고 밝혔다.

  3. 증상

    2013년 7월 18일 2:42 AM PDT 에 모니터링 시스템이 결제용 Redis 클러스터의 장애를 당번 엔지니어에게 알렸다. redis-master 호스트의 부하가 극도로 높았고, redis-master 에 기대는 서비스들이 실패하고 있었다.

    촉발

    7월 18일 1:35 AM PDT 에 네트워크 연결이 끊겨 결제용 redis-slave 전부가 동시에 redis-master 와 끊겼고, 다시 붙으면서 한꺼번에 전체 동기화를 요청했다. 부하가 높아진 redis-master 를 당번 엔지니어가 재시작했다.

    원인
    구성 요소
    기여 요인
    감지

    2:42 AM PDT 에 모니터링 시스템이 Redis 클러스터 장애를 당번 엔지니어링 팀에 알렸다. 잘못된 결제는 3:28 AM PDT 에 결제 시스템 모니터링이 이상 동작을 보고해 알았고, 12:36 PM PDT 에는 모니터링이 같은 이상의 재발을 찾았다.

    조치
    • 2:42 AM PDT 에 당번 엔지니어가 높은 부하를 해결하려고 redis-master 를 재시작했다.
    • 4:10 AM PDT 에 신용카드 결제가 더 일어나지 않게 결제 시스템을 내렸다.
    • 6:24 AM PDT 에 정지된 계정을 모두 되살렸고, 결제 시스템은 원인을 조사하는 동안 꺼 둔 채로 뒀다.
    • 별도 관계형 데이터 저장소에 따로 적어 둔 잔액 기록으로 redis-master 에서 잃은 계정 잔액을 복원했다.
    • 12:36 PM PDT 에 같은 이상이 다시 나타나자 결제 시스템을 다시 내리고 정지된 계정을 바로 되살렸다.
    • 2:57 PM PDT 부터 잘못된 신용카드 결제를 환불해 7월 19일 3:00 PM PDT 에 모두 마쳤다.
    • 7월 19일 3:30 PM PDT 에 영향받은 모든 계정에 최근 30일 Twilio 사용액의 10% 를 크레딧으로 줬다.
    • 장애를 일으킨 원래 Redis 클러스터를 교체했고, redis-master 의 잘못된 설정을 찾아 고쳤다.
    • 7월 19일 7:10 PM PDT 부터 고객 계정 묶음마다 결제 시스템을 차례로 켰다.
    후속 조치
    • redis-master 의 Redis 재시작을 막고, 앞으로 redis-master 복구는 slave 를 승격해 처리한다.
    • 결제 잔액이 없거나 쓸 수 없으면 계정을 정지하거나 신용카드를 결제하지 않도록 안전장치를 넣는다.
    • 결제 시스템이 잔액을 따로 적어 두는 데이터베이스와 실시간으로 대조하게 바꾼다.
    교훈

    Twilio 는 현재 잔액 데이터와 잔액을 갱신하는 능력을 동시에 잃자 자동 충전 시스템이 위험한 방향으로 실패했다고 봤다. 잔액이 없거나 쓸 수 없을 때 자동 충전 시스템은 계정을 정지하지 않고 카드를 결제하지 않는 쪽으로 멈춰야 했다.

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

이 8건에서 가장 많이 나오는 구성 요소: 데이터베이스 복제, 데이터베이스. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별4

인증서·자격 증명 만료1건

  1. IL-0490 관리형 데이터베이스에서 자체 MySQL로 옮긴 뒤 이전 직전에 쓴 기록이 새 데이터베이스에 없어 LootLocker 서비스가 저하됐다

소프트웨어 결함1건

조직
  1. IL-0281 PostHog US 리전 Logs 제품의 ClickHouse 클러스터가 S3 에 둔 로그 데이터를 거의 다 지워 3일보다 오래된 로그가 사라졌다

운영 실수4건

  1. IL-0242 primary 와 secondary PostgreSQL 데이터베이스를 모두 잃어 matrix.org homeserver 가 약 24시간 동안 멈췄다
  2. IL-0431 AWS 로 옮기는 점검 중 PostgreSQL 이 의도하지 않게 장애 조치되어 한 시간 동안 갱신된 데이터가 사라졌다
    Hatena2017
  3. IL-0218 복제를 다시 맞추다 PostgreSQL 프라이머리의 데이터 디렉터리가 지워지고 백업이 작동하지 않아 GitLab.com 이 약 18시간 멈추고 데이터를 잃었다
    GitLab2017
  4. IL-0319 mysql2 를 다시 적재하려고 데이터베이스를 지우는 동안 mysql1 의 데이터베이스도 함께 지워졌다

설정 변경2건

  1. IL-0331 RDS MySQL Multi-AZ 장애 조치 뒤 읽기 복제본 다섯 대의 복제가 모두 깨지고 데이터 5초 분량이 사라졌다
  2. IL-0215 Twilio 결제 시스템의 Redis 가 잔액 데이터를 잃고 읽기 전용이 되어 자동 충전 고객의 신용카드가 반복 결제됐다
    Twilio2013