エンジニアが間違ったデータを消した日:GitLab 2017年の話
2017年1月31日、GitLabのエンジニアが削除コマンドを間違ったサーバーで実行し、約300GBを失いました。その後、5層のバックアップがすべて失敗。GitLab自身の事後分析に沿って振り返り、削除コマンドを打つ前の1分のチェックリストで締めます。
これは実話ですが、私自身の話ではありません。GitLabが2017年2月10日に公表した事後分析(末尾にリンク)に沿って振り返ります。エンジニアの名前は書きません。GitLabの最初の作業メモには名前がありましたが、同社は以後の障害では名前を伏せると述べています。
その人が何を考え、何を感じたかは分からないので、記事ではそれを事実として描きません。私の推測や提案の部分は、そう明記します。
先に要点#
- 2017年1月31日、GitLab.comのデータベースの副サーバーを作り直している最中に、エンジニアが副サーバーでなく主サーバーで削除コマンドを実行しました。1、2秒後に止められましたが、約300GBが消えていました。
- その後、バックアップの層が1つずつ失敗するか、使えないと分かりました。復旧できた唯一のコピーは、同じエンジニアが約6時間前に偶然、手で取ったスナップショットでした。
- サービスは約18時間停止。GitLabは、少なくとも5,000のプロジェクト、5,000のコメント、約700の新規アカウントの変更が失われたと推定しました。Gitリポジトリとwikiは影響を受けませんでした。
- 打ち間違いは最後の輪にすぎませんでした。その前に、負荷の急増、文書のない手作業の手順、誰も気づかないバックアップの失敗がありました。
- GitLabはすべてを公開しました:共有文書、復旧のライブ配信、長い事後分析。技術的な教訓と同じくらい学ぶ価値があります。
むずかしい言葉をやさしく#
| 用語 | やさしい意味 |
|---|---|
| 主サーバー(プライマリ) | 実際に使われ、利用者のすべての変更を受けるデータベースのマシン。 |
| 副サーバー(セカンダリ) | 主サーバーをリアルタイムで複製し、故障時に代わるマシン。 |
| レプリケーション | 副サーバーが主サーバーから変更の記録を受け続け、同じ状態を保つこと。 |
| バックアップ | 障害後に復旧するためのデータのコピー。レプリケーションとは別。 |
| スナップショット | ディスクのある瞬間の状態を写し取ったもの。 |
| 災害復旧 | 深刻なデータ損失の後にサービスを取り戻すこと。 |
| ポストモーテム | 障害の記録:何が起き、なぜで、何を直すか。 |
| ブレームレス | 責める相手でなく、仕組みや手順に目を向ける分析の仕方。 |
1. いつもの日が、悪い方へ向かう#
以下の時刻はすべて、2017年1月31日のUTCです。
17:20ごろ。 あるエンジニアが、本番データベースのディスクのスナップショットを取り、テスト環境に読み込みます。接続の振り分け装置の負荷試験のためです。この作業は通常、毎日01:00に自動で行われていました。この手作業のスナップショットが、その夜を救うことになりますが、まだ誰も知りません。
19:00ごろ。 データベースの負荷が急増。チームはスパムを疑い、多くの人がコメントを投稿できません。負荷の一部は、GitLabの従業員1人のデータを消すバックグラウンド処理でした。その人のアカウントが、不正利用として誤ってマークされ、削除が予約されていたのです。GitLabによると、不正利用の報告システムは細部を見落としやすいものでした。
23:00ごろ。 副サーバーが遅れ、複製が止まります。主サーバーがすでに片付けた変更ログが必要で、当時のGitLab.comはそれを再利用のために保管していませんでした。唯一の方法は、副サーバーを手作業で最初から作り直すことでした。
チームが作業を始めます。再構築のツールは、何も表示せず黙って固まります。設定を調整すると、PostgreSQLが再起動を拒み、さらに調整します。運用をやったことのある人には、おなじみの夜です:小さな作業が小さな作業を呼び、どの段階も筋が通って見えます。
2. 23:30ごろ:1つのコマンド、間違ったマシン#
複製を作り直すには、副サーバーのデータディレクトリを消して、読み込み直す必要があります。GitLabの事後分析によると、エンジニアはその削除を主サーバーで実行しました。
約1、2秒後に止めました。しかし約300GBのデータは、すでに消えていました。
その人の頭の中で何があったかを知る方法はありませんし、推測もしません。自信を持って言えるのは、事後にGitLabが、とても素朴な項目を対策リストに入れたことです:シェルのプロンプトに、今どのマシンのどの環境にいるかを分かりやすく表示する。この項目だけで、当時、2台を見分けることが十分に分かりやすくなかったと見ていることが分かります。
3. バックアップを探す:5つの層、どれも使えない#
直後に、バックアップを探しました。この部分が、この話を教訓にしています:
| 守りの層 | 何が起きたか |
|---|---|
| 毎日のバックアップ(pg_dump) | 実際のデータベースと違うバージョンのツールで動かして失敗。クラウドの保管先は空。失敗の通知はメールで送られたが、メール認証の設定がなく拒否され、誰も知らなかった。 |
| クラウドのディスクスナップショット | ファイルサーバーでは有効だが、他の層で十分と考えて、データベースサーバーでは有効にしていなかった。 |
| LVMスナップショット | 24時間ごと。ステージングへのコピー用で、災害復旧のためではない。手作業のものが約6時間前に1つだけ。 |
| 副サーバーへの複製 | 障害時の切り替え用で、災害復旧用ではない。しかも副サーバーは、作り直すために消したばかりだった。 |
| ステージング環境のコピー | データはあったが、別のリージョンの遅いディスクの上。 |
目立つ点が2つあります。1つ目、各層はそれぞれ違う理由で失敗し、どの理由ももっともらしく聞こえます。2つ目、GitLabによると、バックアップには責任者がおらず、復元できるかを定期的に確かめる人がいませんでした。
4. 復旧:18時間のコピー#
チームは、24時間前のものより失うデータが少ないので、6時間前の手作業のスナップショットを選びました。
- データはステージングの遅いディスクの上にあり、本番へ戻すコピーに約18時間かかりました。ディスクは約60Mbpsに絞られ、ボトルネックはディスクでした。
- データベースは、1月31日の17:20 UTCの状態に復元されました。
- 2月1日の17:00 UTC:GitLab.comは、一時的にWebhookなしで再開。Webhookは、別に取り出したデータから作り直されました。
- データベースのID用の数え上げの番号を10万増やし、障害前に発行されたIDの再利用を避けました。
- 最終確認は、同日の18:00 UTCごろに終わりました。
被害。 GitLabは、17:20ごろ以降の変更が、少なくとも5,000のプロジェクト、5,000のコメント、約700の新規アカウントで失われたと推定しました。(事後分析では、期間の終わりが、被害の節では約23:30、冒頭では00:00とあるので、その境目を正確とは思わないでください。)17:20以降に作られたプロジェクトは、Gitリポジトリだけが復元され、イシューとマージリクエストはありません。Gitリポジトリとwikiは別に保管されていたので影響を受けず、自社運用の顧客も影響を受けませんでした。
5. GitLabが公の場でどう対応したか#
技術以外で、私が最も学ぶ価値があると感じた部分です。
- チームは、各手順を記録する公開の共有文書を保ちました。
- 復旧をYouTubeでライブ配信し、ピーク時には約5,000人が見て、Twitterでも更新しました。
- わずか10日後に、約15の対策タスクの一覧を添えた長い事後分析を公表しました。
- 最初の文書がエンジニアの名前を挙げていたことを認め、今後の障害では名前を伏せると約束しました。
このやり方はブレームレスとよく呼ばれます:1人の間違いを、仕組みがそれを許した印と捉え、「誰が間違えたか」でなく「次に誰もこう間違えないためにはどうするか」を問います。
6. 打ち間違いは、最後の輪にすぎない#
事後分析から解きほぐすと、1つの間違ったコマンドの背後には、鎖があります:
- スパムと、誤って動いたデータ削除処理による負荷の急増。
- システムが変更ログを保管していなかったため、副サーバーが複製を止めた。
- 手作業で文書の乏しい再構築の手順と、黙って固まるツール。
- 毎日のバックアップが静かに失敗し、通知は遮られた。
- データベースでクラウドのスナップショットが有効でなかった。
- バックアップと復元テストに責任者がいなかった。
- 取り違えやすい2台のマシンと、見分けのつかないプロンプト。
この鎖の中で、打ち間違いは、個人のレベルでは最も防ぎにくく、仕組みのレベルでは最も防ぎやすい輪かもしれません。
7. GitLabが直したこと#
GitLabの公開の対策リストには、次のような項目がありました:
- シェルのプロンプトに、マシンと環境を分かりやすく表示。
- 公開ダッシュボードによるバックアップの監視。
- 本番データベースの1時間ごとのLVMスナップショット(GitLabは完了と報告)。
- データベースサーバーのクラウドディスクスナップショット。
- バックアップの自動の復元テスト。
- 継続的なログ保管によるある時点への復旧。
- 複製の再構築の文書と手順書。
- データの耐久性に責任を持つ人の指名。
注目すべきは、GitLabが、危険なコマンドを防ぐことだけでなく、復旧できることとマシンを明確に見分けることに重点を置くと述べたことです。打ち間違いはまた起きると捉え、その結果を小さくしたかったのです。
8. 私が学ぶこと:削除コマンドを打つ前の1分#
この部分は私の提案で、GitLabの結論ではありません。
- どのマシンにいるか? プロンプトのホスト名を読み、決めつけない。2台が似ているなら、本物のプロンプトに色を付ける。
- どのディレクトリにいるか? 消す前に、現在のパスを表示する。
- まず一覧表示だけのモードで。 本当に消す前に、何が消えるかを見る。
- バックアップは本当にあるか? 「たぶん」でなく、一度復元して、データが戻るのを見た。
- 2人目が画面を見る。 本番データでは、もう1組の目は安い。
- 疲れ切っているときや、修正の途中ではやらない。 止まり、休み、それから行う。これは一般的な経験からの推測で、GitLabの事後分析は、そのエンジニアが疲れていたとは述べていません。
- 間違えた直後の1分の計画を持つ。 コマンドを止め、すぐ誰かに伝え、黙って直そうとしない。ここでは、1、2秒後にコマンドを止めたことが、残りを救いました。
9. AIがコマンドを実行できるとき#
この話は2017年で、人の代わりにコマンドを実行するAIエージェントが現れる前のことです。でもこれは1つのことを示唆します:削除の権限を持つAIエージェントは、決して疲れず、そして「何かおかしい」とも決して感じない、コマンドを打つ者です。AIを鵜呑みにしてはいけない理由は別の公開された事件に触れ、MCPとは何かはAIのヒューズ、つまり最小限の権限、読み取り専用から始める、重要な作業は人が承認する、を扱います。それらの原則は、新しい種類のコマンドを打つ者に当てはめた、GitLabの教訓です。
10. バランスの取れた見方#
- 主な情報源は1つです。 私は主にGitLab自身の事後分析に拠っています。細部(たとえばデータ損失の期間の終わり)が節によって違います。
- エンジニアが何を感じたかは分かりません。 記事は感情を事実として描きません。
- この障害は10年近く前のことです。 ツールは変わりましたが、原因(復元したことのないバックアップ、責任者の不在、取り違えやすいマシン)は今もとても身近です。
- 「ブレームレス」は責任がないという意味ではありません。 責任を、1人から、仕組みとそれを設計する人たちへ移すのです。
11. 結論#
1人のエンジニアが、1つのコマンドを打ち間違えました。けれど、この話を痛いものにしたのは、5層のバックアップがすべて、必要なときに使えなかったことです。教訓は2か所にあります:間違ったコマンドを打ちにくくすること、そしてそれでも起きたときに、結果を小さくすること。
今日の小さな一歩:今信頼しているバックアップを1つ選び、今週中に復元を試す。一度も試したことがないなら、バックアップはなく、あるのは希望だけです。
自分で誤ってデータを消した話があるなら、7つの問いに答える形で書き留めてください:いつ起きたか、直前に何をしていたか、何を失ったか、どう気づいたか、どう復旧したか、その後何を変えたか、他の人に何を持ち帰ってほしいか。
参考資料#
よくある質問
- 2017年1月31日にGitLab.comで何が起きましたか
- GitLabの事後分析によると、データベースの副サーバーを作り直している最中に、エンジニアが副サーバーでなく主サーバーで削除コマンドを実行しました。コマンドが止められるまでに約300GBが消えました。サービスは約18時間止まり、約17:20 UTC以降のデータが失われ、少なくとも5,000のプロジェクト、5,000のコメント、約700のアカウントと推定されました。
- なぜバックアップがすべて使えなかったのですか
- 層ごとに理由が違いました:毎日のバックアップはツールのバージョンが違って空で、失敗通知はメール認証の設定がなく拒否されました。クラウドのディスクスナップショットはデータベースサーバーで有効になっていませんでした。LVMスナップショットは24時間ごとで、ステージングへのコピー用でした。レプリケーションは障害時の切り替え用で、災害復旧用ではありませんでした。バックアップの確認に責任を持つ人もいませんでした。
- 1人の責任だったのですか
- GitLabはそうは捉えませんでした。事後分析は、負荷の急増、手作業で文書も乏しい副サーバーの再構築手順、黙って失敗するバックアップ、責任者の不在という原因の連鎖を挙げています。打ち間違いは最後の輪です。GitLabは今後の障害では名前を伏せるとも述べました。
- 削除コマンドを打つ前に何をすべきですか
- 今どのマシンのどのディレクトリにいるかを確かめ、あれば一覧表示だけのモードで試し、バックアップがあり、一度は復元したことを確認し、可能なら2人目に画面を見てもらいます。GitLab自身の対策リストにも、シェルのプロンプトにホストと環境を分かりやすく表示することが入っていました。