バックアップの成否は復旧できたかどうかだけで決まる
バックアップは取得した時点では何の価値も生まない。価値が生まれるのは、失ったデータを実際に書き戻せたときだけだ。それにもかかわらず、小規模な運用で壊れているのは取得側より復旧側であることが多い。cronは毎晩動いているのにダンプの中身が0バイトだった、暗号化パスフレーズをそのサーバーにしか置いていなかった、圧縮ファイルは残っているが戻す手順を誰も知らない。いずれも障害が起きて初めて発覚する。
設計は四つの問いに順番に答えると決まる。何を保存するか、どこに置くか、どの頻度で何世代残すか、そして復旧できることをどう確認するか。以下はこの順で進める。
保存対象は再生成できないものに絞る
対象を選ぶ基準は一つで、失ったときに作り直せるかどうかだ。作り直せるものを含めるとバックアップは肥大し、転送時間と保管費用が増え、結果として頻度が落ちる。
| 種類 | 扱い | 理由 |
|---|---|---|
| データベースの中身 | 必須 | 失うと復元不能 |
| ユーザーがアップロードしたファイル | 必須 | 同上。画像・添付・生成物 |
| 環境変数ファイル・APIキー・秘密鍵 | 必須 | Gitに入れていないため、ここにしか存在しない |
| ミドルウェアの設定(/etc配下) | 推奨 | 再構築の所要時間を大きく縮める |
| アプリのコード | 不要 | Gitのリモートが実質的なバックアップ |
| OSとパッケージ | 不要 | 再インストールと構築手順で戻せる |
| ログ | 原則不要 | 監査要件があるときだけ別枠で保管 |
データベースだけは注意が必要になる。稼働中のMySQLやPostgreSQLのデータディレクトリをそのままコピーしても、書き込み途中の状態を掴むため復元できないファイルになりやすい。専用のダンプコマンドを使う。MySQL系なら mysqldump --single-transaction がInnoDBの整合性を保ったまま取得でき、PostgreSQLなら pg_dump が同じ役割を果たす。
秘密鍵や環境変数は見落としやすい。コードはGitにあり、設定は再現できても、認証情報だけはそのサーバーの中にしかないことが多い。バックアップに含めると同時に、パスワードマネージャなど別の場所にも控えを置く。
保管先は障害の単位で分ける
保管先を考える枠組みとして3-2-1がある。データを3つ持ち、2種類の媒体に置き、1つは物理的に離れた場所に置く、という原則だ。小規模な構成では次のように読み替えられる。
1つ目はサーバー内のローカル領域。取得も復元も速く、ファイルを1つ消しただけといった軽い事故に強い。ただしディスク障害やサーバー削除では同時に消えるため、これ単独はバックアップとして数えない。2つ目がオブジェクトストレージなど外部サービスで、これが実質的な本命になる。3つ目として手元のPCやNASに月次で落としておくと、クラウド側のアカウント事故まで守れる。
意識すべきなのは、どの障害までを守る対象にするかだ。ディスク故障だけを想定するなら同一事業者の別ストレージで足りる。誤操作によるアカウント停止や支払い遅延による凍結まで想定するなら、保管先は事業者ごと分ける必要がある。誤削除やランサムウェアを想定するなら、バックアップ用の認証情報をサーバー本体の管理者権限と分離し、可能であれば削除権限を持たせない。サーバーが乗っ取られたとき、そのサーバーから消せるバックアップは一緒に消える。
外部に置く以上、アップロード前の暗号化は前提になる。後述するresticのようにクライアント側で暗号化するツールを使えば、保管先の事業者にも中身は読めない。
頻度は許容できる損失時間から、世代は気づくまでの時間から決める
頻度を決める値はRPO(目標復旧時点)、つまり最大でどれだけの時間ぶんのデータを失ってよいかだ。日次バックアップは最大24時間ぶんの損失を受け入れる設計を意味する。個人ブログなら問題ないが、受注や決済を扱うなら24時間ぶんの取引消失は許容できない。この場合はデータベースだけ頻度を上げ、容量の大きいアップロードファイルは日次のままにする、という分け方が現実的になる。すべてを同じ頻度で取る必要はない。
世代管理が必要な理由は別にある。データ破損や誤削除、改ざんは、発生から気づくまでに時間差があるためだ。最新の1本だけを上書きし続ける運用では、壊れた状態のデータで正常なバックアップを潰してしまう。日次7世代、週次4世代、月次6世代といった段階的な保持であれば、1か月前の状態にも戻れて容量も抑えられる。
小規模で現実的な構成例
ファイル単位のバックアップにはresticが扱いやすい。重複排除と暗号化と世代管理を備え、S3互換のオブジェクトストレージを保管先にできる。1日1回、次のような処理をcronまたはsystemdタイマーで実行する。
#!/bin/bash
set -euo pipefail
# 1. データベースを整合性を保ったままダンプ
mysqldump --single-transaction --routines --events \
appdb > /var/backups/appdb.sql
# 2. ダンプと実データをまとめて外部へ
restic backup /var/backups /var/www/uploads /etc/nginx /etc/letsencrypt
# 3. 世代を整理して不要なデータを削除
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --pruneset -euo pipefail を付ける理由は、ダンプが失敗したときにその場で停止させるためだ。これがないと、失敗して空になったファイルをそのままバックアップし、正常な世代を押し出してしまう。
resticのリポジトリパスワードは、そのサーバーの外に必ず控える。これを失うとバックアップは復元できない暗号文になる。rcloneでオブジェクトストレージへ同期する構成や、rsyncでNASへ転送する構成でも考え方は変わらない。ツールより、ダンプの整合性・暗号化・世代整理・失敗時の停止という四点が満たされているかが重要になる。
事業者のスナップショットは補完であって代替ではない
多くのVPSやクラウドにはディスク全体のスナップショット機能がある。サーバーごと戻せるため復旧が速く、OSの更新やミドルウェアの設定変更の前に取っておくと切り戻しが容易になる。
一方で、これだけに頼るのは避ける。保存先が同じ事業者のアカウント内にあるため、アカウントに起因する事故には無力だ。取得単位がディスク全体なので、ファイル1つだけを戻す用途には向かない。保持世代数の上限や課金体系も事業者ごとに異なり、条件は変更されるため契約前に公式サイトで確認する。スナップショットはサーバー単位の切り戻し、ファイル単位のバックアップはデータの長期保全と、役割を分けて併用する。
復旧できることを確認する手順
ここが設計の中核になる。取得の自動化に比べて後回しにされやすいが、確認していないバックアップは、あると信じている状態にすぎない。四半期に一度、または構成を変更したときに次を実施する。
第一に、保管データ自体の整合性を検査する。resticであれば restic check で構造を確認でき、restic check --read-data-subset=1/10 のように実データの一部を読み出す指定を加えると、保管先での破損にも気づける。
第二に、本番とは別の場所へ実際に復元する。使い捨てのVPSやローカルのコンテナで十分だ。restic restore latest --target /tmp/restore のように書き戻し、ファイル数とサイズが想定どおりか確認する。本番サーバーの上で復元操作を試すのは避ける。
第三に、データベースを空の状態から復元し、中身を検証する。ダンプを新しいデータベースに読み込み、主要テーブルの件数と最新レコードの日時を確認する。この確認をしないと、テーブル定義だけが入っていてデータが空、という状態を見逃す。
第四に、所要時間を計測する。ダウンロード、展開、データベース投入、動作確認までにかかった実時間が、実際のRTO(目標復旧時間)になる。数時間かかると判明したなら、その事実をもとに構成を見直すか、その時間を許容すると決める。
第五に、手順を文書に残す。復旧時に必要なのは、保管先の場所、認証情報の在り処、実行するコマンドの順序、そして完了を判断する条件だ。障害対応中に思い出しながら進める前提を捨てる。
そして、失敗に気づける仕組みを別に用意する。成功メールは読み飛ばされるようになるため、監視すべきは失敗と無通知のほうだ。実行結果を外形監視サービスへ通知し、一定時間通知が届かなければ警告が出る構成にすると、cronが止まっている状態にも気づける。最新バックアップのサイズが前回から極端に減っていないかの確認も、簡単な割に効果が高い。
次に着手する順序
まだ何も組んでいないなら、完成形を目指す前に順序を守るほうが早い。最初にデータベースのダンプと再生成できないファイルを列挙し、日次でオフサイトへ暗号化して送る経路を1本だけ作る。次に世代管理を7日・4週・6か月の三段で設定し、失敗を検知する通知を足す。ここまでで、失うデータは最大1日ぶんに抑えられる。
その状態を作ったうえで、必ず一度は別の場所へ復元する。所要時間を測り、手順を書き、認証情報の控えを本番の外に置く。この一巡が終わって初めて、そのバックアップは動くと言える。頻度や保管先の追加を検討するのは、その後でよい。