
本当に復元できるVPSをバックアップ
サーバーを運用している人のほぼ全員が、自分では「バックアップ」と呼んでいる何かを持っています。しかし実際に復元したことがある人はずっと少なく、この二つの事実の間にある距離こそが、データが本当に失われる場所です。本ガイドが扱うのは、サーバーを本当に破壊する三つの要因――自分自身の手、侵害、そして制御を失ったアカウント――を生き延びるコピーを、匿名のマシンに検証済みの身元をひそかに結びつけることなく作る方法です。
私たちはこのネットワークを動かすすべてのバイトについて三つのコピーを保持しており、緊急時に必要になった回数よりも多く、なんでもない火曜日にわざと復元を行っています。これは律儀さのための律儀さではありません。リポジトリの中身が、安心できる名前のついた暗号化ノイズのフォルダではなく、本物のバックアップであると知るための唯一の方法だからです。
以下のアドバイスは、意図的に流行から外れています。ダッシュボードや月額課金、サポート窓口を備えたバックアップ製品は登場しません。そうしたものはすべてアカウントであり、アカウントとは名前、カード、そして法域だからです――このサーバーから遠ざけるために、あなたが本気で努力してきたかもしれない三つのものです。代わりに登場するのは、二台目のマシン、およそ40行のシェルスクリプト、そしてほとんど誰も守らない一つの習慣です。
この作業は、一晩あれば十分にこなせるはずです。使用するツールはDebian 13とUbuntu 24.04を前提としていますが、置き換えるべき箇所さえ分かればRHEL系にもそのまま応用できます。サーバーが新しい場合は、このページより先に最初の1時間の堅牢化を済ませてください――すでに他人のものになったマシンをバックアップするのは、その他人の仕事を保全する行為でしかありません。
レンタルサーバーで実際にデータを破壊するもの
誰に尋ねても、バックアップする理由はたいてい「ディスクが壊れたときのため」と返ってきます。現代のVPSにおいて、それはあなたの身に起こる可能性が最も低いことの一つです。あなたのボリュームは冗長化されたアレイ上のNVMeに置かれており、ハイパーバイザーは、あなたが気づく前に障害の出たホストからマイグレーションを済ませてしまいます。ドライブ障害はプロバイダー側の問題であり、しかもすでに解決済みの問題です。それを中心にバックアップ戦略のすべてを組み立てるのは、洪水になる家のために消火器を買うようなものです。
実際に人々のデータを奪っているものを、おおよその頻度順に挙げます。まず、圧倒的多数を占めるのがあなた自身です。空文字列に展開された変数を伴うrm -rf。二つのタブが見分けられなかったせいで間違った端末で実行されたDROP DATABASE。二回実行されたマイグレーションスクリプト。-vが指癖になっていたdocker compose down -v。これらは特殊な事例ではありません。ただの火曜日です。
二番目は、アプリケーション自身が自分のデータを削除するケースです。破壊的なマイグレーションを実行して途中で失敗するアップグレード。キャッシュではなかったことが判明したディレクトリをクリアしてしまうプラグイン。誤ったパスに対して設定されたログローテーション。三番目は侵害です――そしてこれは、攻撃者がバックアップの消去を望んでいるケースであり、それが以下で立ち返ることになる設計上の変化をもたらします。四番目は、no-KYCホストでは忘れがちなアカウントの喪失です。不在の間にゼロになった残高、書き留めなかったパスワードマネージャーのエントリー、放棄したメールアドレス。私たちは残高がゼロになってから数時間以内に停止し、7日後には破棄します。これは意図的に容赦のない仕様であり、あなたが誰であるかを尋ねずに済ませるための算術です。あなたの身元を調べて例外を作ってくれるサポート担当者はいません。そもそも調べるべき身元が存在しないからです。
この最後のカテゴリーこそが、プライバシー最優先のホスティングと主流派のホスティングを分けるものであり、あなたの計画を形作るべきものです。このサーバーをあなたに結びつけにくくする制御はすべて、私たちを含めた誰にとっても、それをあなたに返しにくくします。バックアップこそが、その取引をリスクから選択肢へと変えてくれるものなのです。
なぜスナップショットはバックアップではないのか
スナップショットは素晴らしいものであり、ぜひ使うべきです。カーネルのアップグレードの前、データベースのマイグレーションの前、取り消せるようにしておきたい何かを行う前には、必ず一つ取っておいてください。数秒で復元でき、コストはほとんどかからず、年に十数回はあなたの午後を救ってくれます。
それでも、三つの構造的な理由により、スナップショットはバックアップではありません。これはプロバイダーがどれだけマーケティングをしても変わりません。まず、同じアカウントの内側に存在します。パネルにログインできる者なら誰でも削除できますし、アカウントが機能しなくなれば、スナップショットもそれとともに消滅します。次に、同じ障害ドメインに存在します。同じプロバイダー、同じコントロールプレーン、しばしば同じストレージクラスターであり、一つの悪い出来事がオリジナルとコピーの両方を道連れにしうるということです。そして、不透明です。稼働中のマシンのスナップショットは、書き込み途中のデータベースを、それ以外のすべてとまったく同じように忠実に捉えてしまいます。したがって、稼働中のMySQLサーバーのスナップショット復元が与えてくれるのは、クリーンなデータベースではなくクラッシュリカバリーのシナリオです。
昔ながらの3-2-1ルール――三つのコピーを、二種類のメディアに、うち一つはオフサイトに――は、真ん中の条項がレンタルインフラ上では意味をなさないことに気づかないまま唱えられがちです。あなたには二種類のメディアなど存在しません。あるのは仮想ブロックデバイスと、もう一つの仮想ブロックデバイスであり、どちらも誰かのSANです。VPSへの翻訳を生き延びる部分は、もともと重要だった部分です。すなわち少なくとも一つのコピーは、一つの悪い出来事が届かない場所になければならないということです。別のプロバイダー、少なくとも別のリージョン、別の認証情報、別のアカウント――そして、あなたがオフショアでホスティングする理由が技術的なものではなく法的なものであるなら、別の法域も必要です。そうすれば、一通の裁判所命令が両方のコピーに同時に届くことはありません。
保存先もまた、あなたの脅威モデルの一部である
この節は、他のバックアップチュートリアルには存在しない節であり、このサーバーの代金をMoneroで支払ったのであれば、最も重要な節でもあります。
インターネット上のどこでも見かけるデフォルトのアドバイスは、バックアップをオブジェクトストレージにpushすることです。Backblaze B2、Amazon S3、Wasabi、rclone経由のGoogle Driveなどです。安価で、耐久性があり、実際に機能します。しかしそれは同時に、たった一つのコマンドで、あなたがこれまでやってきたことの多くを台無しにします。そのアカウントを開設するにはカードと、たいていは身分証明書が必要だったはずです。最初のスナップショットがアップロードされた瞬間から、そのプロバイダーはあなたの名前と支払い情報をあなたのサーバーのIPアドレスに結びつけたタイムスタンプ付きの記録を、毎時間更新しながら、永遠に保持し続けます。あなたは私たちに対して身元を確認したのではありません。あなたは彼らに対して身元を確認し、その二つの間に線を引いてしまったのです。
後半はさらに悪く、しかも人々が見落としがちな部分です。そのアップロードを認可するAPIキーは、本番サーバー上のただのファイルです。そのマシンでrootを奪った者――あるいは合法的にディスクを入手した者――は、あなたのデータを手に入れるだけでは済みません。彼らは、たった一回のAPI呼び出しで課金上の身元にたどり着ける認証情報を手に入れることになります。匿名だったはずのサーバーが、あなたの銀行口座を指し示す道標に変わってしまうのです。
だからといってオブジェクトストレージが間違っているわけではありません。それをデフォルトではなく、一つの意思決定にするというだけのことです。もともとあなたが持っていたものをどれだけ保てるか、その順に並べた三つの逃げ道があります。
- 二台目のno-KYC VPSを、できれば本番とは別の法域に用意し、同じ暗号資産残高から支払います。保存先は元のサーバーが持つ匿名性を打ち消すのではなく、そのまま引き継ぐことになります。これが私たちの実践方法であり、このガイドの残り部分もこれを前提としています。
- 身元確認なしで暗号資産を受け付けるストレージプロバイダー。実在はしますが、規模は小さめです。マーケティングの文句を鵜呑みにする前に、支払い経路が本当に身元不要かどうかを確認し、復元のときになって初めて知ることがないよう、事前にエグレス料金を確認してください。
- 物理的に所有しているマシンで、自宅の回線の裏からWireGuard経由でpullします。匿名性の面では申し分ありませんが、可用性は低く、復元速度が自宅のアップロード速度に縛られることを意味します。三つ目のコピーとしては妥当ですが、唯一のコピーとしては心もとないものです。
どれを選ぶにせよ、バックアップを保持するアカウントは、本番環境を保持するアカウントと認証情報、メールアドレス、リカバリー経路を共有すべきではありません。肝心なのは、一つのログインが侵害されても両方には届かないようにすることです。これは他の場所で身元を分離しておくのと同じ規律を、システムの中で最も地味な片隅に適用しただけのことです。
push、pull、そしてランサムウェアがあなたのバックアップを見つけ出す理由
ほとんどすべてのバックアップチュートリアルは、同じアーキテクチャに行き着きます。本番サーバー上のcronジョブが、リモートのリポジトリに対して認証し、そこへ書き込むというものです。単純で、実際に機能しますが、誰も指摘しない性質を一つ抱えています――本番サーバーがリポジトリ全体を削除できる認証情報を保持しているという点です。古いスナップショットのプルーニングには削除権限が必要なので、あなたの夜間ジョブを実行する鍵は、金庫を空にする鍵でもあるのです。
これが侵害の最中に何を意味するか考えてみてください。マシンでrootを奪った攻撃者は、あなたのバックアップを探し出す必要すらありません。あなたが親切にも、動作する認証情報とリポジトリ名を記したコンフィグファイルを残しておいてくれたからです。何か目に見える行動を起こす前にバックアップを列挙して破壊することは、仮説上の洗練された手口ではありません。標準的な手順です。なぜならそれが、インシデントを交渉の場に変えるものだからです。あなたの夜間ジョブは、いわば偵察だったのです。
設計には三種類あり、その違いはひとえに、どのマシンがどの鍵を保持しているかにかかっています。
単純なpushが、先ほどのものです。本番サーバーが読み書き削除の権限をすべて持っています。便利ではありますが、まさにバックアップが最も必要になる場面で完全に破綻します。あなたが本当に気にかけている脅威が自分自身の指先だけである場合に限って使ってください。
append-onlyなpushは同じ形を保ちつつ、危険な動詞だけを取り除きます。リポジトリは、プロトコルを理解し削除を拒否する仕組みによって提供されます。resticならrest-server --append-only、Borgならauthorized_keysから強制したborg serve --append-onlyです。本番サーバーは新しいスナップショットを作成できますが、古いものを削除することはできません。プルーニングは後で、別の場所から、別の鍵を使って行われます。これはわずか20分ほどの作業で得られる大きな改善であり、たいていの人にとってはここで止めておくのが適切です。
pullは接続の向きを逆転させます。バックアップホストがSSH経由で本番環境に手を伸ばし、必要なものをコピーして、自分自身のディスク上でローカルにバックアップツールを実行します。本番サーバーはバックアップ用の認証情報を一切保持しません――そのマシンには見つけ出せるものが何もないのです。自分のバックアップがどこにあるのかを、そのマシン自身が知らないからです。これが最も強力な構成であり、以下のステップバイステップで組み立てていくものです。
pullの代償についても正直になっておきましょう。タダではないからです。鍵を消し去ったのではなく、移動させただけです。バックアップホストが今度は本番環境へのSSH鍵を保持することになるため、バックアップホストの侵害は稼働中のシステムにまで及びます。それでもこちらの方が良い取引です――バックアップホストは何も稼働させず、SSH以外は何も公開しておらず、公開Webサーバーよりもはるかに小さな標的だからです。とはいえこれも取引であることに変わりはなく、その鍵をファイルの読み取り以外には使えない読み取り専用コマンドに強制することで、残ったギャップの大部分を塞ぐことができます。
ツールを選ぶ――restic、Borg、それとも素のrsyncか
三つのツールで、事実上あらゆるケースをカバーできます。フォーラムのスレッドが示唆するほど、悩ましい選択ではありません。
resticはデフォルトで暗号化を行い、スナップショット間で重複排除を行い、SFTP、S3、RESTをはじめ十数種類のバックエンドを話し、どこにでも置ける単一の静的バイナリとして配布されます。リポジトリ形式はコンテンツアドレス方式なので、スナップショット作成は安上がりであり、何台のマシンから送られてきても同一データは一度しか保存されません。コストは実在するものの控えめです。リポジトリのインデックスに比例したメモリを要求しますし、中断された実行は古いロックを残すことがあり、次の実行はrestic unlockで解除するまでそれを乗り越えようとしません。これがデフォルトの推奨であり、以下のステップで使用するものです。
Borgは重複排除と圧縮の性能がより高く、数百万個の小さなファイルを含むリポジトリでは体感できるほど高速です。その代償は結合度の高さです。Borgは両端にインストールしなければならず、バージョンを密接に一致させる必要があり、一つのリポジトリは本来一つのクライアント向けに設計されており、ヘルパー層なしではオブジェクトストレージバックエンドをネイティブには持ちません。バックアップ元が大きなメールスプールや小さなファイルだらけのファイルシステムで、かつ両方のマシンを自分で管理しているなら、Borgは使用容量を半分にしてくれるでしょう。その--append-onlyモードも、両ツールの中でこの発想を最もクリーンに実装したものです。
rsyncはバックアップツールではなく、そうでないふりをすると、壊れたディレクトリを忠実にミラーリングしたコピーが一つできあがるだけです。バージョニングも、重複排除も、保存時の暗号化もありません。rsync --deleteは、あなたの間違いを回線速度でコピー先にも伝播させます。それでもなお、自分が管理する二台のマシンの間でバイト列を移動させる用途には正しいツールであり、それこそがpull構成の中でrsyncが担う仕事です。バイト列が到着した後のバージョニングと暗号化はresticが担当します。それぞれを、それぞれの本分どおりに使ってください。
一つだけやってはいけないことがあります。tarと日付入りファイル名で自前の仕組みを組み立てることです。それは四か月ほどはうまくいきますが、やがて何も期限切れにならないせいでディスクが満杯になる日か、40GBのデータセットを毎晩フルコピーすると月1.2TBものストレージ代を、ほぼ同一のものを30個保持するために払い続けていたと気づく日がやってきます。
何を含めるか――そして、あなたを裏切るデータベース
本能的には、ファイルシステム全体をバックアップしたくなります。それに逆らってください。ルートファイルシステムの大部分は、90秒で再インストールできるディストリビューションのパッケージであり、それらを含めることはストレージと転送時間、そしてさらに悪いことに注意力を消費します。誰もテストしたがらない40GBのバックアップは、四半期ごとに復元される900MBのバックアップよりも役に立ちません。
本当に再構築できないものは、実はそう多くありません。/etc(あなたの設定一式であり、再構築が週末仕事ではなく1時間仕事で済む理由です)、/homeと/root、/srvと/var/www、/var/lib/や/opt/配下のアプリケーション状態、名前付きのDockerボリューム、あなたが書いたcronとsystemdのunit、そしてデータベースのダンプです。/proc、/sys、/dev、/run、/tmp、/var/cache、スワップファイル、ソケット、そして/var/lib/docker/overlay2は飛ばしてください――最後のものはcomposeファイルから再構築でき、しかもディスク上で単独最大のディレクトリになっていることがよくあります。
ここからが、静かに復元を台無しにする部分です。データベースを稼働させたままデータディレクトリをコピーすると、まず使い物にならないファイル一式ができあがります。エンジンは状態をメモリ上に保持し、順序が重要な形で複数のファイルにまたがって書き込みを行いますが、あなたのコピーはそのツリーを数分かけて歩き回り、異なる瞬間に異なるファイルを捉えてしまいます。InnoDBはそこからクラッシュリカバリーできることもあれば、できないこともあります。その失敗は何か月も経ってから、すでに散々な一日になっている最中の復元作業で表面化します。
代わりにエンジンを通してダンプし、そのダンプをバックアップしてください。
# MariaDB / MySQL — InnoDB gets a consistent snapshot from one transaction
mariadb-dump --single-transaction --quick --routines --triggers --events \
--all-databases | zstd -T0 > /var/backups/db/all-$(date -u +%F).sql.zst
# PostgreSQL — pg_dump is consistent by design; grab roles separately
pg_dumpall --globals-only > /var/backups/db/globals.sql
pg_dump -Fc -Z6 mydb > /var/backups/db/mydb.dump
# SQLite — never cp a live database; WAL mode makes that a torn file
sqlite3 /var/lib/app/app.db ".backup /var/backups/db/app.db"持っておく価値のある補足が二つあります。--single-transactionが保証する整合性はInnoDBに対してのみです――もしまだMyISAMのテーブルが残っていれば、それはトランザクションの外でコピーされ、残りの部分と整合しない可能性があります。そうしたテーブルは変換するかロックしてください。そしてダンプを取るようになったら、稼働中のデータディレクトリはファイルバックアップの対象から除外してください。両方をバックアップするということは、正しいコピーの隣に大きな破れたコピーを保存することになり、将来の復元に、片方がひそかに間違っている二つの候補を与えることになります。
コンテナであっても原則は変わらず、変わるのはパスだけです。docker compose exec -T db mariadb-dump …でダンプを実行し、その下にあるボリュームではなく、結果として得られるファイルをバックアップしてください。
暗号化キーはどこに置くべきか
クライアントサイド暗号化こそが、データの出所とは異なるマシンにそれを保存してもよい、唯一の理由です。resticとBorgはどちらも、何かが送信元を離れる前に暗号化を行うため、バックアップホストは暗号文だけを保持することになり、信頼できないストレージとして扱うことができます。この性質の価値は、あなたが鍵をどう扱うかとまったく同じだけの価値しかなく、それ以上ではありません。
この失敗パターンは、うんざりするほどよくあります。リポジトリのパスワードは本番サーバー上の/etc/restic/envに置かれており、それ自体は自動化のために問題なく、むしろ必要なことです。そして、失うのが本番環境そのものになったとき――破壊され、押収され、あるいはアカウントごと消えたとき――あなたは数百ギガバイトの暗号化されたブロックを前にして、鍵の唯一のコピーが、まさに失うことから自分を守ろうとしていたそのものの中にあったのだという事実に、じわじわと気づくことになります。
つまり、サーバー上のパスワードは作業用のコピーであって、記録の正本ではありません。正本は、サーバーが失われても残る場所に置いてください――それ自体が別の場所にバックアップされているパスワードマネージャーの保管庫、あるいは引き出しの中の紙、あるいはその両方です。リポジトリの場所と、その隣に正確な復元コマンドを書き留めておいてください。文脈のないパスフレーズは、18か月後にストレスの中で解くことになるパズルだからです。そして5分だけ時間を取って検証してください。本番とは別のマシンから、パスワードマネージャーの中身だけを使って、そのリポジトリに対してrestic snapshotsを実行するのです。それがうまくいけば、あなたにはバックアップがあります。本番にしか存在しない何かが必要になるなら、あなたが持っているのはとても高価なフォルダにすぎません。
どちらのツールも、一つのリポジトリに複数の鍵を持たせることをサポートしており(restic key add)、これはプルーニングジョブや二人目の管理者に、元のパスフレーズを共有せずにアクセス権を与えるためのきれいな方法です。また、本番のディスクがLUKSで暗号化されている場合は、二つの秘密を本当の意味で分離しておいてください――resticのパスフレーズをLUKSボリュームの中に、LUKSのパスフレーズをresticリポジトリの中に保存してしまうと、両側とも締め出されて開かなくなるループになります。
保持期間――1週間しか残さないという罠
保持期間は、一見ストレージコストの問題に見えますが、実際には検知までの遅延の問題です。重要な数字は、どれだけディスクを費やしたいかではなく、あなたのシステムの中で問題がどれだけ長く気づかれずにいられるかです。その期間がどれほどであれ、あなたの最も古いバックアップは、それより古くなければなりません。
7日分の日次スナップショットは、一見十分なようでいて、実際には心もとないものです。誰もクエリしない壊れたテーブル、誤動作するcronジョブによる緩慢な削除、何か目に見えることをする前に1か月静かに潜んでいた侵入――これらはどれも、表面化するまでに1週間より長くかかるのが常であり、あなたの履歴が7日分しかなければ、保持しているスナップショットはすでにすべて汚染されていることになります。実際の侵入における滞留時間は、日常的に数週間単位で計測されます。安価な月次スナップショットは、まさにそれに対する防御であり、重複排除のおかげで実に安上がりです。1年分保持する月次スナップショットが追加で消費する容量は、フルコピー1個分のごくわずかな割合にすぎません。実際に変化したブロックだけが二重に保存されるからです。
restic forget \
--keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 2 \
--pruneこの階段状の構成――1週間分の日次、1か月分の週次、1年分の月次、数年分の年次――は、スナップショットにしておよそ26個であり、典型的なデータであればフルコピー1個分の2倍を大きく下回ります。特別な理由がない限り、これをデフォルトとして推奨します。
運用上の注意点が二つあります。--pruneを伴わないforgetはラベルを削除するだけなので、プルーニングするまで容量は解放されません――これは、いつまでも縮まらないディスクを眺めている人を驚かせます。そして--pruneにはリポジトリへの削除権限が必要なので、append-onlyやpullの構成では本番サーバー上では実行されません。実行されるのはバックアップホスト上、あるいはあなたのノートパソコンからであり、本番サーバーが一度も目にしたことのない鍵を使います。この分離こそが肝心な点です。最後のステップで、利便性のためにそれを台無しにしないでください。
静かな失敗を起こさずに自動化する
典型的なバックアップの大惨事とは、クラッシュするジョブのことではありません。動かなくなったのに誰にも知らせず、11か月後にそれを必要とした誰かによって発見されるジョブのことです。以下の要素はすべて、まさにその結末を起こりえなくするために存在しています。
cronではなくsystemdタイマーを使ってください。終了ステータス付きの本物のログがジャーナルに残り、Persistent=trueにしておけば、マシンの電源が落ちていた間に逃した実行は、永遠にスキップされるのではなく次回起動時に行われます。そしてRandomizedDelaySecにより、大量のマシンが03:00ちょうどにバックアップホストへ殺到することもありません。cronの失敗通知はローカルのメールボックス宛のメールであり、現代的なサーバーではまさにどこにも届きません。
# /etc/systemd/system/backup.timer
[Unit]
Description=Nightly encrypted backup
[Timer]
OnCalendar=*-*-* 03:17:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target次に、デッドマンスイッチを追加してください。これは、このガイド全体の中で単独では最も価値の高い一行です。スクリプトのまさに末尾で――バックアップが成功した後に、前にではなく――毎日連絡があることを期待し、なければ警告するモニターに対してHTTPSリクエストを送ります。これは通知のロジックを反転させます。失敗がメッセージを生成することに頼るのではなく、不在そのものが警報になるのです。3日間動いていないジョブは、危機的状況での発見ではなく、あなたの受信箱の中の一通のメッセージになります。サードパーティのアカウントをこれ以上作りたくなければ、モニター自体をバックアップホスト上でセルフホストしてください。数行のコードと、それ自身のタイマーがあれば済みます。
最後に、私たち自身が少なくとも一度はぶつかったことのある、小さな運用上の落とし穴です。中断された実行はロックを残し、以降のすべての実行は、4日目の夜以降はもう読まなくなるメッセージとともに失敗し続けます――restic unlockは反射的にではなく、意図的に扱ってください。保存先のディスクが満杯になれば、誰かが気づくまですべてのジョブが失敗します。ジョブの状態だけでなく、空き容量についても警告してください。有効期限の切れたSSH鍵、ローテーションされたホスト鍵、関係のない作業中に追加されたnftablesのルール――そのどれもが、pullを静かに断ち切ります。そしてstartだけでなくsystemctl enable --nowを使ってください。一度も有効化されなかったタイマーは、最初の再起動までは見事に動作し、その後は二度と実行されません。
復元訓練
ここまで述べてきたことはすべて準備にすぎません。これをバックアップへと変える部分であり、ほとんど誰もが省略してしまう部分です。
四半期に一度、新しいVPSをデプロイしてください――5ドルのStarterプランで十分であり、料金は時間単位で秒割りなので、この演習全体でかかるのはわずか数セントです。実際の災害時に手元にあるはずのものだけを使って、そこへ復元してください。リポジトリのアドレス、パスワードマネージャーに入っているパスフレーズ、そして書き留めておいた手順です。本番環境にあるものは意図的に一切使わないでください。あなたがリハーサルしているシナリオでは、本番環境は存在しないことになっているからです。アプリケーションを起動し、hostsファイルのエントリーを新しいIPに向け、実際にクリックして回ってください。それが済んだら破棄してください。
# on a throwaway machine, with nothing but the passphrase
export RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/prod
restic snapshots # can you even see the history?
restic restore latest --target /restore
restic check --read-data-subset=10% # verify stored blocks, not just metadataこれが捕まえてくれるものは、決してあなたが予想したものではありません。includeリストがカバーしていなかったディレクトリにあった設定ファイル。パスワードが変更されたのにスクリプトが終了ステータスをチェックしていなかったせいで、5週間もの間ゼロバイトのままだったデータベースダンプ。オーケストレーターが保持する環境変数にシークレットが存在し、ファイルシステム上にはそもそも一度も存在しなかったせいで起動しないアプリケーション。文書化されていないDNSレコード。443番ポートが何か応答する前に再発行しなければならない証明書。そのどれもが、静かな午後であれば10分で直せるものであり、朝の3時であれば見苦しい2時間になるものです。
この訓練が最初から最後までどれだけかかったかを書き留めてください。その数字こそが――バックアップの頻度ではなく――あなたの本当の復旧時間であり、どれだけ止まるのかと聞かれたときの唯一の誠実な答えです。restic check --read-data-subset=5%も月次タイマーに加えてください。インデックスだけでなく、実際に保存されたブロックのローテーションするサンプルを読み込んで検証してくれるので、まだ頼れる良いコピーが残っているうちに、静かな破損を発見できます。もしいつかサーバー全体を新しいホストへ移行することになれば、リハーサル済みの復元は、その移行作業の大部分をすでに終わらせてくれているということでもあります。
- 本当に再構築できないものを棚卸しする
ツールに手をつける前に、リストを書き出してください。マシンを一通り確認し、各ディレクトリについて、もしこれが消えたらパッケージマネージャーやgitリポジトリ、composeファイルから再現できるか、と自問してください。できるのであれば、それはバックアップに含めるべきものではありません。残るものは、たいてい人々が予想するよりもずっと小さくなります――設定、ユーザーデータ、アプリケーションの状態、データベースのダンプです。
# the fast way to find what is actually big and stateful du -x -h -d2 / 2>/dev/null | sort -rh | head -30 docker volume ls # named volumes are state; overlay2 is notその結果を、
/etc/restic/include.txtと/etc/restic/exclude.txtという二つの明示的なファイルにしてください。凝ったfind式よりも明示的なリストのほうが優れています。レビューできますし、サーバー上に新しいディレクトリが現れたときに、それが黙って含まれてしまうのではなく、意図的な決定であるべきだからです。 - バックアップ先を、別の法域にデプロイする
本番環境が動いているのとは別のリージョンに、二台目のVPSを注文してください――本番がパリにあるなら、コピーはレイキャビクかブカレストに置きます。重要なのは、単一の法的または物理的な出来事が両方のマシンに届かないようにすることです。月額5ドルのStarterプランには80GBのNVMeが付属しており、重複排除後であれば典型的な小規模サーバーのかなり長い履歴を保持できます。12か月契約であればそれが半額になります。保存先が元のサーバーの匿名性を打ち消すのではなく引き継ぐように、同じ暗号資産残高から支払ってください。
認証情報を完全に分離したいのであれば、本番環境とは別のアカウントで用意してください。そのうえで、他のあらゆるものと同じように堅牢化してください――鍵認証のみのSSH、デフォルト拒否のファイアウォールです――そしてそれ以外は何もインストールしないでください。このマシンの価値は、退屈であることにあります。Webサーバーもなく、SSH以外に開いているポートもなく、インターネットから突くべきものが何もありません。
- バックアップホストから本番環境への、読み取り専用の入り口を作る
これが、構成をpullにするステップです。バックアップホスト側で、専用の鍵を生成してください。そして本番側に、その公開鍵を強制コマンド付きでインストールし、その鍵にできることをファイルの読み取りただ一つだけに限定します。シェルを開くことも、ポートをフォワードすることも、何かを書き込むこともできません。
# on the backup host ssh-keygen -t ed25519 -a 100 -f ~/.ssh/pull_prod -C "pull@backup" # on production, in /root/.ssh/authorized_keys — one line command="/usr/bin/rrsync -ro /",restrict ssh-ed25519 AAAAC3Nz… pull@backuprrsyncはrsyncに同梱されています(Debian 13では/usr/bin/rrsync、それより古いリリースでは/usr/share/doc/rsync/scripts/rrsync)。-roは読み取り以外のすべてを拒否させます。restrictは、ポートフォワーディング、エージェントフォワーディング、PTYの割り当て、X11を一語で無効にします。それに頼る前に、この檻がきちんと機能することを確認してください――ssh -i ~/.ssh/pull_prod root@productionは、シェルを与えることに失敗しなければなりません。 - 本番環境でデータベースを、独自のスケジュールでダンプする
本番環境が引き続き担う仕事が一つだけあります。pullが回収する先となるステージングディレクトリに、整合性の取れたダンプを作り出すことです。これはバックアップ用の認証情報を一切必要としません。まさにそれこそが、この設計がうまく機能する理由です。
# /usr/local/sbin/dump-db.sh (chmod 700) set -euo pipefail D=/var/backups/db; install -d -m 700 "$D" mariadb-dump --single-transaction --quick --routines --triggers --events \ --all-databases | zstd -T0 > "$D/all.sql.zst.tmp" mv "$D/all.sql.zst.tmp" "$D/all.sql.zst"一時ファイルに書き込んでからリネームしている点に注目してください。これにより、タイミングがどうであれ、pullが書きかけのダンプを回収してしまうことは決してありません。
set -euo pipefailは飾りではありません――これがなければ、失敗したmariadb-dumpが空のストリームをzstdに渡してしまい、それは成功してしまうため、中身が何もない有効な圧縮ファイルができあがります。これこそが、バックアップが静かに無意味になる最も一般的な原因です。pullの30分前に、専用のタイマーから実行してください。 - データをバックアップホストへpullする
バックアップホスト側で、本番環境のincludeリストをステージング用のツリーへrsyncしてください。変化したブロックだけが回線を流れるため、最初の実行以降は高速かつ低コストです。
# /usr/local/sbin/pull.sh (chmod 700, runs on the BACKUP host) set -euo pipefail [email protected] # -r is spelled out on purpose: with --files-from, -a does NOT imply # recursion, and without it you silently copy empty directories. rsync -aHAX -r --delete --numeric-ids \ -e "ssh -i /root/.ssh/pull_prod -o BatchMode=yes" \ --files-from=/etc/backup/include.txt \ --exclude-from=/etc/backup/exclude.txt \ "$SRC:/" /srv/staging/prod/-aHAXはハードリンク、ACL、拡張属性を保持し、--numeric-idsは/etc/passwdが異なるマシンの間でも所有者情報を意味のあるものに保ちます。--deleteがここで安全なのは、ステージングがバックアップそのものではないからに他なりません――バージョン管理された履歴は次のステップで構築するresticリポジトリの中にあるので、ステージングに伝播した削除も、前日のスナップショットから復元可能なままです。 - リポジトリを初期化し、鍵をオフラインで保管する
引き続きバックアップホスト側で、ローカルのresticリポジトリを作成し、ステージングツリーをそこへバックアップしてください。ローカルであるということは、ホットパスにネットワークが介在せず、盗まれるリモート認証情報も存在せず、復元がディスク速度で実行されるということです。
apt install -y restic install -d -m 700 /etc/backup openssl rand -base64 32 > /etc/backup/pass # write this into your password manager NOW chmod 600 /etc/backup/pass export RESTIC_REPOSITORY=/srv/restic/prod export RESTIC_PASSWORD_FILE=/etc/backup/pass restic initそのパスフレーズをパスワードマネージャーにコピーし、その隣にリポジトリのパスと復元コマンドを書いておいてください。そして証明してください。三台目のマシンから、パスワードマネージャーの中身だけを使って、
restic -r sftp:backup@…:/srv/restic/prod snapshotsを実行します。それでスナップショットが一覧表示されれば、その鍵は本当に復元可能です。どちらかのサーバーにしか存在しない何かが必要になるなら、それを後で発見するのではなく今すぐ直してください。 - スケジュールし、沈黙そのものを警報にする
pull、resticの実行、プルーニングを一つのスクリプトにまとめ、systemdタイマーから駆動してください。ここで
forget --pruneを実行しても安全なのは、バックアップホストが正当にリポジトリを所有しているからです――本番サーバーは、いかなる時点でも削除用の認証情報を保持したことがありません。# tail of /usr/local/sbin/backup.sh restic backup /srv/staging/prod --tag nightly restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 \ --keep-yearly 2 --prune curl -fsS -m 10 --retry 3 https://hc.example.net/ping/<uuid> # only on successsystemctl daemon-reload systemctl enable --now backup.timer systemctl list-timers backup.timer # confirm the next run is where you expectcurlが実行されるのは、それより前のすべてのコマンドが成功した場合だけです。set -eのおかげです。反対側のモニターは毎日のpingを期待しており、それが途絶えると警告します。これにより、静かに止まったジョブは、考古学的な発見ではなく一通のメールになります。startではなくenable --nowを使ってください――一度も有効化されなかったタイマーは、まさに最初の再起動まで生き延びるだけです。 - 復元訓練を実行し、かかった時間を書き留める
四半期に一度、あなたが実際に目を通しているものの中に、繰り返しのリマインダーを設定してください。使い捨てのStarterプランをデプロイし、パスフレーズと書き留めた手順だけを使ってそこへ復元し、アプリケーションを起動し、実際のデータを返していることを確認し、そのマシンを破棄してください。時間単位の課金なので、この訓練全体でかかるのはわずか数セントです。
restic restore latest --target /restore zstd -dc /restore/var/backups/db/all.sql.zst | mariadb systemctl start nginx app curl -H 'Host: example.com' http://127.0.0.1/healthかかった時間と、遭遇したすべての驚きを記録し、その驚きは記憶の中にではなくバックアップスクリプトの中で修正してください。その所要時間こそが、あなたの本当の目標復旧時間です。一度計測するまでは、あなたが口にするどんな数字も、単なる推測にすぎません。
コピーをどこに置くか
| 保存先 | 匿名性への代償 | コスト | 復元速度 | 生き延びるもの |
|---|---|---|---|---|
| 二台目のno-KYC VPS(別リージョン) | なし――同じ暗号資産残高から支払い、どこにも身元は残らない | 月5ドル、12か月契約で半額 | 高速――データセンター間を1~10Gbpsで | アカウント喪失、侵害、一つの法域、自分自身のミスを生き延びる |
| プロバイダーのスナップショット | なし | 安価 | 数秒 | ほぼ何も生き延びない――同じアカウント、同じ障害ドメインで、両方ともろとも消える |
| オブジェクトストレージ(B2 / S3 / Wasabi) | 高い――アカウントにカードと身分証明書が紐づき、サーバー上のAPIキーがそこを指し示す | 保存コストは非常に安いが、復元時のエグレス料金が痛い | 高速、エグレス料金を受け入れるなら | 侵害とアカウント喪失を生き延びるが、マシンに名前が貼りつく代償を払う |
| 暗号資産を受け付けるストレージプロバイダー | 低い、支払い経路が本当に身元不要であれば――確認すること、思い込まないこと | 中程度 | 大きくばらつく――必要になる前にエグレス上限を確認すること | たいていのものを生き延びるが、自分で管理するVPSよりカウンターパーティリスクが大きい |
| WireGuard経由でpullする自宅のNAS | なし――暗号化されず身元と紐づかない形で自宅ネットワークを出るものは何もない | すでに所有しているハードウェア | 低速――自宅のアップロード速度が上限 | 自宅以外のすべてを生き延びる。唯一のコピーとしては心もとないが、三つ目としては優秀 |
| 何もしない/「gitに入っている」 | なし | 無料 | 不可能 | 何も生き延びない。Gitが保持するのはあなたのコードだけであり、データベースも、アップロードされたファイルも、/etcも保持しない |
よくある質問
プロバイダーのスナップショットは、もうすでにバックアップなのではありませんか。
いいえ、違います。そしてこの区別は重箱の隅をつつく話ではありません。スナップショットは、それがコピーするサーバーと同じアカウント、同じ認証情報のもとで、同じ障害ドメインに存在します――そのためアカウントが失われれば生き延びられませんし、パネルに到達できる者なら誰でも、オリジナルと一緒にそれを削除できます。稼働中のマシンから取得される点も問題です。負荷のかかったデータベースは、書き込みの途中で捉えられてしまいます。スナップショットはリスクの高い変更の前の取り消しボタンとして使い、バックアップとしてはオフサイトの暗号化されたリポジトリを使ってください。
resticとBorg、どちらを使うべきですか。
特に理由がない限りresticです。デフォルトで暗号化を行い、保存先に何もインストールする必要がなく、使う価値のあるバックエンドをひととおり話し、単一の静的バイナリとして配布されます。Borgは重複排除と圧縮の性能がより高く、数百万個の小さなファイルを含むファイルシステムでは体感できるほど高速ですが、両端にインストールしてバージョンを一致させる必要があり、本来は一つのリポジトリにつき一つのクライアント向けに設計されています。大きなメールスプールで、かつ両方のマシンを自分で管理しているならBorg。それ以外はrestic、というのが結論です。
バックアップ先には、どれくらいのディスク容量が必要ですか。
このガイドの保持期間の階段――日次7、週次5、月次12、年次2――を使った重複排除リポジトリであれば、サーバー全体ではなく、実際にバックアップしているデータの2~3倍程度を目安にしてください。26個のスナップショットは26個分のコピーを意味しません。変化したブロックだけが改めて保存されるからです。実データが15GB程度の小さなサイトであれば、80GBのStarterプランに、何年分もの余裕を持った履歴とともに、無理なく収まります。
二台目のVPSを用意すると、ホスティング料金が倍になりませんか。
バックアップ先が本番環境と同じスペックである場合に限りそうなりますが、そうする必要はありません。アプリケーションを何も動かさず、トラフィックも一切さばかないので、必要なのはCPUではなくディスクです。30ドルの本番サーバーに対して5ドルのStarterプランなら、請求額は6分の1増にしかならず、12か月契約であればさらに半額になります。すべてを失うコストと比べれば、請求書の中で最も安い項目であり、しかもそのバックアップ先は復元訓練のリハーサル機としても二重に役立ちます。
リポジトリのパスワードは、どこに置いておくべきですか。
それ自体が別の場所にバックアップされているパスワードマネージャーの保管庫に、あるいは紙に、あるいはその両方に――そしてその隣に、リポジトリのアドレスと正確な復元コマンドも一緒に置いてください。サーバー上の/etcにあるコピーは、自動化のための作業用コピーであって、記録の正本ではありません。正しく検証してください。本番でもバックアップホストでもないマシンから、パスワードマネージャーの中身だけを使ってrestic snapshotsを実行するのです。それ以外の何かが必要になるようであれば、あなたにはまだ復元可能なバックアップがありません。
バックアップはどれくらいの頻度で実行すべきですか。
どれだけの作業をやり直してもよいかを自問してください。ほとんどのサーバーには毎晩の実行が適しています。失われるのはせいぜい1日分であり、一晩に一回の実行なら把握しやすいからです。負荷の高いデータベースにはもっと頻繁な実行が必要です――データベースだけを1時間ごとにダンプし、ファイル全体のパスは毎晩のままにしておく、というのがよくある形です。それ以上の頻度を追求しても、同じ労力を保持期間の延長や実際の復元訓練に振り向けた場合ほどの効果は得られません。本当のリスクは、そちらの側にあるからです。
ディスクがLUKSで暗号化されているサーバーもバックアップできますか。
できます。しかもこの二つは重複しているのではなく、互いを補完し合っています。LUKSはマシンの電源が落ちている間ディスクを守り、バックアップは削除、破損、マシンそのものの喪失からあなたを守ります。マウントされたファイルシステムは、暗号化されていない場合とまったく同じ方法でバックアップしてください――resticが送り出す際にもう一度暗号化してくれるので、リポジトリは信頼できないストレージの上でも安全です。二つの秘密は、本当の意味で別々の場所に保管してください。LUKSのパスフレーズをresticリポジトリの中だけに、あるいはresticのパスフレーズをLUKSボリュームの中だけに保存してしまうと、両側とも締め出されて開かなくなるループになります。
サーバーが侵害された場合、バックアップは安全ですか。
それは、あなたが何か月も前に下した一つの決定だけにかかっています。本番サーバーがリポジトリへの削除権限を持つ認証情報を保持しているなら、答えはノーです――攻撃者はまずバックアップを列挙して破壊します。それがインシデントを交渉に変えるものだからです。リポジトリがappend-onlyであるか、バックアップホストがpullを行い、本番サーバーがバックアップ用の認証情報を一切保持していないのであれば、履歴は生き延び、侵入以前のスナップショットへ復元できます。これこそがpull構成を支持する論拠のすべてであり、必要になる前にそれを選んでおくべき理由です。