「毎日バックアップしているから大丈夫」と思っていませんか?実は復元テストをしていないバックアップは「ないも同然」かもしれません。現場で見てきた失敗事例と、正しい検証方法を解説します。
「バックアップは取っています」——その一言が、最悪の事態を招くことがある
こんな経験はありませんか?
- サーバー担当者に「バックアップは取っていますか?」と聞いたら「毎日取っています」と即答された
- 障害発生時に「バックアップから復元しようとしたら、なぜかうまくいかなかった」
- 復元できたはずなのに、データが1週間分消えていた
弊社がこれまで20年以上にわたって中小企業のサイト運用に携わってきた中で、最もヒヤリとする場面のひとつが「バックアップはあるのに復元できない」という事態です。取得しているという事実だけが一人歩きし、実際に動くかどうかは誰も確認していない——これが現場の現実です。
この記事では、バックアップが「形だけ」になってしまうメカニズムと、実際に機能することを確認するための復元テストの手順を、具体的なコマンドと事例を交えて解説します。
なぜ「バックアップが取れているのに復元できない」が起きるのか
バックアップの落とし穴は、大きく4つのパターンに分類できます。
① バックアップファイル自体が破損している
毎日 mysqldump やファイルコピーを実行していても、その結果が正常に完了しているとは限りません。ディスクの空き容量不足やネットワークエラーで途中終了していても、スクリプトのログを確認していなければ気づけません。バックアップファイルが「存在する」ことと「正常に使える」ことは別物です。
② 保存先が同一サーバー内になっている
サーバーが物理的に壊れた場合、同じサーバー上のバックアップも一緒に消えます。「バックアップフォルダに入っているから安心」というケースで、実は同じディスク上に保存していただけ、という事例は珍しくありません。
③ 復元手順が属人化している
担当エンジニアしか復元方法を知らない状態では、その人が不在のタイミングで障害が起きたときに詰みます。手順書がなく、「なんとなくやれば分かる」が通用しないのが障害発生時です。
④ 復元テストを一度もしていない
最も深刻なのがこれです。バックアップを「保存する仕組み」は整えているのに、「取り出す仕組み」を確かめていない。本番で初めて復元を試みる、という状況は、火事のときに初めて消火器を使おうとするのと同じです。
実案件で起きた「復元失敗」の話
あるクライアントから「サイトが突然表示されなくなった」と連絡を受けたのは、深夜0時を過ぎた頃でした。WordPressで構築したECサイトで、原因はホスティング会社側のディスク障害でした。
サーバー会社に問い合わせると「バックアップから復元できます」との回答。しかし実際に着手してみると、直近3日分のバックアップファイルがすべて同じ破損状態で、使えたのは4日前のデータだけでした。その間の注文データが消え、手作業で復旧するのに1日以上かかりました。
このクライアントはバックアップを「取っている」という認識でしたが、正常に復元できることを一度も確認していませんでした。この経験から弊社では、納品後の運用保守契約に「月次復元テスト」を標準で組み込むようにしています。
復元テストの具体的な手順
「では実際に何をすればいいか」を、MySQLデータベースとファイルのバックアップを例に解説します。
ステップ1:バックアップ取得スクリプトの整備
まず、バックアップの取得と完了確認をセットで行うスクリプトを用意します。
#!/bin/bash
# 設定
DB_NAME="your_database"
DB_USER="your_user"
DB_PASS="your_password"
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
# バックアップ実行
mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$FILE"
# 完了チェック:ファイルサイズが0でないか確認
if [ -s "$FILE" ]; then
echo "[OK] バックアップ完了: $FILE ($(du -h $FILE | cut -f1))"
else
echo "[ERROR] バックアップが空です: $FILE"
exit 1
fi
# 30日以上前のバックアップを削除
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
ステップ2:ステージング環境での復元テスト
バックアップから実際に復元できるかを確認するには、本番とは別のテスト環境(ステージング環境) を使います。本番で復元テストを行うのは論外ですが、まったく試さないのも問題です。
# テスト用DBに復元してみる
gunzip -c /var/backups/mysql/your_database_20250101_030000.sql.gz \
| mysql -u test_user -p test_restore_db
# テーブル数とレコード数を確認
mysql -u test_user -p test_restore_db -e "
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = 'test_restore_db'
ORDER BY table_name;
"
復元後にテーブル数・レコード件数が本番と一致しているかを確認するだけでも、バックアップの健全性を把握できます。
ステップ3:復元結果の自動通知
手動でのテストは継続できません。月次や週次のスケジュールでテストを自動化し、結果をSlackやメールで通知する仕組みを作ることで、「やり忘れ」を防げます。
#!/bin/bash
# 復元テスト結果をSlackに通知
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXXX"
RESULT=$(mysql -u test_user -p"$DB_PASS" test_restore_db \
-e "SELECT COUNT(*) FROM users;" 2>&1)
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\": \"【月次復元テスト】結果: $RESULT\"}"
よくある失敗パターンと対処法
❌ 失敗1:バックアップの保存先がローカルのみ
同一サーバー内だけにバックアップを置いている構成は危険です。S3やGoogle Cloud Storageといったオブジェクトストレージに転送する設計に切り替えましょう。
# AWS S3への転送例
aws s3 cp "$FILE" s3://your-backup-bucket/mysql/ --storage-class STANDARD_IA
❌ 失敗2:cronが止まっていることに気づかない
バックアップをcronで自動実行している場合、cronプロセス自体が止まっていたり、スクリプトがエラーで落ちていても気づかないことがあります。実行ログを定期的に確認するか、「最終バックアップからN時間以上経過したら警告」という監視を入れましょう。
❌ 失敗3:DBだけバックアップしてアップロードファイルを忘れる
WordPressやLaravelでは、DBとは別に wp-content/uploads や storage/app/public 以下のメディアファイルが存在します。DBだけ復元しても、画像が全滅という状況になりかねません。ファイルシステムのバックアップもセットで設計してください。
flowchart TD
A[バックアップ取得] --> B{ファイルサイズ正常?}
B -->|No| C[アラート通知 & 再取得]
B -->|Yes| D[外部ストレージへ転送]
D --> E[月次: ステージングへ復元]
E --> F{テーブル数・件数が一致?}
F -->|No| G[担当者へ緊急通知]
F -->|Yes| H[Slackに「復元テストOK」通知]サーバー管理、丸ごとお任せください
サーバー保守・運用
監視・障害対応・パフォーマンス改善まで、安定稼働をサポートします
※ 通常1営業日以内にご返信します
まとめと今すぐできる次のステップ
「毎日バックアップを取っている」は、運用の出発点に過ぎません。バックアップが本当に機能するかを確かめるのは、復元テストを実施してからです。サーバー障害やランサムウェア被害が起きてから初めて「使えない」と分かるのでは、取り返しがつきません。
弊社では、LaravelやWordPressサイトの運用保守において、外部ストレージへのバックアップ転送・月次復元テスト・Slackへの自動通知をセットにした仕組みを標準導入しています。「今の運用で本当に大丈夫か不安」という方は、ぜひ一度ご相談ください。現状の確認だけでも対応しています。