運用・障害対応代表記事全11節Operate
障害から復旧し、業務を再開できるか確認する
この記事で身につくこと障害の初動、状態の保全、コードの切戻しとデータの復元の区別、別の場所への復元と基準時点との照合、戻らない変更の扱い、利用再開の判断を、一つの流れとして説明し、演習を実行できる。
記事内目次(24項目)
この記事の障害はすべて架空の設定で、演習用のデータだけを使います。障害の原因として示すスクリプトは、教えるために意図的に危険にした説明用の失敗例で、正常な保持方針ではありません。実在の障害を観測したものではありません。本文・コード・記入例はClaude Codeが作成しました。
この記事は何の話か。障害からの復旧の話です。復旧とは、壊れたデータを、業務として正しい状態に戻し、安心して使い続けられると確認してから再開することです。そのために、「何を止めるか」「どの時点に戻すか」「戻らない変更をどうするか」「いつ再開するか」を決めます。
なぜ必要か。順番を間違えると、被害が広がる、原因が分からなくなる、戻せたはずのデータが戻せなくなる、のどれかが起きます。「バックアップがあるから大丈夫」と思っていても、復元したデータが業務として正しいかは、確かめるまで分かりません。
この記事を読むとできるようになること。
- 障害が起きたとき、復旧より先に何をするか(止める・記録する・保全する)を順番どおりに言える
- 「プログラムを前の版に戻す」と「データをバックアップから戻す」の違いと、それぞれで戻るもの・戻らないものを説明できる
- 架空データで復元の演習を実行し、基準時点との照合、戻らない変更の一覧、再開の判断までを記録に残せる
読む順路。この記事は3つの深さで読めます。
- 基本だけ理解する:第1節→第3節→第4節→第5.2節の表(手順と期待結果)→第7.1節の結果の日本語説明→第7.2〜7.3節。コードを読まなくても、「コードを戻してもデータは戻らない」「復元は基準時点どおりで元どおりではない」「照合の一致は再開の承認ではない」が分かります。
- 作って確かめる:上に加えて第5.1節(ファイルと安全の約束)→第5.3節(仕組み)→第6節(対応手順の失敗例)→第9節の手順。
- 設計判断を深める:上に加えて第8節。
1. 今回の業務課題と完成するもの
この記事でやることは、架空の障害(掃除スクリプトが承認の履歴を消した)に対して、初動から利用再開までを順番どおりに行い、復元の演習を実際に実行して、記録に残すことです。
架空企業「みらい業務サービス」の依頼・承認管理アプリは利用が始まっています。ある日、運用管理者が「古い下書きを片付ける」掃除スクリプトの新しい版を実行しました。その直後、承認者から「承認した依頼を開いても、誰がいつ承認したかが表示されない」という申告が来ました。
| 運用の流れ | この記事では |
|---|---|
| 何をするのか | 止める → 伝える → 壊れた状態を保全する → 原因を切り分ける → 復旧する → 照合する → 再開を判断する |
| なぜその操作が必要か | 復旧より先に保全しないと原因調査の材料が消える。切戻しだけではデータは戻らない |
| 実行前に何を確認するか | 演習用のデータだけを使うこと。バックアップの世代を上書きしないこと。復元先が空いていること |
| 実行すると何が起きるか | 別の場所に基準時点のデータが戻る。元のファイルはそのまま残る |
| 何を観測するか | 件数・内容(全行)・関係・不変条件の照合結果、指紋、戻らない変更の一覧、終了コード |
| 正常かどうかどう判断するか | 自動照合が一致し、かつ業務担当者が目で確認してから再開。照合の一致だけでは再開しない |
| 異常ならどう戻すか | 復元は別の場所に作るので、失敗しても元と壊れた状態の両方が残る。やり直せる |
この記事を読み終えると、次のものが手元にできます。
- 障害の初動から利用再開までの手順(影響の把握、追加被害の防止、状態の保全、原因調査、復旧、結果照合、関係者連絡)
- 「コードを旧版に戻す」と「データをバックアップに戻す」の違いと、それぞれで戻るもの・戻らないもの
- 元データを壊さずに、別の場所へ復元して基準時点と照合する演習の実行記録
- バックアップ以降の変更が復元されない例を、架空の前後データで見た結果
- 障害対応記録と復旧確認表のひな形(空欄版と記入例)
2. 前提知識と読む順路
この記事で使う言葉を先に説明します。言葉を覚える必要はありません。「何のことを言っているか」が分かれば十分です。
バックアップと基準時点
一言でいうと、バックアップはある時点のデータの写しを別の場所に取っておくこと、基準時点はその写しを取った時刻です。
たとえば、依頼5件・履歴7件のときに写しを取れば、その時刻が基準時点で、写しの中身は依頼5件・履歴7件です。
これがないと、壊れたあとに戻る先がありません。また、写しを上書きしてしまうと、良い写しを壊れた写しで置き換えてしまいます。
システムでは、写しを世代ごとのフォルダ(gen-001、gen-002…)に分けて保存し、既存の世代を上書きしないようにします。
復元
一言でいうと、バックアップの写しからデータを戻すことです。
たとえば、演習では work/restore/gen-001/ という別の場所に戻し、元のファイルには触れません。
これがないと、壊れた状態のまま業務が続きます。ただし、戻るのは基準時点の内容だけで、基準時点より後の変更は戻りません。「元どおり」ではなく「基準時点どおり」です。
システム開発では、これを復元(リストア、restore)と呼びます。この記事では、元のファイルに上書きせず、別の場所に戻してから照合します。
切戻し
一言でいうと、プログラムを問題のあった版から前の版に戻すことです。
たとえば、履歴を消してしまう掃除スクリプト v1.1 を、消さない v1.0 に戻します。
これがないと、同じ操作でまた壊れます。ただし、切戻しで戻るのはプログラムだけで、消えたデータは戻りません。データを戻すのは復元です。
システム開発では、これを切戻し(ロールバック、rollback)と呼びます。「コードを戻す」と「データを戻す」は別の作業として扱います。
履歴と不変条件
一言でいうと、履歴は「誰がいつ何をしたか」の記録、不変条件は「いつ見ても守られているべき約束」です。
たとえば、承認済みの依頼には承認の履歴が1件以上あるはず、が不変条件です(記事Aの不変条件1)。
これがないと、「承認済みなのに誰が承認したか分からない」依頼が生まれても、異常だと気づけません。
システムでは、復元後にこの約束が守られているか(承認履歴のない承認済みが0件か)を照合の項目に入れます。
指紋(ハッシュ)
一言でいうと、ファイルの中身から計算した短い要約値で、中身が変わったかどうかを見分けるのに使います。
たとえば、バックアップを取ったときの指紋と、今から復元するファイルの指紋が同じなら、そのファイルは変わっていません。
これがないと、「復元したファイルが本当にバックアップと同じか」を、全部の行を目で比べる以外に確かめる方法がありません。
システム開発では、これを指紋(ハッシュ、hash)と呼びます。同じ中身なら同じ値になり、中身が変われば(ごく僅かな例外を除いて)値が変わります。短い要約値なので、ファイル全体と一対一に対応するわけではありません。この記事では SHA-256(シャー256)という計算方法を使います。詳しくは第5.3節。
読む順路:データの関係(依頼と履歴)は記事A。「承認済みなのに履歴がない」がなぜ問題かは記事Bの途中失敗の説明が該当します。構成やバックアップの方針を決める工程は 05-04、定期的な復元演習は 12-04 です。
3. 理解する:障害対応でやること、やってはいけないこと
3.1 手順の全体
障害対応は、次の流れで進めます。番号は目安の順序で、関係者への連絡は最初から最後まで並行して続けます。
- 影響の把握:何が、どの範囲で、いつから起きているか
- 初報:「問題が起きている。調査中。承認操作は控えてほしい」を関係者に伝える。原因が分からなくても出す
- 追加被害の防止:原因になりそうな処理を止める。利用者の操作を制限する
- 状態の保全:壊れた状態を、そのまま別名で保存する
- 原因の切り分け:何が起きたか。安全に緩和・復旧できる見通しが立てば、根本原因の詳しい調査は復旧と並行してよい
- 復旧:コードの切戻し、データの復元、またはその両方
- 結果の照合:復元したデータが基準時点と一致するか。基準時点より後の変更はどうなったか
- 途中経過の連絡:戻したこと、戻らなかった変更、再操作が必要な人への個別連絡
- 利用再開の判断:業務の結果が整合していることを、利用者の目でも確認してから再開する
- 再開連絡と再発防止:再開を伝え、原因に対する対策を決める
順番に意味があります。「状態の保全」を「復旧」より先にするのは、復旧で壊れた状態を上書きすると、原因調査の材料が消えるからです。連絡を最後に置かないのは、対応中に利用者が操作を続けると被害が広がり、また関係者が状況を知らないまま待たされるからです。連絡担当を決めて定期的に更新する考え方は、インシデント対応の一般的な資料でも示されています(第11節)。
原因を完全に確定するまで復旧を待つ、とは限りません。この演習では、掃除スクリプトを止めれば追加被害が止まると分かった時点で復元に進めます。「なぜ日付条件が抜けたのか」「なぜ確認せずに実行されたのか」の調査は、再発防止として復旧のあとに続けます。
3.2 よくある誤解
「コードを戻せばデータも戻る」。戻りません。掃除スクリプトを旧版に戻しても、新版が消した履歴は消えたままです。コードの切戻しは「これ以上壊さない」ためのもので、壊れたデータを直すのはデータの復元です。この記事の演習で実際に確かめます。
「バックアップから復元すれば元どおり」。バックアップは基準時点の写しです。基準時点より後に起きた変更(新しい依頼、承認)は、復元したデータには含まれません。「元どおり」ではなく「基準時点どおり」です。基準時点より後の変更をどうするかは、別に決める必要があります。
「サービスが起動したら復旧完了」。起動は、プログラムが動き始めたという意味しかありません。依頼の件数、履歴の件数、承認済みに承認履歴があるか、といった業務の結果が整合しているかは、別に確認します。
「復元は元の場所に上書きすればよい」。上書きすると、壊れた状態が消えて原因調査ができなくなり、復元に失敗したときに戻る先もなくなります。まず別の場所に復元し、照合してから、切り替えます。
「バックアップを取り直せば安心」。障害のあとにバックアップを取ると、壊れた状態の複製ができます。それで良いバックアップを上書きしてしまうと、戻る先がなくなります。バックアップは世代を分け、既存の世代を上書きしないようにします。演習ではこれをコードで強制しています。
3.3 架空の障害シナリオ
演習の設定です。
- 掃除スクリプトには v1.0(既知版)と v1.1(新版)がある。v1.0は、この演習では「削除処理を行わない」ことが分かっている版として単純化している。v1.1は「承認済み依頼の古い履歴も片付ける」機能を追加したが、日付の条件が抜けており、承認済み依頼の履歴を日付に関係なく全部消してしまう。v1.1は意図的に危険にした演習例であり、正常な保持方針ではない。
- 演習データには依頼が5件、履歴が7件ある。この状態でバックアップを取る(基準時点)。
- 基準時点のあと、業務が続く。新しい依頼 REQ-C06 が提出され、REQ-C02 が承認される(依頼6件、履歴9件)。
- 運用管理者が v1.1 を実行する。承認済み3件(REQ-C01、C02、C04)の履歴6件が消える(依頼6件、履歴3件)。
- 承認者から申告があり、対応が始まる。
REQ-C02 に注目してください。基準時点では「提出済み」、その後に承認されて「承認済み」になり、その承認履歴を v1.1 が消しました。この依頼は、あとで「どちらにも記録がない変更」として問題になります。
4. 判断すること
障害対応で人が判断することは、次の5つです。
- 何を止めるか:掃除スクリプトの実行は止める。承認・差戻しの操作も、履歴が正しく残らない間は止めるか。利用者への影響と、止めないことの危険を比べて決める。
- どの時点に戻すか:直近のバックアップ(基準時点)か。それより前か。基準時点より後の変更の量で決まる。
- 戻らない変更をどうするか:再入力してもらう、再操作してもらう、諦める、保留にする。依頼ごとに決め、関係者に伝える。
- いつ再開するか:照合が終わり、利用者側の確認が済み、原因の処理(v1.1の実行禁止)が効いている状態で。
- AIに任せる範囲:ログや差分の整理、照合スクリプトの作成、記録の下書きは任せられる。何を止めるか、どの時点に戻すか、再開するかの判断と、関係者への連絡の内容の決定は人が行う。
失敗の影響を整理します。承認済みなのに承認履歴がない依頼は、記事Aの不変条件1(承認済みには承認履歴が必ずある)を破っています。誰が承認したかを説明できないため、業務上の統制が崩れています。これが「起動しているが業務としては壊れている」状態です。
5. 作って確かめる:復元演習
5.1 準備と安全の約束
実行前に確認することは2つです。演習用のデータだけを使うこと、そして演習が触れる場所が演習領域(work で始まるフォルダ)の中だけであることです。ここでは、その約束をコードがどう守っているかを説明します。
examples/sample-c/ に5つのファイルがあります。
| ファイル | 役割 |
|---|---|
| drill.py | 演習の各手順を番号順に実行する。演習領域(work/)の中だけで動く |
| cleanup_v1_0.py | 掃除スクリプトの既知版(架空)。削除処理を行わない |
| cleanup_v1_1.py | 説明用の失敗例。日付条件が抜けており、承認済み依頼の履歴を全部消す |
| drill_safety.py | 共通の安全確認。演習領域の位置、目印の内容、最終ファイルまでの実パスとリンクを検査する |
| drill_selfcheck.py | 自己点検。一時的な演習領域で、異常ケースと保護の動作を確かめる(終了時に一時領域を削除) |
安全のために、コードは次の3つを強制します。「約束している」のではなく、違反する操作は拒否されます。演習領域(examples/sample-c の直下の work で始まるフォルダ)の外には読み書きも削除もしないこと。バックアップの既存の世代と、既存の復元物を上書きしないこと。照合で不一致があれば止まること(終了コード1)。個々の検査の内容は、次の技術詳細にあります。
技術詳細安全チェックの一覧
安全のために、コードで次を強制しています。「約束している」のではなく、違反する操作は拒否されます。
- 信頼する基点は
examples/sample-cだけです。演習領域はその直下にある、名前がworkで始まるフォルダだけで、それ以外の場所を指定すると拒否します。 setupが演習領域として使うのは、新しいフォルダ、目印のない空のフォルダ、または内容つきの目印(種別と実パス)が一致するフォルダだけです。無関係なファイルがある既存フォルダには、目印を付けず、何も変更せずに拒否します。reset(演習領域の作り直し)は--confirmを付け、かつ内容つきの目印が一致する場合しか削除しません。空の目印は無効で、削除の許可になりません。- 読取り元・書込み先・削除先は、親フォルダだけでなく最終ファイルまで実際の場所を検査します。演習領域の中にあるシンボリックリンク(別の場所を指す名前。リンク先がまだ無いものを含む)は拒否します。
- 掃除スクリプトは単独では実行できず、
drill.pyから演習領域のpractice.dbに対してだけ動きます。 - バックアップは世代フォルダ(
gen-001、gen-002…)を新しく作る。既存の世代は上書きしない。 - 復元は、復元元の指紋を先に照合し、復元先が既にあれば(リンクを含めて)拒否する。既存の復元物を消さない。
- 照合(
verify)は、不一致があれば終了コード1を返す。レポートは不一致でも書き出す。
これらは「単独で実行し、架空データと通常のローカルファイルを使う」場合の誤操作を防ぐためのものです。目印やコードを故意に書き換える人、別のプログラムによる同時の差し替え、OSのあらゆる特殊なファイル機能までを防ぐものではありません。
「元データを上書きしない」という約束の範囲も正確にしておきます。上書きしないのは復元についてです。演習の setup・after-backup・break は演習領域内の practice.db を変更しますし、reset --confirm は演習領域ごと削除します。演習の全手順が何も変えない、という意味ではありません。
5.2 手順と期待結果
手順は10段階で、コマンドを1つずつ実行します。各手順で「実行すると何が起きるか」を期待結果として先に決めておき、あとで実行結果と照合します。
各手順を実行する前に、期待結果を決めておきます。先に examples/sample-c/ へ移動してから実行します。
| 番号 | コマンド | 何をするか | 期待結果 |
|---|---|---|---|
| 0 | python3 drill.py reset --confirm | 演習領域を作り直す(初回は作るだけ) | 空の演習領域と目印ファイル。終了コード0 |
| 1 | python3 drill.py setup | 架空データを作る | 依頼5件、履歴7件 |
| 2 | python3 drill.py backup | 世代 gen-001 にバックアップを取り、バックアップ自体から基準の控えを作る | 控えに依頼5件・履歴7件の全行と指紋が残る |
| 3 | python3 drill.py after-backup | 基準時点後の業務変更 | 依頼6件、履歴9件。REQ-C02 が承認済み |
| 4 | python3 drill.py break | v1.1 を実行(障害) | 履歴が3件に減る。承認履歴のない承認済みが3件 |
| 5 | python3 drill.py freeze | 壊れた状態を別名保存 | work/incident/ に複製と指紋 |
| 6 | python3 drill.py rollback-code | v1.0 に戻して実行 | 「削除処理なし」。履歴は3件のまま(データは戻らない) |
| 7 | python3 drill.py restore | 指紋を照合し、別の場所へ復元 | work/restore/gen-001/practice_restored.db ができる。元は無傷。復元後の指紋も記録 |
| 8 | python3 drill.py verify | 基準と照合し、戻らない変更を一覧化 | 8項目すべて一致で終了コード0。REQ-C06 と REQ-C02 の変更が「戻らない」として出る |
| 9 | python3 drill_selfcheck.py | 自己点検(別の一時領域で) | 25件すべて期待どおりで終了コード0 |
5.3 復元と照合の仕組み
仕組みは5つです。
- バックアップ:ファイル全体を複製して、世代ごとのフォルダに置く。
- 基準の控え:複製の直後に、バックアップファイルそのものを読んで、依頼と履歴の全行と指紋を記録する。あとで「基準時点どおりか」を比べる相手はこの控えです。
- 指紋:ファイルの中身から計算した短い要約値。復元の前に「バックアップは控えを取ったときから変わっていないか」、復元の後に「復元したファイルは壊れていないか」を確かめるために使います(計算方法は SHA-256)。
- 復元:指紋が一致し、データベースとして読めることを確かめてから、別の場所へ複製する。復元先が既にあれば消さずに拒否する。
- 照合:復元したデータを、次の6つの観点で控えと比べる。それぞれ別の確認で、どれか1つで代用はできません。
| 確認 | 何を見るか | 見つけられること |
|---|---|---|
| 指紋 | 復元後のファイルの指紋が復元記録と一致するか | 復元後にファイルが壊れた・書き換わった |
| 可読性 | データベースとして開いて読めるか | ファイルは同じでも読めない状態 |
| 件数 | 依頼と履歴の件数が控えと一致するか | 行の消失・増加 |
| 内容 | 依頼と履歴の全行が控えと一致するか(重複行も個数ごとに比べる) | 件名・操作者・時刻・備考などの書き換え |
| 関係 | 依頼のない履歴が0件か | 依頼と履歴のつながりの破れ |
| 不変条件 | 承認履歴のない承認済み依頼が0件か | 記事Aの不変条件1の破れ |
1つでも不一致があれば、明細を書き出したうえで終了コード1(失敗)で止まります。人が画面を見なくても、自動の手順の中で失敗を止められます。
さらに、保全した障害時のファイルと復元結果を比べ、障害時のファイルにあって復元結果にないもの(基準時点より後の変更)を一覧にします。これは「復元が成功したか」とは別の判断で、「戻らなかった変更をどうするか」を人が決めるための材料です。終了コードには影響しません。
この2段階の照合が、要件REQ-008(復元後は基準時点に照らして依頼と履歴の件数・内容・関係を照合し、戻らない変更があるかを説明する)に対応します。「承認履歴のない承認済み」の確認は、REQ-005(操作の履歴を残す)とREQ-007(片方だけの完了を正常としない)が復元後も守られているかを見るものです。
技術詳細内部仕様(backup・控え・SHA-256・改訂の経緯)
バックアップは、Pythonの sqlite3 にある backup 機能でファイル全体を複製します。公式ドキュメントでは、他の接続が使用中でも複製を取れる機能として説明されています(第11節)。この演習では、複製の時点で practice.db を更新する処理は動いておらず、接続は閉じています。この前提が崩れる実稼働中のデータベースや、付随するファイルがある構成では、単純なファイルの複製では足りません。
**基準の控え(manifest)**は、複製の直後に、バックアップファイルそのものを読み取り専用で開いて作ります。元のデータベースを読んだ結果を「同じ時点のはず」として使うのではなく、実際に復元に使うファイルの中身を控えます。控えには、依頼と履歴の全行(依頼ID・依頼者・件名・状態・承認者、履歴の依頼ID・操作・操作者・時刻・備考)と、ファイルの指紋を入れます。
**指紋(ハッシュ)**は、ファイルの中身から計算した短い要約値で、変更の検出に使います。同じ中身なら同じ値になり、中身が変われば(ごく僅かな例外を除いて)値が変わります。この演習では SHA-256 を使い、次の2か所で使います。
- 復元の前:復元元(バックアップファイル)の指紋が、控えと一致するか。一致しなければ復元を中止する。
- 復元の後:復元先のファイルの指紋を記録し、照合のときに再計算して一致するか。
指紋が一致することは「ファイルが変わっていない」ことしか示しません。控えを取った時点が誤っていれば、指紋が一致しても業務要件には合いません。
復元は、指紋の照合と「データベースとして読める」ことの確認のあとで、別の場所(work/restore/gen-001/)へ複製します。復元先が既にあれば、消さずに拒否します。
**照合(verify)**では、復元したデータについて次を順に確かめます。それぞれ別の確認で、どれか1つで代用はできません。
これらに1つでも不一致があれば、レポートに明細を書き出したうえで、終了コード1で終わります。終了コードは、コマンドが呼び出した側に成功(0)か失敗(0以外)かを伝える数字です。人が画面を見なくても、自動の手順の中で失敗を止められます。初版の照合は、件数と状態しか比べず、不一致でも終了コード0を返していました。GPTのレビューで「件名や履歴の中身を書き換えても一致と出る」反例が示され、改訂版で直しました。
6. 初稿・失敗例・修正
復旧の失敗は、コードの不具合より、人の手順の順番で起きます。ここでは、順番を間違えたときに何が失われるかと、どう戻すかを5つの例で示します。
この記事の「失敗例」は、対応手順の失敗です。教えるために意図的に並べた説明用の例で、最後の1つだけは初版の演習スクリプトに実際にあった弱さです。
失敗例1:保全せずに復旧した。申告を受けてすぐ、バックアップを元の場所に上書きした。壊れた状態が消え、「何件消えたか」「基準時点より後に何があったか」を調べる材料がなくなった。→ 修正:復旧の前に、壊れた状態を別名で保存する(手順5)。復元は別の場所へ(手順7)。
失敗例2:コードを戻して完了とした。掃除スクリプトを v1.0 に戻し、「原因を取り除いたので復旧完了」と連絡した。履歴は消えたままで、承認者は依然として承認の記録を見られない。→ 修正:コードの切戻しとデータの復元は別の作業。演習の手順6で、切戻し後も履歴が3件のままであることを実際に確かめる。
失敗例3:復元して起動したので再開した。復元したファイルに切り替え、アプリが起動したので利用を再開した。基準時点より後に提出された REQ-C06 が消えていることに、依頼者の田中が翌日気づいた。→ 修正:復元後に、障害時のファイルとの差分から戻らない変更を一覧にし(手順8)、関係者に伝えてから再開する。
失敗例4:障害のあとにバックアップを取り直した。「今の状態も残しておこう」とバックアップを実行し、良いバックアップを壊れた状態で上書きした。→ 修正:バックアップは世代を分け、既存の世代を上書きしない。壊れた状態は「保全」として別の場所に置く(手順5と手順2の違い)。
失敗例5:件数が一致したので終わりにした(初版の演習にあった弱さ)。初版の照合は件数と状態だけを比べていた。件名や履歴の操作者・時刻が書き換わっていても「すべて一致」と出て、終了コードも0だった。→ 修正:全行の内容を比べ、不一致なら終了コード1にする(第5.3節)。改訂版の自己点検で、件名だけ・操作者だけ・時刻だけ・備考だけを変えた場合をそれぞれ検知することを確かめた(第7.1節)。
7. 確認結果
7.1 実行結果(実行確認済み)
ここで見るのは、手順を実際に実行したときの出力です。正常かどうかは、件数・内容・関係・不変条件の照合が「一致」で終了コードが0か、そして戻らない変更が一覧に出ているか、で判断します。
2026-09-19に、改訂版 rev2 の手順0〜9を続けて実行しました。全文は evidence/rev2-2026-09-19/sample-c/run_log.md にあります(rev1 の記録は evidence/rev1-2026-09-18/、初版の記録は evidence/sample-c/run_log.md に別に残しています)。要点を抜き出します。
要点を、手順ごとに日本語で示します。
- 手順1(setup):依頼5件・履歴7件。承認履歴のない承認済みは0件。
- 手順4(break):v1.1 が履歴を6件削除。依頼6件・履歴3件になり、承認履歴のない承認済みが3件。依頼のデータ自体は残っているので一覧画面は普通に見え、障害に気づきにくい形です。
- 手順6(rollback-code):v1.0 に戻して実行しても、履歴は3件のまま。「コードを戻せばデータも戻る」が誤りであることを、この行が示しています。
- 手順7(restore):復元元の指紋が控えと一致し、別の場所へ復元。元の
practice.dbは上書きしていない。復元後の指紋も復元元と一致。 - 手順8(verify):8項目すべて一致、終了コード0。あわせて「戻らない変更」が3件出ました(下の表)。
- 手順9(自己点検):25件すべて期待どおり。
手順8(verify)の照合表:
| 項目 | 基準 | 復元結果 | 判定 |
|---|---|---|---|
| 復元後ファイルの指紋 | 28e7e404… | 28e7e404… | 一致 |
| データベースとして読める | 読める | 読める | 一致 |
| 依頼の件数 | 5 | 5 | 一致 |
| 履歴の件数 | 7 | 7 | 一致 |
| 依頼の内容(ID・依頼者・件名・状態・承認者) | 5行 | 不一致0件 | 一致 |
| 履歴の内容(依頼ID・操作・操作者・時刻・備考) | 7行 | 不一致0件 | 一致 |
| 依頼のない履歴(関係の破れ) | 0 | 0 | 一致 |
| 承認履歴のない承認済み(不変条件) | 0 | 0 | 一致 |
終了コードは0でした。「戻らない変更」の表:
| 対象 | 障害時のファイルにある内容 | 復元結果 | 扱い |
|---|---|---|---|
| 依頼 REQ-C02 の状態 | 承認済み | 提出済み | 基準時点以降の操作を再実施するか判断 |
| 依頼 REQ-C06(会議用弁当の手配) | 存在(提出済み) | 存在しない | 再入力または再提出が必要 |
| 履歴 REQ-C06 提出(田中) | 存在 | 存在しない | 再操作で再生成するか、記録として扱う |
手順9の自己点検は、壊れたバックアップからの復元を止めるか、既存の復元物や世代を壊さないか、演習領域の外に触れないか、を一時領域で25件確かめたものです。
技術詳細実行結果の全文と自己点検25件
手順1(setup):
依頼 5 件 / 履歴 7 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 0 件手順4(break):
cleanup v1.1: 削除した履歴 6 件
依頼 6 件 / 履歴 3 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 3 件承認済み3件の履歴が消え、不変条件1が3件で破れました。依頼のデータ自体は残っているので、一覧画面は普通に見えます。障害に気づきにくい形です。
手順6(rollback-code):
cleanup v1.0: 削除処理なし(履歴 3 件はそのまま)
依頼 6 件 / 履歴 3 件 / 依頼のない履歴 0 件 / 承認履歴のない承認済み 3 件コードを戻しても、履歴は3件のままです。「コードを戻せばデータも戻る」が誤りであることを、この行が示しています。
手順7(restore):
復元元(世代 gen-001 のバックアップファイル)の指紋が控えと一致した
別の場所へ復元した(元の practice.db は上書きしていない)
復元後のファイルの指紋 28e7e4040a4fb328…(復元元と一致)手順9(自己点検):一時的な演習領域で、次の25件を確かめ、すべて期待どおりでした(2026-09-19、改訂版 rev2)。
| 確認 | 結果 |
|---|---|
| 正常な復元は8項目一致・終了コード0 | 期待どおり |
| 件名だけ/履歴の操作者だけ/時刻だけ/備考だけを書き換えた復元結果 | 4件とも「不一致あり」・終了コード1 |
| 履歴を1件削除/状態だけ書き換えた復元結果 | 2件とも「不一致あり」・終了コード1 |
| 指紋不一致のバックアップで再復元 | 中止。既存の復元物の有無と指紋は不変 |
| 復元先が既にある状態で再復元 | 拒否。既存の復元物は不変 |
| 障害後に backup を再実行 | gen-002 が増え、gen-001 のファイルと控えは不変 |
| 世代が複数あるとき、世代を省略した verify | 拒否 |
| 世代を明示した verify gen-001 | 一致・終了コード0 |
| 掃除スクリプトを単独で実行する/関数に演習領域の外のデータベースを渡す | 2件とも拒否。削除されない。内容不変 |
reset を --confirm なしで実行/目印のない領域を reset/空の目印で reset/空の目印で通常コマンド | 4件とも拒否 |
| 基点の直下でない領域/名前が work で始まらない領域で setup | 2件とも拒否。作成なし |
| 無関係なファイルがある既存フォルダで setup → reset --confirm | 両方拒否。目印は付かず、ファイルは不変 |
| practice.db が演習領域の外へのリンクのとき after-backup | 拒否。外のデータベースは不変 |
| リンク先の無い復元先リンクがある状態で restore | 拒否。外にファイルは作られず、リンクは不変 |
| v1.0 は削除処理なし | 「削除処理なし」と表示 |
7.2 自動照合では見つからない変更
REQ-C02 の承認履歴は、「戻らない変更」の表に出ていません。基準時点より後に作られ、障害で消されたため、障害時のファイルにも復元結果にも存在しないからです。表に出ているのは、REQ-C02 の状態が「承認済み→提出済み」に戻ったという事実だけです。
2つのファイルの比較だけでは、どちらにもない変更は検出できません。この演習では、承認者の鈴木の記憶と、障害時のファイルで状態が承認済みだったことから推定するしかありません。ただし、これは「必ず人の記憶に頼るしかない」という一般則ではありません。別系統の記録(承認時の通知メール、申請の受付記録、アプリとは別に取っている操作記録など)があれば、そこから回収できる可能性があります。この演習ではそれらを用意していないため、未実装の選択肢として第8.2節で扱います。
7.3 この演習で確かめたこと・確かめていないこと
| 確かめたこと | 確かめていないこと |
|---|---|
| ファイル1つのバックアップと、別の場所への復元 | 実際のサービスの停止と再開 |
| 復元元と復元後の指紋の記録と照合 | 複数のファイルやサービスにまたがる復旧 |
| 件数・内容(全行)・関係・不変条件の照合と、不一致時の終了コード | 利用者による業務の目での確認(復旧確認表の第4節は「未実施」) |
| コード切戻しではデータが戻らないこと | 本番環境での復元にかかる時間 |
| 基準時点より後の変更の一覧化 | 復元中に新しい操作が来た場合の扱い |
| 既存の復元物・バックアップ世代を失わない保護(自己点検) | 実稼働中のデータベースの複製(この演習は更新処理を止め接続を閉じた前提) |
| 演習領域の外への操作の拒否(無関係な既存フォルダ、リンク経由、掃除スクリプトの単独実行) | サポート中のPython(3.12・3.13など)での実行 |
この演習は、小さなファイル1つの復元です。実際のデータベースやサービス全体の復旧試験ではありません。自動照合の「すべて一致」は、復元がバックアップの時点どおりであることの確認であって、業務確認でも利用再開の承認でもありません。

