Incident Library
적용된 필터 구성 요소데이터베이스 복제증상데이터 유실기여 요인장애 중첩모두 풀기검색어 없이 필터만 적용한 목록입니다. 최근 사례부터입니다.
추천 필터 검색하면 검색어에서 찾은 분류 값이 여기에 나옵니다.

그래프

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

사례2건

2건 중 1~2번째

  1. 증상

    2025년 9월 2일 디스크 용량을 늘리는 작업 중 11:17 UTC 에 primary 데이터베이스 서버 db-02 의 RAID 배열에서 기존 드라이브 하나가 사라졌고 모니터링 알림이 울렸다. RAID 10 이라 데이터는 잃지 않았고 배열은 degraded 모드로 계속 동작했지만, 드라이브가 하나 더 고장 나면 버티지 못할 수 있었다.

    촉발

    디스크 용량을 늘리는 정기 작업 중 primary 데이터베이스 서버 db-02 의 RAID 배열에서 드라이브가 빠져 secondary 인 db-01 로 장애 조치를 했다. 그 뒤 db-02 를 복원하던 운영자가 실패한 복원의 흔적을 지우려다 db-01 에 있던 새 primary 의 데이터 디렉터리를 지웠다.

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

    11:17 UTC 에 db-02 의 RAID 배열에서 드라이브가 빠지자 모니터링 알림이 울렸고 Mythic Beasts 가 문제를 확인했다. 운영자가 db-01 의 데이터 디렉터리를 지운 것을 스스로 알아챘다. 그 뒤 백엔드 팀을 호출하고, 장애 대응을 시작하며 비상 상황을 선언했다.

    조치
    • 16:11 UTC 에 새로 만든 RAID 10 배열로 db-02 서버를 다시 올렸다.
    • 16:50 UTC 에 db-01 에 있던 primary 에서 막혀 있던 WAL 의 S3 보관을 다시 돌아가게 했다. 그래서 db-01 이 쌓인 WAL 을 지울 수 있게 됐다.
    • 17:30 UTC 에 S3 에서 백업을 가져오는 wal-g 로 db-02 에 데이터베이스 복원을 시작했다.
    • 알 수 없는 데이터베이스 상태를 피하려고 Synapse 와 db-01 의 primary 데이터베이스를 모두 내렸다.
    • 18:06 UTC 에 db-01 의 데이터 파티션을 읽기 전용으로 다시 마운트해, 나중에 살릴 경우에 대비해 데이터베이스가 더 망가지지 않게 했다.
    • S3 로 보관되지 못한 WAL 을 db-01 에서 db-02 로 복사해, S3 백업 복원이 끝나면 바로 재생할 수 있게 준비했다.
    • wal-g 를 같은 메이저 버전의 최신 릴리스로 올린 뒤, 전체 백업이 이미 들어 있는 비어 있지 않은 데이터 디렉터리를 받아들이도록 wal-g 를 고쳐 빌드했다. 09:23 UTC 에 이 wal-g 를 db-02 에 써서 S3 에서 다시 받지 않고 증분 백업을 복원했다.
    • S3 와 db-01 에서 옮겨 온 파일 시스템 양쪽에서 WAL 을 가져오는 래퍼 스크립트로 restore_command 를 설정하고 Postgres 를 standby 모드로 띄웠다. 11:00 UTC 부터 WAL 을 재생했다.
    • 16:45 UTC 에 db-02 를 primary 데이터베이스로 승격하고, Synapse 시험 worker 를 띄워 새 WAL 이 S3 에 올라오는 것을 확인한 뒤 16:54 에 Synapse 를 시작했다. 18:00 에 모든 worker 가 안정됐다.
    • WAL 재생이 느렸기 때문에, 없어진 복제본 대신 primary 데이터베이스에서 백업을 받도록 다시 설정했다. 03:26 UTC 에 새 증분 백업이 끝났다.
    • db-01 에 NVMe 디스크 두 개를 RAID 배열과 LVM 볼륨 그룹에 넣었다. 배열 재구성 중 재부팅해 서버가 올라오지 않자 배열을 부팅할 수 있는 상태로 고쳤고, db-02 의 basebackup 을 복사해 db-01 을 다시 복제 상태로 두었다.
    후속 조치
    • 저장 공간이 늘어난 지금 pg_repack 을 돌려 빈 공간을 되찾는다.
    • 증분 백업은 WAL 재생보다 훨씬 빨리 복원되므로 증분 백업을 더 자주 받도록 이미 바꿨다.
    • Synapse 가 데이터를 저장하는 방식을 고쳐 앞으로 필요한 저장 공간을 크게 줄인다.
    • 데이터베이스가 클러스터에서 맡은 역할에 따라 터미널 배경색을 바꾸는 방법을 검토한다. 데이터 디렉터리에 standby.signal 파일이 있는지 보고 primary 인지 secondary 인지 판단해 배경색을 바꾸는 안이 나왔다.
    • rm 의 alias 처럼 위험한 명령을 감싸는 래퍼 스크립트를 두거나, primary 에서 secondary 로 base backup 을 시작하는 일 같은 작업을 자동화하는 방법을 논의했다.
    • 복구 목표 시간을 줄인다. 더 쉽고 빠르게 복구할 수 있는 무료 인프라를 얻으려고 서비스 제공자들과 이야기하고 있다.
    • 재해 복구 서버뿐 아니라 모든 서버의 wal-g 를 올렸고, 올린 wal-g 로 재해 복구 시험을 한 차례 했다. 재해 복구 서버와 운영 서버를 계속 같게 유지하는 방법은 아직 찾지 않았다.
    • 다음 하드웨어 교체 때 ZFS 를 검토해, 엉뚱한 데이터베이스를 지우는 사고에서도 로컬 스냅숏으로 훨씬 빨리 복구할 수 있게 한다.
    • 장애 대응 절차에 status.matrix.org 를 장애 중 공식 정보원으로 갱신하는 단계를 넣고, advocacy 팀과 함께 소셜 미디어도 계속 갱신한다.
    교훈

    이번 장애가 이렇게 힘들었던 데에는 데이터베이스가 너무 크다는 점이 크게 작용했다. 엉뚱한 서버에서 파괴적인 명령을 실행한 것이 결정적인 순간이었다. 하지만 팀은 개인을 탓하지 않고, 사람이 쓰는 도구와 절차를 고쳐 같은 일이 다시 일어날 가능성을 줄여야 한다고 보았다. 세 사람이 함께 작업했지만 급박한 상황에서는 실수를 잡지 못했다. db-01 은 primary, db-02 는 secondary 처럼 읽히지만 실제 역할은 서로 바뀔 수 있으므로, 뜻이 담긴 서버 이름은 혼란을 부른다. primary 와 secondary 를 모두 잃고도 데이터를 잃지 않은 것은 정기적으로 시험한 특정 시점 복구 절차 덕분이었다. 다만 복구 목표 시간은 줄여야 한다. 재해 복구 시험에서 찾은 wal-g 수정이 운영 서버에 빠져 있었으므로, 재해 복구 서버와 운영 서버를 같게 맞춰야 한다.

  2. 증상

    Mackerel 을 온프레미스 데이터 센터에서 AWS 로 옮긴 뒤 애플리케이션 동작을 확인하다가, 특정 시간대의 데이터가 사라진 것을 발견했다. 2017년 8월 7일 14시 30분부터 15시 30분 사이에 PostgreSQL 서버에서 갱신된 데이터가 없었다.

    촉발

    새 마스터인 db001 로 접속을 바꾸고 한 시간 뒤 db001 과 네트워크 통신이 끊겼고, 복제가 멈춰 있던 스탠바이 서버 db002 로 의도하지 않은 장애 조치가 일어났다.

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

    마이그레이션 작업 뒤 애플리케이션 동작을 확인하던 중 특정 시간대의 데이터가 사라진 것을 발견했다.

    조치
    • db001 에 남은 테이블 데이터로 손으로 복구할 수 있는지 검토했지만, 이미 많은 데이터가 갱신되어 테이블 사이 정합성을 지키며 db002 에 반영하기 어렵다고 보고 포기했다.
    • 점검 전 베이스 백업, db001 에 남은 WAL, 백업 서버로 보내 둔 WAL 아카이브로 PostgreSQL 의 PITR 복구를 시도했지만, 갈라진 두 타임라인을 이어서 복구할 수 없어 포기했다.
    • 장애 조치 직후 db002 를 마스터로 하고 새 슬레이브 서버 db003 을 만들어 db002 와 db003 으로 이중화했다.
    • 장애가 난 db001 은 조사용으로 점검 상태에 남겨 두었다.
    • db002 에서 db001 과 같은 문제가 나지 않는지 일정 시간 지켜본 뒤 마이그레이션 점검을 마쳤다.
    후속 조치
    • PostgreSQL 레플리카 생성 스크립트가 recovery.conf 에 recovery_target_timeline = 'latest' 를 기본으로 넣도록 바꾼다.
    • Serverspec 같은 서버 구성 관리 테스트 도구에 recovery.conf 안에 recovery_target_timeline = 'latest' 문자열이 있는지 확인하는 항목을 더한다.
    교훈

    Hatena 는 이 문제가 평소 운영에서는 일어나지 않지만, PostgreSQL 버전을 올리거나 다른 리전으로 옮길 때 같은 구성을 다시 쓸 수 있다고 보았다. recovery_target_timeline = 'latest' 는 평소 스탠바이 서버에 두어도 문제가 없으므로, 사람이 빠뜨릴 여지를 없애려고 슬레이브를 만들 때 기본으로 넣도록 운영을 바꿨다. 이 설정을 쓰려는 사람은 자기 구성에서 문제가 없는지 먼저 확인하라고 덧붙였다.

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

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

원인별1