バックアップの復元テストを自動化する:月次DR演習のスクリプト例
TL;DR
バックアップは、復元できて初めて意味があります。「取得成功」のログだけを見て安心するのは、保険証券を持っているだけで補償内容を一度も確認していないのと同じ状態です。
- バックアップジョブの成功表示は「ファイルが作られた」ことしか保証しません。中身が壊れている・必要なデータが漏れている・そもそも戻す手順を誰も知らない、というケースは珍しくないと各所のバックアップ解説で繰り返し指摘されています。
- 対策は復元テストを定期的に・自動で・記録が残る形で回すことです。人力の月次チェックリストは、忙しい月に必ず飛ばされます。
- 本記事では、cronで月次の復元テストを自動実行し、整合性チェック・所要時間の計測・Slack通知までを行うスクリプト例を示します。
なぜ「バックアップが取れている」だけでは足りないのか
バックアップの世界的な指針である3-2-1ルールは、近年「3-2-1-1-0」へと拡張されて語られることが増えています。追加された「1」は不変(イミュータブル)コピー、そして最後の「0」は復元エラーがゼロであることを検証で確認するという意味です。つまり「戻せることの確認」自体が、バックアップ設計の正式な一項目として扱われるようになっています。3-2-1の基本設計そのものは中小企業のための3-2-1バックアップ戦略にまとめていますが、その先にある「0」の部分——復元テスト——が本記事のテーマです。
「バックアップがある」ことと「復旧できる」ことは別物だと、複数のセキュリティベンダーの解説記事が共通して強調しています。取得ログが緑色でも、実際に戻そうとした瞬間に初めて問題が発覚する、という順序になりがちだからです。
よくある失敗パターン
| 失敗パターン | 何が起きるか |
|---|---|
| アーカイブ自体が壊れている | 転送中の欠損やディスクエラーで、gzipやtarの展開時にエラーになる |
| 復元先の暗号化パスワードが違う・失効している | 担当者の異動やパスワード変更が復元手順書に反映されていない |
| 復元先の空き容量が足りない | バックアップ側の容量は監視していても、復元先(本番機や検証機)の空き容量は見ていない |
| 復元手順を知っているのが1人だけ | その担当者が不在・退職した瞬間に手順が失われる(属人化) |
| 復元はできてもRTO内に終わらない | 「戻せる」ことは確認できても、「決めた時間内に戻せる」かは別に検証していない |
| バックアップ対象からデータが漏れていた | 設定ミスで一部テーブル・ディレクトリが対象外になっていたことに、復元して初めて気づく |
これらはどれも、バックアップの取得工程では検出できず、復元を実際にやってみて初めて表面化するという共通点があります。だからこそ「取れているか」ではなく「戻せるか」を定期的に確認する仕組みが要ります。
月次DR演習の設計
四半期に1回、少なくとも年1回の復元テストを推奨する解説が目立ちますが、対象システムが事業継続に直結するなら月次まで頻度を上げておくと、問題を早期に検出できます。設計するのは次の4点です。
| 項目 | 内容 |
|---|---|
| 何を | 本番相当のバックアップから、隔離したテスト環境(本番と切り離したDB・サーバー)へ実際に復元する |
| どの頻度で | 最低でも月次。重要システムはより高頻度、それ以外は四半期でも可 |
| 誰が確認するか | 実行自体は自動化し、結果(成功/失敗/所要時間)を担当者がSlack等で確認する。手順書は復元テストの実行担当者以外でも読める状態にしておく |
| 何を持って合格とするか | ①整合性チェックが通る ②復元後のデータが想定通り読める ③所要時間が目標復旧時間(RTO)以内 の3点 |
演習を「人が手作業でやる年中行事」にすると、担当者交代や繁忙期に必ず抜けます。実行そのものは自動化し、人は結果の確認と異常時の一次対応だけを行う設計にするのが現実的です。
自動化スクリプト例:cronで復元テストを回す
以下は、日次バックアップ(MySQLダンプの例)を対象に、月初1回の頻度で「隔離したテスト用DBへの実復元」までを自動化し、結果をSlackへ通知するスクリプト例です。DBの種類によって復元コマンドの構文は変わるため、PostgreSQL/MySQLそれぞれの正確なリストア手順はPostgreSQL/MySQLバックアップ&リストアガイドを、オブジェクトストレージ側にResticでバックアップしている構成はRestic + S3互換ストレージで安全バックアップを参照してください。
/usr/local/bin/restore-test.sh として保存し、実行権限を付けます。
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR=/backup
RESTORE_TEST_DIR=/tmp/restore-test
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/XXXX/XXXX/XXXX"
RTO_THRESHOLD_SEC=1800 # このシステムのRTO = 30分
notify_slack() {
local message="$1"
curl -sf -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"${message}\"}" \
"$SLACK_WEBHOOK_URL" > /dev/null
}
mkdir -p "$RESTORE_TEST_DIR"
START_TS=$(date +%s)
# 1. 直近24時間以内のバックアップファイルを取得
LATEST=$(find "$BACKUP_DIR" -name 'db_*.sql.gz' -mtime -1 | sort | tail -n 1)
if [ -z "$LATEST" ]; then
notify_slack "復元テスト失敗: 24時間以内のバックアップファイルが見つかりません"
exit 1
fi
# 2. アーカイブ自体の整合性チェック(壊れていないか)
if ! gzip -t "$LATEST"; then
notify_slack "復元テスト失敗: ${LATEST} の整合性チェックNG(破損の疑い)"
exit 1
fi
# 3. 隔離したテスト用DBへ実際に復元する
gunzip -c "$LATEST" > "$RESTORE_TEST_DIR/dump.sql"
if ! mysql -h 127.0.0.1 -u restore_test -p"$RESTORE_TEST_DB_PASSWORD" \
restore_test_db < "$RESTORE_TEST_DIR/dump.sql"; then
notify_slack "復元テスト失敗: ${LATEST} のリストア実行でエラー"
exit 1
fi
# 4. 復元後のデータが空でないかを確認(想定テーブルの件数チェック)
ROW_COUNT=$(mysql -h 127.0.0.1 -u restore_test -p"$RESTORE_TEST_DB_PASSWORD" \
-N -e "SELECT COUNT(*) FROM restore_test_db.users;")
END_TS=$(date +%s)
ELAPSED=$((END_TS - START_TS))
if [ "$ROW_COUNT" -eq 0 ]; then
notify_slack "復元テスト失敗: 復元後のusersテーブルが0件です(データ欠損の疑い)"
exit 1
fi
if [ "$ELAPSED" -gt "$RTO_THRESHOLD_SEC" ]; then
notify_slack "復元テスト完了(要注意): 所要 ${ELAPSED}秒 がRTO ${RTO_THRESHOLD_SEC}秒を超過"
else
notify_slack "復元テスト成功: ${LATEST} / 所要 ${ELAPSED}秒 / ${ROW_COUNT}件"
fi
# 後片付け(テスト用DBを空の状態に戻す)
rm -rf "$RESTORE_TEST_DIR"
mysql -h 127.0.0.1 -u restore_test -p"$RESTORE_TEST_DB_PASSWORD" \
-e "DROP DATABASE IF EXISTS restore_test_db; CREATE DATABASE restore_test_db;"
cronには月初の深夜に登録します。実行ログはファイルへも残し、Slack通知と二重化しておくと後から経緯を追えます。
0 4 1 * * /usr/local/bin/restore-test.sh >> /var/log/restore-test.log 2>&1
ポイントは、復元先を本番から隔離したテスト用DBに固定することです。誤って本番を上書きする事故を避けつつ、「実際に復元コマンドを流して、データが読める状態まで持っていく」検証だけは省略しません。定期実行の仕組み自体(cronとsystemd timerの使い分けや失敗通知の設計)は、別記事でより詳しく扱っています。
復元テストで見るべきチェック項目
自動化したスクリプトが返す結果のうち、人が確認すべきポイントは次の3つに整理できます。
| チェック項目 | 確認すること | NGだった場合 |
|---|---|---|
| 整合性 | アーカイブが展開できるか、復元後のデータにチェックサム上の欠損がないか | バックアップ取得元・転送経路を点検。同じバックアップジョブが繰り返し壊れていないか履歴を確認 |
| 所要時間 | 復元開始から完了までの実測時間 | 目標復旧時間(RTO)を超えていれば、復元先のスペックやデータ量、転送帯域を見直す |
| RTOとの照合 | 実測した所要時間が、事前に決めた目標復旧時間の範囲に収まっているか | 収まらない場合はRTOの設定自体が非現実的か、復元手順の高速化(差分復元・並列化など)が必要 |
所要時間の実測値をどう解釈し、経営層にどう説明するかはRPO/RTOを経営層に説明するで扱った目標復旧時間(RTO)・目標復旧時点(RPO)の考え方と対応させて整理すると、単なる技術チェックで終わらせずに済みます。「復元できた」だけでなく「決めた時間内に、業務が再開できる状態まで戻せた」ところまでを合格ラインに置くのが実務上の落としどころです。
まとめ
バックアップは取得できていることではなく、必要なときに、必要な時間内に、正しく戻せることがゴールです。復元テストを人力の年次イベントにすると、忙しさや担当者交代で必ず抜け落ちます。
- 復元テストは月次を目安に、自動実行できる形に落とし込む
- チェックするのは整合性・所要時間・RTOとの照合の3点
- 結果はSlack通知などで担当者以外にも見える形にし、失敗時の一次対応者を決めておく
- 「取れているか」ではなく「戻せるか」を、取得ログではなく実際の復元操作で確認する
このスクリプト例をそのまま使う必要はありませんが、「毎月決まった日に、実際に復元コマンドを流してみる」という骨格さえ自動化できていれば、本番障害の当日に初めて手順の不備に気づく、という最悪のシナリオは避けられます。