GitLab DB削除事故(2017)— 5重バックアップ全滅
本番DBの誤削除で約310GBが消え、5系統の備えは全滅——GitLab事故の実録から、リストア検証なきバックアップの危うさと疲労時の破壊的操作を避ける原則を、公式ポストモーテムに忠実に学べる。
- 2017年1月31日、レプリケーション復旧作業中の疲労したエンジニアが本番db1をレプリカdb2と取り違えて rm -rf を実行。約310GBのうち約4.5GBを残して喪失し、GitLab.comは約18時間停止した。
- pg_dumpは9.2/9.6のバージョン不整合で無音失敗、失敗通知はDMARC検証で拒否され不達、S3バケットは空、AzureスナップショットはDB対象外、レプリカは再同期のため消去済み——5系統の備えが全て機能しなかった。
- 偶然6時間前に取られていたstaging同期用LVMスナップショットから復旧したが、issue・MR・コメント約6時間分を喪失。作業メモの公開とライブ配信で全過程を晒し、個人を処分しない方針を明示した。
何が起きたか(日時・影響範囲)
2017年1月31日23時頃(UTC)、GitLab.comの本番PostgreSQLプライマリ db1.cluster.gitlab.com 上で、レプリカ db2 に対して実行するつもりだったデータディレクトリの rm -rf が実行され、約310GBのうち約4.5GBを残してデータが消えた。5系統あったはずの備えはどれも役に立たず、約6時間前に偶然取られていたLVMスナップショットからの復旧となった。issue・マージリクエスト・コメント・ユーザー登録など約6時間分のデータ(推計でプロジェクト約5,000件、コメント約5,000件、新規ユーザー約700件)を喪失し、サービスは約18時間停止した。GitリポジトリとwikiはDB外のストレージにあり無事で、self-managed版にも影響はない。
タイムライン(UTC)
- 17:20頃: 担当エンジニアがstaging同期のため本番db1のLVMスナップショットを手動取得。結果的にこれが命綱になる
- 21:00頃: スニペットを大量作成するスパムに加え、荒らしの虚偽通報を発端とする従業員アカウントのハード削除ジョブが重なり、DB負荷が急上昇
- 22:00頃: db2のレプリケーションが遅延の末に停止。追い付きに必要なWALセグメントは既に破棄され、
pg_basebackupによる全再同期が必要に - 23:00頃: 「データディレクトリが残っているのが原因」と思い込み削除を実行。1〜2秒後、シェルの接続先がdb2ではなくdb1だと気づく
- 23:27: 削除を停止。残存は約4.5GB。サービス停止を決断
- 2月1日 0:36: staging DBからの復元を決定。低速なストレージから約300GBのコピーが続く(staging同期時に削除されていたWebhookは別のスナップショットから復元)
- 2月1日 18:14: GitLab.com復旧
根本原因の技術解説
直接の引き金は取り違えだが、そこへ至る技術的連鎖がある。高負荷でdb2が追随できなくなると、プライマリは保持上限(wal_keep_segments)を超えた古いWALを破棄するため、レプリカは差分適用では復帰できず、pg_basebackup でのフル再同期しか道がなくなる。これが空のデータディレクトリを要求するため、db2側は消去済みだった。ところが pg_basebackup は実行しても無出力のまま止まって見えた。実際はプライマリ側のチェックポイント完了を黙って待つ挙動(既定では負荷分散のため時間をかけて書き出す)だが、進捗もエラーも表示されないため「ディレクトリの状態が悪い」と誤読された。さらに max_wal_senders を増やすとセマフォ不足でPostgreSQLが起動を拒み、max_connections を8000から2000へ下げるなど試行錯誤が深夜まで続いた。複数のターミナルでdb1とdb2を往復し、疲労の極みで打った削除コマンドの相手が本番だった。
取り違えは最後の一押しであり、ミス一つで全データを失える構造——本番とレプリカを見分けにくい端末、破壊的操作への保護なし、検証されないバックアップ——が本質である。GitLab自身も「システムの失敗」と結論づけ、担当者を処分しない方針を公表した。
なぜ防げなかったか
横にスクロール
公式ポストモーテムは「配備していた5つのバックアップ/レプリケーション手法のうち、確実に動いていた、あるいはそもそも設定されていたものは一つもなかった」と総括している。
| 備え | 想定 | 実際 |
|---|---|---|
| pg_dump定期バックアップ | cronで24時間ごとに取得 | 9.2のバイナリで9.6サーバーに実行され即失敗。成果物は数バイトのファイルのみ |
| S3への保管 | ダンプをアップロード | バケットは空。誰も中身を確認していなかった |
| Azureディスクスナップショット | サーバー単位の保護 | NFSサーバーのみ有効化され、DBサーバーは対象外 |
| LVMスナップショット | 24時間ごとに取得 | 定時分は最大24時間古い。使えた1本はstaging同期用に偶然取られたもの |
| レプリケーション(db2) | 障害時の切替先 | 遅延で停止し、再同期のため消去済み。誤操作も伝播するためそもそもバックアップではない |
pg_dumpの無音失敗には機構的な理由がある。omnibus-gitlabはデータディレクトリの PG_VERSION ファイルでPostgreSQLのバージョンを判定するが、バックアップ実行ホストにはデータディレクトリ自体がなく、既定の9.2バイナリが選ばれ、9.6サーバーへの pg_dump は毎回即座に失敗していた。cronの失敗通知メールはDMARC検証で受信側に拒否され、誰にも届かない。「バックアップはあるはず」という認識だけが残り、リストア検証も鮮度監視も存在しなかった。
教訓(原則として一般化)
- リストアで検証されていないバックアップはバックアップではない。復元テストを定期的に自動実行し、成果物のサイズと鮮度を監視する。GitLabは対策としてWALのS3アーカイブ(WAL-E)とバックアップ監視を導入した
- 失敗は「通知」ではなく「検知」で扱う。メールは届かないことがある。無音で失敗できる仕組みは、存在しないのと同じ
- 本番環境を視覚で区別する。ホスト名と環境を示す色分けプロンプト(PS1変更)は公開された対策リストの筆頭だった
- 疲労時・深夜に破壊的操作をしない。やむを得ない危険作業は二人一組で相互確認する
- 責任追及より事実の公開。GitLabは作業メモを公開ドキュメントで共有し、復旧作業をYouTubeでライブ配信した。この透明性が信頼をつなぎ、正確なポストモーテムと再発防止を可能にした
レプリケーションとWALの内部動作はデータベース、バックアップ検証や運用自動化の設計はDevOpsの各記事も参照。
- 5系統の内訳: pg_dump(9.2/9.6不整合で無音失敗)・S3(空)・Azureスナップショット(DB対象外)・LVM(24時間周期)・レプリカ(停止・消去済み)
- レプリカはバックアップではない。誤削除や論理破壊はそのまま伝播する
- 復旧源は約6時間前のstaging同期用LVMスナップショット。損失は約6時間分のDBデータ(Gitリポジトリは無事)
- 一般化: リストア訓練・無音失敗の監視・環境の視覚的識別・疲労時の破壊的操作禁止
障害事例の記事ガイド
GitLab DB削除事故(2017)— 5重バックアップ全滅を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
GitLab
比較で見る軸
難易度: advanced / カテゴリ: 障害事例 / タグ数: 6
導入後に効く点
pg_dumpは9.2/9.6のバージョン不整合で無音失敗、失敗通知はDMARC検証で拒否され不達、S3バケットは空、AzureスナップショットはDB対象外、レプリカは再同期のため消去済み——5系統の備えが全て機能しなかった。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- 障害事例
- タグ数
- 6
判断チェックリスト
- 自社の用途が「GitLab / ポストモーテム」に近いか確認する。
- 強みである「2017年1月31日、レプリケーション復旧作業中の疲労したエンジニアが本番db1をレプリカdb2と取り違えて rm -rf を実行。約310GBのうち約4.5GBを残して喪失し、GitLab.comは約18時間停止した。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。