WEELWORKS

バックアップは「取れているか」ではなく「戻せるか」で選ぶ

サーバが壊れたとき、最初に確認するのはバックアップの有無ではありません。バックアップは「ある」のです。cron に仕込んだスクリプトは毎晩動いていて、共有フォルダには日付入りの .tar.gz が並んでいます。問題は次の一手で、その中の1本を選んで展開したときに、何が入っているかを誰も知らないことです。

展開してみると、DB のダンプが 0 バイトでした。原因はすぐに分かります。数か月前にサーバのバージョンを上げたとき、pg_dump がバージョン不一致で失敗するようになっていたのです。ただスクリプトは pg_dump ... | gzip > dump.gz と書かれていて、gzip は空の入力を受け取って正常終了し、cron は毎晩「成功」を返し続けていました。

この連載では resticresticprofile でバックアップ環境を組み立てていきます。初回の今回は手を動かさず、「なぜこの構成にしたのか」を書きます。

選定の前に、要件を言葉にする

ツールを比べる前に、何を満たしたいのかを決めておきます。冒頭の話から取れる要件は、次の5つです。

1. 失敗が失敗として見えること

いちばん重要な要件です。動いていないことに気づけないバックアップは、無いバックアップより悪い。「バックアップがある」という前提で他の対策を省いてしまうからです。

2. 復元が簡単であること

障害対応の最中に、復元手順を思い出しながら操作することになります。「特定の1ファイルだけ、3日前の版で欲しい」に何分で答えられるかが、実務では効きます。

3. 保管したデータが壊れていないと確認できること

ディスクは静かに腐ります。保管先が壊れていないことを、復元を試さずに確かめられる手段が要ります。

4. 保管先を信用しなくてよいこと

バックアップ先はたいてい自分の管理外です。レンタルの NAS、共用のファイルサーバ、クラウドストレージ。そこに平文で置けば、保管先の管理者が読めます。

5. 世代を持てて、容量が破綻しないこと

毎日フルバックアップを取れば容量が持ちません。世代を残しつつ容量を抑える仕組みが要ります。

候補を並べる

この5つを基準に、よく使われるツールを見ていきます。

ツール重複排除暗号化世代管理保管先に必要なもの
tar + cronなし自前自前ファイルが置ければよい
rsync / rsnapshotハードリンクなしハードリンクで擬似的にrsync(または SSH)
DuplicityなしGPGフル+増分ファイルが置ければよい
BorgBackupありありありリモートは borg 本体
Kopiaありありありファイルが置ければよい
resticありありありファイルが置ければよい

tar + cron は要件1で落ちます。落ちるというより、全部を自分で書くことになります。冒頭の 0 バイトのダンプは、まさにこの自前実装の隙間から生まれたものでした。

rsync / rsnapshot は同期のツールです。転送は速く、復元は「コピーして戻すだけ」で分かりやすい。ただし暗号化がなく(要件4)、ミラーである以上、元のファイルが壊れた状態で同期が走れば壊れたものが上書きされます。

Duplicity は GPG で暗号化でき、増分も取れます。ただ重複排除がないため、増分を積み重ねると復元時に全チェーンを辿ることになり、定期的なフルバックアップの取り直しが避けられません。

BorgBackup は要件を素直に満たします。重複排除も暗号化も世代管理もあります。引っかかるのは保管先の条件で、リモートに置く場合は保管先にも borg 本体が必要です(SSH 越しに borg serve が動きます)。「ファイルが置けるだけの場所」――WebDAV やオブジェクトストレージ――を直接は扱えません。

Kopia は restic とかなり近い位置にあります。重複排除・暗号化・多様な保管先を持ち、GUI もあります。

restic を選んだ理由

Borg と Kopia を落として restic を選んだのは、次の3点です。

保管先に何も要求しない

restic のリポジトリは、ただのファイルとディレクトリの集まりです。保管先に必要なのは「ファイルを置けること」だけで、専用のサーバプロセスは要りません。この連載でローカルディスク・SFTP・SMB 共有・WebDAV と保管先を差し替えていきますが、変わるのは接続文字列1行だけです。

暗号化を選べない

restic には「暗号化しない」という選択肢がありません。リポジトリは常に暗号化されます。設定を間違えて平文で置いてしまう事故が、構造的に起こらないということです。要件4に対する答えとして、これ以上のものはありません。

復元の手段が3つある

restic restore でファイルとして書き出す、restic dump で標準出力へ流す、restic mount でリポジトリをファイルシステムとしてマウントする。とくに mount は、障害対応中に「どのスナップショットに欲しいものが入っているか」を探すときに効きます。普通の lscp で中身を歩けるからです。

