WEELWORKS

復元してから、世代を整理する

前回、ローカルディスクにリポジトリを作って3本のスナップショットを取りました。ただ、まだ一度も戻していません。取れることと戻せることは別の話で、確認していないほうが本番で効いてきます。

今回は戻し方を3通り確認し、保管したデータが壊れていないことを検証する方法を見て、最後に増え続けるスナップショットを整理します。

3つの戻し方

restic には復元の手段が3つあります。使い分けの基準がはっきりしているので、順に見ます。

1. restore — ファイルとして書き出す

いちばん基本の形です。

restic restore latest --target /srv/restore
restoring snapshot 578b2b2f of [/srv/data] at 2026-08-14 11:47:46 +0900 JST by root@backup-host to /srv/restore
Summary: Restored 8 files/dirs (5.000 MiB) in 0:00

--target の下に、元のディレクトリ構造がそのまま作られます。

find /srv/restore -type f
/srv/restore/srv/data/docs/estimate.md
/srv/restore/srv/data/docs/minutes.md
/srv/restore/srv/data/photos/photo1.jpg
/srv/restore/srv/data/photos/photo2.jpg

/srv/data/srv/restore/srv/data に展開されています。元の場所に直接上書きするのではなく、いったん別の場所へ出すのが基本です。復元したものが期待どおりか確認してから移せます。

確認は diff -r で十分です。

diff -r /srv/data /srv/restore/srv/data && echo "差分なし"
差分なし

このひと手間が、この連載でいちばん大事な習慣です。バックアップの正しさは、この比較でしか確かめられません。

必要なものだけ戻すこともできます。

restic restore latest --target /srv/restore2 --include /srv/data/docs/minutes.md
Summary: Restored 4 / 1 files/dirs (37 B / 37 B) in 0:00

5 MB のスナップショットから 37 バイトだけ取り出しました。「あのファイルだけ、3日前の版で欲しい」に答えるのがこれです。

2. dump — 標準出力へ流す

ファイルを書き出さずに中身を見たいときは dump です。

restic dump latest /srv/data/docs/minutes.md
議事録 2026-08-14
追記した行

標準出力に出るので、そのままパイプできます。設定ファイルの中身を確認したいだけ、といった場面で速い。連載の中で DB のダンプを扱うときにも、この dump がそのまま復元手段になります。

3. mount — ファイルシステムとして歩く

障害対応中にいちばん効くのがこれです。

mkdir -p /mnt/restic
restic mount /mnt/restic

別の端末から覗くと、リポジトリがディレクトリツリーとして見えます。

ls /mnt/restic
hosts
ids
snapshots
tags
ls /mnt/restic/snapshots
2026-08-14T11:47:44+09:00
2026-08-14T11:47:45+09:00
2026-08-14T11:47:46+09:00
latest

日時のディレクトリがそのままスナップショットです。あとは普通のファイル操作で歩けます。

cat /mnt/restic/snapshots/latest/srv/data/docs/minutes.md
議事録 2026-08-14
追記した行

lscatgrepcp がそのまま使えるので、「どのスナップショットに欲しいものが入っているか」を探す作業に向いています。復元コマンドを何度も打ち直す必要がありません。

mount には FUSE が必要です。Ubuntu なら fuse3 パッケージを入れておきます。終わるときは fusermount3 -u /mnt/restic でアンマウントします。

保管したデータが壊れていないか確かめる

ディスクは静かに腐ります。復元を試さずに健全性を確認する手段が check です。

restic check
check all packs
check snapshots, trees and blobs
[0:00] 100.00%  3 / 3 snapshots
no errors were found

ただし、これは構造の検査です。目録に書かれたチャンクが全部そこにあるか、参照が壊れていないかを見ています。ファイルの中身が本当に元どおりかまでは見ていません。

中身まで検証するには、実データを読み直させます。

restic check --read-data-subset=10%
check all packs
check snapshots, trees and blobs
[0:00] 100.00%  3 / 3 snapshots
read 10.0% of packfiles
[0:00] 100.00%  1 / 1 packs
no errors were found

--read-data-subset=10% は「保管データの1割を実際に読んでハッシュを照合する」という指定です。全部読む --read-data もありますが、リポジトリが大きいと転送量と時間が現実的でなくなります。毎週1割ずつ読めば、10週で一巡します。 この連載では後ほど、週1回の check を systemd timer に登録します。

スナップショットが増え続ける問題

ここからが今回の後半です。毎日バックアップを取れば、スナップショットは毎日1本ずつ増えます。

検証のために、3か月ぶんの履歴を作りました。restic backup には --time があり、スナップショットの日時を指定できます。

d=$(date -d "2026-05-17" +%s)
end=$(date -d "2026-08-14" +%s)
while [ $d -le $end ]; do
  ds=$(date -d "@$d" +%Y-%m-%d)
  echo "day $ds" > /srv/hist/journal.txt
  restic backup /srv/hist --time "$ds 02:00:00" --quiet
  d=$((d+86400))
done

これで90本のスナップショットができました。

restic snapshots --json | jq length
90

3か月で90本。1年運用すれば365本です。全部を残す意味はありません。1年前の水曜日の版が要ることは、まずないからです。

