RPO、RTO、そして3-2-1
一言でいうと
バックアップ設計のあらゆる選択は、RPO(どこまで失ってよいか)とRTO(どれだけ早く戻らなければならないか)という2つの数字から決まります。
なぜ必要なのか
「バックアップの間隔をどう決めましょうか」という質問には答えがありません。「データを何分まで失ってもよいですか」に置き換えると、答えが出ます。
- RPO(Recovery Point Objective): 障害が起きた時点から、どれだけ過去のデータまで復元できるかです。つまり許容できるデータ損失量です。RPOが1時間なら、バックアップの間隔も1時間以下でなければなりません。
- RTO(Recovery Time Objective): 障害からサービス再開までに許される時間です。これはバックアップ方式だけでなく、復旧手順の全体が決めます。
2つの数字はコストに直結します。RPOを分単位に縮めるには継続的アーカイブが必要で、RTOを分単位に縮めるには待機システムが必要です。だからこの数字は、技術ではなくビジネスが決めます。 エンジニアの仕事は、その数字を受け取って実現可能な設計に落とし込むことです。
どう動くのか
3つの方式
| 方式 | 何を保存するか | バックアップ時間 | 復旧時間 | 保存容量 |
|---|---|---|---|---|
| フル(Full) | すべて | 長い | 短い(1つだけ展開すればよい) | 大きい |
| 増分(Incremental) | 前回のバックアップ以降の変更分 | 短い | 長い(フル+すべての増分を順番に) | 小さい |
| 差分(Differential) | 前回のフル以降の変更分 | 中程度 | 中程度(フル+最後の差分) | 中程度 |
増分と差分の違いは混同しやすいのですが、基準点が違います。 増分は「直前のバックアップ」が基準で、差分は「前回のフル」が基準です。そのため差分は日が経つほど大きくなりますが、復旧するときは2つあれば足ります。
実務でよくある組み合わせは週1回のフル+毎日の増分です。ただしこの組み合わせのリスクは、増分が1つ壊れると、それ以降がすべて無効になることです。そのため、世代保存ポリシーと定期的な検証がセットで付いてきます。
3-2-1ルールと現代的な補強
古典的なルールは、コピーを3つ、異なる媒体を2種類、そのうち1つはオフサイトです。
これに加えて、最近は2つが付け加わります。
- 1つはオフラインか、変更不可(immutable)にする: ランサムウェア対策です。バックアップサーバーが本番ネットワークからアクセスできると、バックアップも一緒に暗号化されてしまいます。
- エラー0: 定期的に復旧を検証して、エラーが0であることを確認します。
整合性: バックアップ中に変わるデータ
ファイルをコピーしている間にアプリケーションがそのファイルに書き込んでいると、バックアップはどの時点の状態でもない寄せ集めになります。データベースでは特に致命的です。
対処法は3つの層に分かれます。
- アプリケーションレベルのダンプ:
pg_dump -Fc、mysqldump --single-transaction。最も確実で、移植性も高いです。 - ファイルシステムのスナップショット: LVM/Btrfs/ZFS。時点を固定してから、それをバックアップします。ただしブロックレベルで時点を固定するだけなので、アプリケーションがメモリに持っていたデータは反映されません。さらにスナップショット用のボリュームが満杯になるとスナップショットが無効になるので、サイズは余裕をもって確保します。
- 継続的アーカイブ: WAL/バイナリログをアーカイブし続けて、任意の時点に復旧します。RPOを分単位に縮めなければならないときに使います。
復旧訓練が実際に明らかにすること
バックアップ戦略で検証されていない部分は、いつも復旧側です。定期的に1回、実際に復元してみると、たいてい次のうちのいくつかが出てきます。
復旧先がない。 バックアップはあるのに、それを展開するディスクもサーバーもありません。障害の最中にその資源を手配するのにかかる時間が、そのままRTOに加わります。復旧時間を本当に測るには、空のサーバーから始める必要があります。
必要なものがバックアップに入っていない。 データベースは取ったのに、アップロードファイルがない、設定ファイルがない、証明書と鍵がない、といったことです。一覧を作るときに「データ」しか考えず、サービスを再び立ち上げるのに必要なものすべてを数えなかったために起きます。
鍵がバックアップの中にある。 暗号化したバックアップの復号用の鍵を、そのバックアップと同じ場所にだけ置いても意味がありません。逆に、鍵を誰も知らない場所に置くと復旧できません。鍵はバックアップとは別の場所に、ただし2人以上が手の届く場所に置きます。
順番を知らない。 データベースを先に立ち上げるべきか、キャッシュを空にするべきか、キューに残ったメッセージをどうするかが、ドキュメントにありません。そのため復旧はできたのに、サービスが正常に戻りません。
バックアップが静かに止まっていた。 成功の通知だけを送っていると、何も届かない状態が正常に見えてしまいます。最新バックアップの経過時間を指標として測り、それにアラートを設定します。「24時間以内に成功したバックアップがなければ通知する」は、「失敗したら通知する」より強力です。
取ったものが読めるか確認したことがない。 ファイルサイズさえ合っていれば成功とみなす場合が多くあります。最低限、展開してみて、データベースなら実際に接続して1行クエリを実行します。復旧を試していないバックアップは、バックアップではなくただのファイルです。
現場での姿
バックアップが静かに壊れる5つのパターンを覚えておくとよいでしょう。
- 対象から漏れている: 新しいボリュームを追加したのに、バックアップスクリプトに入れていません。
- 成功ログだけを見て失敗に気づかない: cronは基本的に静かです。終了コードを確認して失敗を通知する仕組みが必要です。
- ランサムウェアがバックアップまで複製される: 最新のバックアップだけを残す構成は、この状況では無力です。
- 復元先の環境が違う: UID/GIDのマッピング、SELinuxコンテキスト、カーネルバージョンが違うと、復元はできてもサービスが起動しません。
- バックアップが本番の性能を損ない、バックアップウィンドウが狭まる: そのうち間隔を延ばし、ある時点からRPOを守れなくなります。
2番に対する最小限の防御線は次のとおりです。
#!/usr/bin/env bash
set -euo pipefail
trap 'echo "backup FAILED at line $LINENO" >&2; exit 1' ERR
rsync -aHAX --numeric-ids --delete /srv/data/ /backup/prod/data/
echo "backup OK $(date -Is)"
さらに、成功した時刻をファイルに記録して、「最近の成功が24時間以内か」を監視するほうが、ログを読むよりはるかに優れています。
次の確認で見ること
続くクイズでは、RPO・RTO、フル・増分・差分、オフサイトのコピーをどういう条件で選ぶかを確認します。その基準を通過したら、次のモジュールからtarのフル・増分バックアップ、rsyncの世代保存、検証スクリプトと時間を測る復旧リハーサルへと続きます。