バックアップを取る際によく引き合いに出される経験則に「3-2-1ルール」がある。「3つのコピー・2種類の異なる媒体・1つは別の場所」という3つの数字の並びだが、それぞれの数字が何を守るためのものかを分解して理解している人は意外と少ない。ここでは3-2-1ルールの成り立ちと、実際のバックアップ設計にどう落とし込まれるかを整理する。
補足: 3-2-1ルールは、写真家のPeter Krogh氏が提唱したとされるデータ保全の経験則。特定の企業や規格が定めた公式基準ではなく、長年の実務から広まった目安である。
ルールの中身 — 3・2・1がそれぞれ守るもの
3-2-1ルールは次の3つの条件をまとめたものである。
| 数字 | 内容 | 守るもの |
|---|---|---|
| 3 | データのコピーを合計3つ持つ(本番データ+バックアップ2つ) | 1つのバックアップが壊れても、もう1つが残る |
| 2 | そのうち2つは異なる種類の媒体・保存先に置く | 特定の媒体・保存先固有の障害からの保護 |
| 1 | 少なくとも1つは物理的に別の場所(オフサイト)に置く | 火災・盗難・停電など「場所」単位の被害からの保護 |
重要なのは、この3つがそれぞれ別々の失敗パターンに対応しているという点である。単に「バックアップを3つ取ればいい」という話ではなく、「なぜ2種類の媒体が必要か」「なぜ1つは別の場所でなければならないか」を分けて考える必要がある。
なぜ「1つのバックアップ」では足りないのか
最も単純な疑問から始める。「本番データ+バックアップ1つ」の合計2つでは、なぜ不十分なのか。
バックアップ自体が壊れる可能性を考えると理由が見えてくる。バックアップの取得中にディスクエラーが起きる、保存先のストレージ自体が故障する、あるいは気づかないうちに破損したファイルを長期間バックアップとして持ち続けてしまう、といった事態は現実に起こる。バックアップが1つしかなければ、それが壊れていた瞬間に「本番データが壊れたときに戻せる場所がない」という状態になる。コピーが3つ(本番+バックアップ2つ)あれば、うち1つの破損は残る2つでカバーできる。
なぜ「同じ媒体」では不十分なのか
次に「2種類の異なる媒体」という条件を見る。仮にバックアップを2つ取っていても、両方とも同じ種類の保存先(例えば同一サーバー上の別ディレクトリ)に置いていた場合、その保存先固有の障害が起きるとバックアップごと巻き込まれてしまう。ストレージコントローラーの故障、ファイルシステムの破損、ホスティング事業者側の設備トラブルなどは、同一の保存先に置かれた複数のファイルへ同時に影響しうる。
媒体を分ける(例えば「サーバー上のディスク」と「手元のローカルディスク」のように、物理的に独立したハードウェア・管理主体に置く)ことで、片方の媒体で起きた障害がもう片方に伝播しにくくなる。
なぜ「同じ場所」では不十分なのか
最後の「1つは別の場所」は、媒体の種類とは別の軸の話である。仮に異なる2種類の媒体にバックアップを置いていても、両方が同じ建物・同じ拠点にあれば、火災・水害・盗難・大規模停電のような「場所」単位の被害には無力になる。地理的に離れた場所にコピーを1つ置いておくことで、その場所固有の被害からデータを守る。
実例 — このアプリのバックアップ設計と3-2-1ルールの関係
3-2-1ルールを完全に満たすには、本来はクラウドストレージや専用のオフサイトサービスまで組み合わせる必要があるが、このアプリのDBバックアップ設計は「2種類の異なる媒体」の部分を具体的な実装として体現している。
WordPress本体・プラグインの更新前に、対象サイトのデータベースをバックアップする処理では、まずリモートサーバー上の専用ディレクトリ(wpmm_backups/)に .sql ファイルを保存したのち、そのファイルを保守担当者のローカルマシン(Mac・Windows)にも転送して保存する。つまり「サーバー側のストレージ」と「保守担当者の手元のディスク」という、管理主体もハードウェアも異なる2種類の媒体にコピーを持つ設計になっている。
# maintenance_agent.py(要旨)
c.get(remote_backup_path, local_tmp_path)
# サイズ検証後に atomic rename
os.replace(local_tmp_path, local_backup_path)
self.manage_backup_generations(...) # ローカル側の世代管理
self._rotate_remote_backups(c, remote_backup_dir, site['site_name']) # リモート側の世代管理
サーバー側だけにバックアップを置く運用では、そのサーバー自体に障害が起きた場合(ディスク故障・ホスティング事業者側のトラブル・誤操作によるディレクトリ削除など)、バックアップごと失われるリスクが残る。ローカル側にも同じデータを持つことで、サーバー側の障害からは独立した状態でコピーが1つ確保される。保守担当者のマシンがサーバーとは別の場所にあることが多い点では、「1つは別の場所」の条件にも部分的に近づく設計といえる。
世代管理の「3」と混同しないための注意
ここで紛らわしい点がある。このアプリのローカル・リモート双方のバックアップ処理には、それぞれ max_keep=3 という設定があり、最新3世代のみを保持して古いものから削除していく仕組みが実装されている。
def manage_backup_generations(self, safe_site_name, display_name=None, max_keep=3):
files = sorted(glob.glob(os.path.join(site_backup_dir, '*.sql')))
while len(files) > max_keep:
oldest = files.pop(0)
os.remove(oldest)
この「3」は3-2-1ルールの「3」とはまったく別の概念である。3-2-1ルールの3は「同時に存在させるコピーの総数」の話であり、世代管理の3は「1つの保存先の中で、時系列的に何回分の履歴を残しておくか」という話である。前者は障害からの保護、後者は「1つ前の状態ではなくもっと古い状態に戻したい」というニーズへの対応であり、目的も判断基準も異なる。たまたま同じ数字が採用されているだけで、両者を混同すると設計意図を読み違えることになる。
まとめ
| 観点 | 3-2-1ルール | このアプリの世代管理 |
|---|---|---|
| 「3」の意味 | 同時に存在させるコピーの総数 | 1つの保存先に残す履歴の世代数 |
| 守る対象 | バックアップ自体の破損・媒体障害・拠点被害 | 「もっと前の状態」に戻したいという運用ニーズ |
| このアプリでの実装箇所 | サーバー側+ローカル側の二重保存(媒体分散) | manage_backup_generations / _rotate_remote_backups |
「バックアップを取っているから安心」ではなく、そのバックアップが何種類の媒体に、何ヶ所の場所に存在しているかを意識することで、単一障害点(Single Point of Failure)に依存しない保全設計に近づく。数字の語呂合わせのようなルールだが、分解してみると一つ一つが具体的な障害シナリオへの備えになっている。