保持ポリシーを決める

restic の forget は「何を残すか」を指定します。「何を消すか」ではありません。

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run

意味はこうです。

  • --keep-daily 7 — 直近7日ぶん、日ごとに最新の1本を残す
  • --keep-weekly 4 — 直近4週ぶん、週ごとに最新の1本を残す
  • --keep-monthly 6 — 直近6か月ぶん、月ごとに最新の1本を残す

これらは重なります。今日のスナップショットは「日」にも「週」にも「月」にも該当しますが、1本として数えられます。

必ず --dry-run を先に付けてください。 forget は取り消せません。

実行すると、90本のうち77本が削除対象になりました。

Would have removed the following snapshots:
{03ada452 09616fb5 106f3c71 ... f9f8a4bc fd208ffa}

--dry-run を外して実行し、残ったものを見ます。

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6
restic snapshots
ID        Time                 Host         Tags        Paths      Size
------------------------------------------------------------------------
391cc849  2026-05-17 02:00:00  backup-host              /srv/hist  15 B
b01b50b8  2026-05-31 02:00:00  backup-host              /srv/hist  15 B
0c79b33b  2026-06-30 02:00:00  backup-host              /srv/hist  15 B
299edc5a  2026-07-26 02:00:00  backup-host              /srv/hist  15 B
4927b2ae  2026-07-31 02:00:00  backup-host              /srv/hist  15 B
fc267e30  2026-08-02 02:00:00  backup-host              /srv/hist  15 B
22063c3c  2026-08-08 02:00:00  backup-host              /srv/hist  15 B
99bb1b2e  2026-08-09 02:00:00  backup-host              /srv/hist  15 B
0184b610  2026-08-10 02:00:00  backup-host              /srv/hist  15 B
b91c0e8c  2026-08-11 02:00:00  backup-host              /srv/hist  15 B
6859d59b  2026-08-12 02:00:00  backup-host              /srv/hist  15 B
a4aaf241  2026-08-13 02:00:00  backup-host              /srv/hist  15 B
5439b358  2026-08-14 02:00:00  backup-host              /srv/hist  15 B
------------------------------------------------------------------------
13 snapshots

90本が13本になりました。並びを見ると意図がよく分かります。直近1週間(08-08 から 08-14)は日ごとに全部残り、そこから遡るにつれて間隔が広がり、いちばん古いのは5月の1本だけです。近いほど細かく、遠いほど粗く。 これが --keep-daily / --keep-weekly / --keep-monthly の組み合わせで作られる形です。

forget だけでは容量は減らない

ここが引っかかりやすいところです。forget の直後にリポジトリのサイズを見ます。

du -sh /srv/backup/hist
2.2M	/srv/backup/hist

77本消したのに、forget の前と同じ 2.2M のままです。

forget が消すのは目録(スナップショット)だけで、実データには触っていないからです。目録から参照されなくなったチャンクは残り続けます。これを回収するのが prune です。

restic prune
to repack:            52 blobs / 13.391 KiB
this removes:          0 blobs / 0 B
to delete:           308 blobs / 61.500 KiB
total prune:         308 blobs / 61.500 KiB
remaining:            52 blobs / 10.395 KiB
unused size after prune: 0 B (0.00% of remaining size)

repacking packs
[0:00] 100.00%  26 / 26 packs repacked
rebuilding index
[0:00] 100.00%  91 / 91 indexes processed
removing 180 old packs
[0:00] 100.00%  180 / 180 files deleted
done
du -sh /srv/backup/hist
1.2M	/srv/backup/hist

2.2M から 1.2M へ減りました。

prune の出力にある repack に注目してください。restic はチャンクを pack というファイルにまとめて保存します。1つの pack の中に「まだ使われているチャンク」と「もう使われていないチャンク」が混ざっていると、pack ごと消すわけにいきません。そこで生きているチャンクだけを新しい pack に詰め直してから、古い pack を消します。これが repack です。

この詰め直しがあるので、prune は重い処理です。 リポジトリ全体を見渡し、pack を読み直して書き直します。ローカルディスクなら気になりませんが、連載の後半でリモートの保管先に移すと、ここが転送量として効いてきます。毎回のバックアップの後に prune を走らせる設計にはしないほうが無難です。

なお、forget--prune を付ければ両方を続けて実行できます。

今回のまとめ

backup(スナップショットが増える)forget(目録を減らす)prune(実データを回収する)check(壊れていないか見る)
  • restore で戻し、diff -r で必ず突き合わせる
  • 探すときは mount、中身を見るだけなら dump
  • check は構造の検査。中身を見るには --read-data-subset
  • forget は目録を減らすだけ。容量を戻すのは prune
  • forget は取り消せない。--dry-run を先に

次回

ここまでのコマンドを毎晩実行することを考えると、引数の量が現実的でなくなってきます。バックアップして、世代を整理して、週1回は検証して、対象ごとに設定を変えて。前回まででシェルスクリプトが200行に育つ、と書いた地点にちょうど差しかかりました。

次回は resticprofile を入れて、ここまでの内容を1つの設定ファイルに移します。

この回で使ったツール