正直に書いておくと、この3点で Kopia が明確に劣るわけではありません。ここは「決定的な差で選んだ」というより、単一バイナリで依存がなく、ドキュメントと運用事例が積み上がっている restic を選んだ、という判断です。

restic に足りないもの

利点だけ並べても選定の記録にはなりません。restic を使ううえで確実に困る点を挙げます。

  • 設定ファイルがない:restic はコマンドライン引数と環境変数だけで動きます。設定ファイルという概念がありません。
  • prune が重い:不要になったデータの回収は、リポジトリ全体を見渡す処理です。データ量に比例して時間がかかります。
  • メモリを使う:リポジトリのインデックスをメモリに載せます。小さなVPSで巨大なリポジトリを扱うと、ここが効いてきます。
  • 同時実行できない:1つのリポジトリに同時に書き込めません。ロックで直列化されます。

このうち最初の1点が、運用の形を決めてしまいます。

設定ファイルがないと、何が起きるか

restic に設定ファイルがない以上、実際の運用はシェルスクリプトになります。最初は数行です。

#!/bin/bash
export RESTIC_REPOSITORY=/mnt/nas/restic
export RESTIC_PASSWORD_FILE=/etc/restic/password
restic backup /srv/data

ここに要件が足されていきます。世代を整理したい、終わったら通知したい、DB を止めてから取りたい、失敗したら止めたものを戻したい、二重起動を防ぎたい、ログを残したい。

半年後には200行のシェルスクリプトができています。そしてそのスクリプトには、テストがありません。バックアップの正しさが、誰もテストしていないシェルスクリプトの正しさに置き換わっている――これが、設定ファイルがないことの本当の代償です。

冒頭の pg_dump | gzip も、まさにこの種のスクリプトの一部でした。

resticprofile を足す

そこで resticprofile を挟みます。restic の前段に置いて、設定ファイルから restic のコマンドラインを組み立てるツールです。

profiles.yaml(設定)resticprofileresticローカル / SFTP / SMB / WebDAVフック実行スケジュール生成HTTP 通知コマンドラインを組み立てる暗号化して保存

resticprofile が引き受けるのは、さきほど200行に育っていった部分です。

シェルスクリプトで書いていたものresticprofile での書き方
世代整理の forget を後から呼ぶretention セクション
バックアップ前後にコマンドを走らせるrun-before / run-after / run-finally
cron や systemd unit を手書きするschedule を書いて resticprofile schedule
二重起動を防ぐlock
成功・失敗を通知するsend-after / send-after-fail
対象ごとに設定を書き分けるプロファイルと inherit

重要なのは、resticprofile が restic を隠さないことです。設定から組み立てられた restic のコマンドラインは、--dry-run を付けるとそのまま表示されます。何が実行されるのか分からないまま任せる、という状態にはなりません。この点は連載の中で実際に確認します。

この構成で捨てるもの

  • ツールが2つに増える:restic のバージョンと resticprofile のバージョン、両方を追うことになります。
  • 設定ファイルの書き方を覚える必要がある:restic の引数を知っていても、それが設定ファイルのどのキーに対応するかは別途覚えることになります。
  • GUI がない:全てコマンドラインです。バックアップの状況を人が見て確認する運用には向きません。連載の中で監視を入れるのは、この穴を埋めるためでもあります。
  • Windows での自動実行は別物になる:この連載は Linux サーバを対象に、systemd timer で自動実行します。Windows ではタスクスケジューラになり、手順が変わります。

対象が1台のデスクトップで、たまに手で取れば十分、という状況ならこの投資は回収できません。止められない何かが動いているサーバに限って効く構成だと考えています。

この連載で作るもの

次回から手を動かします。到達点は次のとおりです。

  1. ローカルディスクへ、フォルダを1つバックアップする
  2. 復元と世代管理を覚える
  3. 設定を profiles.yaml に移す
  4. systemd timer で毎晩動かす
  5. DB のダンプを流し込む
  6. Docker コンテナを止めてから取る
  7. 失敗と無音を検知する
  8. 保管先をリモートへ移す

各回は、動く最小の状態から始めて足していきます。そして途中で何度か、「成功」と表示されているのに壊れている状態を意図的に作ります。冒頭の 0 バイトのダンプは特殊な事故ではなく、素直に組むと普通に踏む地雷だからです。

この回で使ったツール

  • restic — 重複排除と暗号化を備えたバックアッププログラム(ドキュメント
  • resticprofile — restic を設定ファイルから駆動するラッパー
  • BorgBackup — 重複排除バックアップツール(比較対象)
  • Kopia — 重複排除バックアップツール(比較対象)
  • Duplicity — GPG 暗号化に対応した増分バックアップツール(比較対象)