8. 設計を判断する
8.1 復旧時間の目標と、復旧時点の目標
復旧の設計では、次の2つを先に決めます。どちらも既存の専門用語で、この教材で意味を変えてはいません。
- 復旧時点の目標(RPO、Recovery Point Objective):障害のあと、データをどの時点まで戻す必要があるか、という目標。米国NISTの用語集では「停止のあとにデータを復旧すべき時点」と定義されています(第11節)。実務では「どれだけ前の時点までなら許容できるか」という時間の幅で扱うことが多く、この教材ではそれに加えて、失う件数と業務への影響も補足します。
- 復旧時間の目標(RTO、Recovery Time Objective):業務への影響が出る前に復旧を終えるべき時間の目標。NISTの用語集では「システムの構成要素が、組織の業務に悪影響を与える前に復旧段階にとどまれる全体の時間」と定義されています。復元コマンドの実行時間だけではなく、検知から照合・確認・再開までの全体の時間です。
この演習では、バックアップ後に2件の変更(REQ-C06の提出、REQ-C02の承認)があり、どちらも復元では戻りませんでした。これは「実際に失った変更」であって、「業務が許容する目標」ではありません。この架空ケースでは、業務側の許容できる損失はまだ決まっていません(未決)。次の表は、その決定のための比較です。
| 方針 | 戻らない変更の量(条件付き) | 費用・複雑さ | 前提となる条件 |
|---|---|---|---|
| 1日1回のファイル複製(今回の演習に近い) | 取得が毎日成功し、世代が残っていれば最大1日分 | 低い。手順が単純 | 取得の成功確認、世代の保管場所、復元演習を定期的に行うこと |
| 数時間ごとの複製 | 同様の条件で最大数時間分 | 保存場所と手順の自動化が要る | 自動化した手順が失敗したときの通知 |
| 変更のたびに記録を残し、基準時点から再生する | 記録が残り、再生が検証済みなら小さくできる | 高い。記録の設計と再生手順の検証が要る | 記録の保存先が本体と同じ障害で消えないこと。再生の順序と重複の設計 |
どの方針も、条件が満たされて初めて表の値になります。「1日1回だから必ず最大1日分」「記録すれば必ずゼロ」とは言えません。この架空ケースで進めるなら、「業務側が『最大1営業日分の再操作を許容する』と合意した」という条件を明示したうえで1日1回から始め、再操作の負担が見えてから見直す、という進め方が考えられます。合意がないまま採用方針にはしません。
8.2 再処理と別系統の記録
戻らない変更を「再操作してもらう」ためには、何を再操作すべきかが分からなければなりません。演習では、障害時のファイルを保全していたから一覧を作れました。保全していなければ、依頼者・承認者の記憶だけが頼りです。
上級の設計では、操作のたびに追記だけの記録(記事Aの履歴に近いもの)を、データ本体とは別の場所にも残しておき、復元後にその記録から再処理する方法があります。第7.2節の「どちらにもない変更」も、この別系統の記録があれば回収できる可能性があります。ただし、記録の側も同じ障害で消えるなら意味がないため、保存先を分けることと、再処理の順序と重複(記事BのREQ-006)を設計する必要があります。この演習では未実装です。
8.3 コード切戻し・データ復元・再発防止の組み合わせ
| 状況 | コードの切戻し | データの復元 |
|---|---|---|
| 新版が誤ったデータを作った・消した(今回) | 必要(これ以上壊さない) | 必要 |
| 新版が動かないだけで、データは無事 | 必要 | 不要。復元すると基準時点後の正しい変更まで失う |
| コードは正しいが、操作ミスでデータが消えた | 不要 | 必要 |
| 保存先の機器が壊れた | 不要 | 必要(別の機器へ) |
「データが無事なのに復元してしまう」は、復元が損失を生む例です。復元は常に安全な操作ではありません。
再発防止は、切戻しとも復元とも別の範囲です。記事Bのトランザクションは、承認と履歴を同時に確定することで「承認済みなのに履歴がない」状態が承認処理から生まれるのを防ぎますが、そのあとに別の削除処理が履歴を消すことまでは防げません。今回の障害はまさにそれです。再発防止の検討先は、削除を伴う処理を実行できる権限の限定、履歴の保持方針(削除ではなく保管にする)、保守処理を演習データで事前に確認する手順、の3つになります。いずれも本格的な実装はこの記事の範囲外です。
8.4 適用限界
この記事の手順は、データが1つのファイルに収まり、更新処理を止めて接続を閉じられる、という条件で成り立っています。実稼働中のデータベースや、付随するファイル(添付、設定、索引)を持つ構成では、単純な複製では足りず、その保存先が提供するバックアップ機能の手順に従う必要があります。外部サービスと連携している場合は、相手側のデータは復元できず、再送や取消の設計が要ります。金銭や個人情報を扱う場合は、復旧の手順そのものに社内の承認や法令の確認が加わります。初版の題材はそれらを含まないため、この記事の範囲では扱っていません。
9. 持ち帰る成果物
| 成果物 | 空欄版 | 記入例 |
|---|---|---|
| 障害対応記録 | templates/incident_record_template.md | templates/incident_record_example.md |
| 復旧確認表 | templates/recovery_checklist_template.md | templates/recovery_checklist_example.md |
| 復元演習スクリプトと自己点検 | examples/sample-c/ | — |
| 実行記録 | evidence/rev2-2026-09-19/sample-c/run_log.md(rev1 は evidence/rev1-2026-09-18/、初版は evidence/sample-c/run_log.md) | — |
自分の環境で復元演習をするときの手順は次のとおりです。
- 演習用のデータだけを使うこと、本物のデータに触れる手順が混ざっていないことを、実行前に確かめる
- バックアップを世代を分けて取り、バックアップファイル自体から全行と指紋を控える
- 演習として変更を加え、障害を起こし、壊れた状態を別名で保存する
- 復元元の指紋を照合してから別の場所へ復元し、復元後の指紋も記録する
- 指紋・可読性・件数・内容(全行)・関係・不変条件を照合し、不一致なら止まることを確かめる
- 障害時のファイルと復元結果の差から、戻らない変更を一覧にする
- 「どちらにもない変更」を、別系統の記録と業務担当者の確認から洗い出す
- 復旧確認表を埋める。自動照合の一致、業務確認、再開承認を別々に記録し、業務確認が済むまで「再開保留」とする
10. 次工程への受渡し
次へ渡すもの:障害対応記録、復旧確認表、戻らない変更の一覧と扱いの決定、再発防止の対策一覧。
次工程での使われ方:12-04(バックアップから復元を確かめる)では、この演習を定期的に行う計画に変わります。13-01(改善要望)では、再発防止の対策(削除権限の限定、削除ではなく保管にする、実行前に演習データで件数確認をする)が改善要望として登録されます。13-02(変更の影響)では、掃除スクリプトの修正が履歴の設計に影響することをたどります。
前工程へ戻る条件:演習で「戻らない変更の再操作が業務に耐えられない」と分かったら、05-04(構成と障害・復旧の方針)に戻り、バックアップの頻度や記録の設計を見直します。「承認履歴のない承認済み」を作らない仕組みが必要なら、05-02(保存・更新のルール)と記事Bのトランザクションの設計に戻ります。
11. 根拠と更新情報
- 会社・人物・障害・掃除スクリプトはすべて架空の設定です。cleanup_v1_1.py は意図的に危険にした説明用の失敗例で、実際の障害やAI出力を観測した記録ではありません。本文・コード・記入例はClaude Codeが作成しました。
- 実行記録:evidence/rev2-2026-09-19/sample-c/run_log.md(改訂版 rev2、2026-09-19、macOS 26.6.2、Python 3.9.6、SQLite 3.51.0)。rev1 の記録は evidence/rev1-2026-09-18/、初版の記録は evidence/sample-c/run_log.md にそのまま残しています。障害対応記録の記入例の時系列は、教材のために時刻を架空に引き延ばしています。
- 改訂の来歴:2026-09-18にGPTによる別環境(Linux、Python 3.13.5)での再実行と反例確認を受け、同日に改訂版(rev1)を作成。照合の内容化と終了コード、復元・バックアップ・resetの保護、v1.0の単純化、初動手順の連絡の位置づけ、RPO/RTOの説明、再開判断の表示を修正した。2026-09-19の限定再レビューで演習範囲の保護に4経路の抜け(無関係な既存フォルダの登録、掃除スクリプトへの任意の親フォルダ指定、リンク経由の外部更新、リンク先の無い復元先リンク)が示され、rev2で共通の安全確認(drill_safety.py)を追加して閉じた。人による実読は未実施。
- Python公式ドキュメント「sqlite3 — DB-API 2.0 interface for SQLite databases」(Python 3.9版、https://docs.python.org/3.9/library/sqlite3.html 、2026-09-18確認)。参照箇所:
Connection.backup()が、他の接続が使用中でもデータベースの複製を作れること(Python 3.7で追加)。 - NIST Computer Security Resource Center 用語集「Recovery Point Objective」(https://csrc.nist.gov/glossary/term/recovery_point_objective 、2026-09-18確認)。出典は NIST SP 800-34 Rev. 1。参照箇所:停止のあとにデータを復旧すべき時点、という定義。
- NIST Computer Security Resource Center 用語集「Recovery Time Objective」(https://csrc.nist.gov/glossary/term/recovery_time_objective 、2026-09-18確認)。出典は NIST SP 800-34 Rev. 1。参照箇所:組織の業務に悪影響を与える前に復旧段階にとどまれる全体の時間、という定義。
- Google「Site Reliability Engineering」Managing Incidents(https://sre.google/sre-book/managing-incidents/ 、2026-09-18確認)。参照箇所:対応中に連絡担当が関係者へ定期的に更新を出す役割の位置づけ。
- Python Developer's Guide「Status of Python versions」(https://devguide.python.org/versions/ 、2026-09-18確認)。参照箇所:Python 3.9 の end-of-life は 2025-10-31。
- 未検証事項:実際のサービスの停止・再開、利用者による業務確認、複数ファイル・複数サービスの復旧、本番環境での復元時間、復元中の新規操作の扱い、実稼働中のデータベースの複製、サポート中のPythonでの実行。第7.3節参照。
- 2026-09-19(Phase 4D-R5):非エンジニア向けに、冒頭の結論、第1節の運用の流れ、第2節の用語の説明順序、第5.1・5.2・6・7.1節の冒頭を改善した。手順・コード・実行結果・結論は変更していない。改善後の実読による確認は未実施。
- 2026-09-19(Phase 4D-R6):第5.1・5.3・7.1節を「主本文の要約」と「技術詳細(開閉)」に分けた。安全チェックの一覧、内部仕様、実行結果の全文、自己点検25件は技術詳細の中に原文のまま残している。削除した技術内容はない。
- 2026-09-19(Phase 4D-R6後):ユーザー実読でPASS。R5で指摘された「第5〜6節以降が重い」という評価は、R6のProgressive Disclosure適用後は解消したとユーザーが判断した。手順・コード・実行結果・結論は変更していない。
