Macバックアップ戦略・改訂版

TL;DR

macOSの変更で起動可能なクローンが作れなくなり、長年使ってきたSuperDuperを手放しました。信用しきれないTime Machineも避け、rsyncでホームディレクトリを差分・鏡像コピーする方式へ移行しています。標準のopenrsyncは使わずHomebrew版を入れ、クラウド系を除外し、launchdで週一回まわす構成です。

Cover
Table of Contents

長らくMacのバックアップにはSuperDuperを使い、Bootableなクローンを作ってきました。その方針は、以前Apple Silicon時代のMacバックアップ最新戦略で整理したとおりです。ですが新しいmacOSへの対応がなかなか進まず、Tahoeに至っては一年あまり経った今も正式版すら出ません。そこでSuperDuperを諦め、rsyncでホームディレクトリを鏡像コピーする方式に切り替えました。この記事はその続編であり、乗り換えの理由と設定方法をまとめたものです。

なぜTime Machineが嫌いか

バックアップの話をすると、まずTime Machineを勧められます。ですが私は、ごく初期に使ったきり、その後はすっかり避けるようになりました。

もともとは、Bootableなクローンを作れるサードパーティツールを使っていたからです。 Bootableなクローンが作れれば、ディスクが壊れた際にはクローンからmacOSを起動して復旧できます。

ただし、現在ではBootableなクローンを技術的に作成できなくなりました(後述)。 それでも、Time Machineには不信感が根強く、直感が先に拒否します。その裏には技術的な理由が3つあります。

  • 不確実性

    • Time Machineはバックアップ先を専用のアーカイブ(sparsebundle)として抱え込む。中で何が起きているかは見えず、壊れていても警告は出ない。実際、復元しようとしたらアーカイブが読めず、修復もできず、履歴を全部捨てて最初からやり直すしかなかったという報告は後を絶たない。
  • 破損に弱い

    • sparsebundleは中身を多数の小さなバンドファイルに分け、その内側に1つのファイルシステムを抱える構造になっている。個々のファイルを直接取り出すことはできず、必ずイメージ全体をマウントして読む。だからバンドや内部の目次(カタログ)が一部でも壊れると、イメージごとマウント不能になり、世代を問わず全データに手が届かなくなる。世代を積み上げる構造だけに、過去のバックアップまでまとめて道連れになりやすい。
  • ブラックボックス

    • 何がどうコピーされ、どう積み上げられているのか、仕組みそのものがユーザーからは見えない。バックアップ先を開いても何がどう入っているか分からず、「このファイルが今そこにある」とFinderで素朴に確かめることもできない。中で何が行われているか把握できないものに大事なデータを預けるのは、技術者としてどうにも信用しきれない。

私が欲しいのはその逆です。バックアップ先を開けば、コピー元とそっくり同じ構造でファイルがそのまま並んでいて、いざというとき個別のファイルをドラッグで取り出せる。履歴はいりません。今この瞬間の鏡像が1つあれば十分です。見えて、触れて、そのまま取り出せること。Time Machineはこの要求に対して、仕組みからして向いていません。

そのため、私は丸ごとBootableなディスククローンを作成できるサードパーティ製のツールを利用してきました。

macOSの変化で、bootableクローンが封じられた

ところが、ここ数年のmacOSの変化で、その前提が崩れました。サードパーティ製ツールによるBootableなクローンが、事実上できなくなったのです。経緯を追うと、じわじわと封じられてきたことが分かります。

  • Big Surでシステムとデータが分離

    • 起動ディスクが、OSの入った読み取り専用のシステムボリュームと、ユーザーデータのデータボリュームに完全分離された。システム側は改ざん防止のため署名・封印され、サードパーティ製アプリが直接コピーできなくなった。OSを複製する役目はApple自身の仕組み(レプリケーター)に移り、それが動き続けるかどうかもApple次第になった。
  • Sequoia 15.2が決定打

    • Appleがそのレプリケーターを壊し、SuperDuperはBootableなバックアップを作れなくなった。データ複製の終盤、PrebootやRecoveryをコピーするあたりで失敗する。これはツール側のバグではなく、Apple側の問題だと開発元が明言している。
  • Tahoeでは正式対応すら出せず

    • 互換性の壁がさらに増え、SuperDuperは正式版を出せていない。Tahoe対応は暫定ベータ(v3.20系)でしのぐ状態が続いている。一時はベータでの改善に期待もしたが、一年あまり経ってもベータのまま。起動可能クローンへの望みは、もう捨てた。

要点を取り違えないことが大事です。壊れたのはBootableなシステム丸ごとクローンであって、ホーム(データ)のコピーが禁じられたわけではありません。Appleが封じたのはシステムボリュームの複製で、ユーザーのデータをコピーすること自体は今も普通にできます。

今日のmacOSでのバックアップ戦略

ここで要件が変わります。

Bootableにできないのなら、システムボリュームまで丸ごとコピーする意味はもうありません。bootableという目的が消えた以上、「全部コピーする」根拠も一緒に消えました。残ったのは、自分のデータだけを守れればいい、という縮退した要求です。

つまり「ホームディレクトリだけでいい」は、最初からそう思っていたわけではありません。bootableを失った結果として、そこまで切り詰めた制約の上に成り立っています。この経緯を踏まえると、要件は次のように整理できます。

  • バックアップ対象はホームディレクトリのみ。システムは諦める。
  • アプリが~/Library配下などに書く、ユーザーが関知しないデータも要らない。多くは再生成されるので、守る価値がない。
  • 差分でいい。変わっていないファイルは書き換えず、変わったぶんだけ転送する。
  • 世代管理はいらない。常にコピー元と同じ、今この瞬間の鏡像が1つあればいい。

rsyncのメリット

この縮退した要件に、rsyncはきれいに合います。

第一に、差分転送が本職です。rsyncは初期状態で、サイズと更新日時を見て一致するファイルをまるごとスキップします。変わっていないファイルは触らず、変わったぶんだけ送ります。「変更ないファイルは書き換えたくない」という要求が、特別な設定なしにそのまま満たされます。

第二に、コピー先がただのファイルになります。バックアップ先は元と同じフォルダ階層がそのまま並ぶので、Finderで全部見えますし、個別ファイルをドラッグで取り出せます。Time Machineの抽象化とは正反対の、見える鏡像が手に入ります。

第三に、世代を持たない運用ができます。--deleteを付ければ、コピー元で消したものはバックアップ先からも消え、常に最新の一枚の鏡像が保たれます。履歴の束は作られません。

第四に、無料で、スクリプトに組めて、スケジュール実行まで自前で完結します。専用アプリのライセンスや、OSの更新に追従できないツールの都合で振り回されることもありません。

標準のrsyncは使わない

Sequoia/Tahoe標準のrsyncはopenrsyncという別実装で、出来がよくありません。-aで特殊ファイルに当たると止まる、拡張属性でクラッシュするといった報告があります。標準のものは避け、Homebrewで本家のrsync(3.x系)を入れて使いましょう。

準備

まずHomebrewで本家rsyncを入れます。TahoeはApple Silicon専用なので、入れると/opt/homebrew/bin/rsyncに置かれます。

brew install rsync

もう1つ、フルディスクアクセスの設定が要ります。最近のmacOSでは、これが無いとホーム内の保護領域(メールなど)が読めず、警告も出さずに黙って飛ばされます。手で動作確認するときは使っているターミナルを、自動実行するときは後述のスクリプトを、システム設定のプライバシーとセキュリティ → フルディスクアクセスに追加しておきます。これを怠ると「バックアップしたつもりで一部入っていない」が起きます。

コマンドと除外設計

バックアップ本体は、次の一本にまとめます。

#!/bin/bash
DEST="/Volumes/Backup"
LOG="$HOME/Library/Logs/backup-home.log"

if ! /sbin/mount | grep -q "on $DEST "; then
  echo "$(date) $DEST not mounted, skipped" >"$LOG"
  exit 0
fi

/opt/homebrew/bin/rsync -aE --delete --info=progress2 \
  --exclude='/Library/' \
  --exclude='.Trash/' \
  --exclude=".cache/" \
  --exclude='tmp/' \
  --exclude='.DS_Store' \
  --exclude='/pCloud Drive' \
  --exclude='/.pcloud/' \
  "$HOME/" "$DEST/home/" >"$LOG" 2>&1
echo "$(date) done" >>"$LOG"

先頭のif ! /sbin/mount ...は、バックアップ先が未マウントのときに内蔵ディスクへ誤って書き込む事故を防ぐガードです。-aは権限・更新日時・シンボリックリンクなどを保ったまま再帰コピーし、-EはmacOSの拡張属性を保持します。--deleteでコピー元から消したものをバックアップ先からも消し、常に鏡像に保ちます。--info=progress2は全体の総進捗を一行で表示します。$HOME/の末尾スラッシュは「ホームの中身を」コピーする指定で、これが無いとhome自体が一段深く入れ子になります。

除外設計が実は一番の勘どころになります。詰まりの原因はほぼここに集約されます。

  • .Trashはゴミ箱、.cachetmpは再生成される一時データ、.DS_StoreはFinderの表示情報。どれもバックアップする価値がない。
  • クラウドや外部接続の仮想ドライブも必ず外す。ローカルに実体がなく、rsyncが舐めるとオンデマンドのダウンロードを次々誘発し、激遅やタイムアウト、最悪はクラウド上の全データの引きずり下ろしを招く。iCloud DriveやDropbox、OneDrive、Google DriveはLibrary/CloudStorageLibrary/Mobile Documentsにあるので、/Library/を外せば一緒に消える。pCloud Driveのようにホーム直下へマウントされるものだけ、個別に除外する。

Library配下をどこまで残すかは判断が要ります。大半はアプリが再生成する状態ファイルなので、丸ごと--exclude='Library/'で外しても困らない人は多いでしょう。ただしローカルにしか実体のないデータ(ローカル保存のメールなど)がある場合は、その特定フォルダだけ残す配慮がいります。

自動化

週1回の自動実行は、非推奨のcronではなくlaunchdで組みます。さきほどの~/bin/backup-home.shに実行権を付けます。

chmod +x ~/bin/backup-home.sh

次に~/Library/LaunchAgents/com.user.backup-home.plistを作ります。毎週日曜の午前3時に走る例です(Weekdayの0が日曜)。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.user.backup-home</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/ユーザ名/bin/backup-home.sh</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Weekday</key><integer>0</integer>
    <key>Hour</key><integer>3</integer>
    <key>Minute</key><integer>0</integer>
  </dict>
</dict>
</plist>

登録します。

launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.backup-home.plist

launchdの利点は、指定時刻にMacが起動していなくても、次に起動したときに取りこぼしを拾って実行してくれる点です。cronのように「その時刻に寝ていたら丸ごと飛ぶ」ということがありません。複数回ぶんが溜まっても、起動時に走るのは1回にまとめられます。週一の用途なら、これで実用上困りません。

つまずきどころ

実際に動かすと、たいてい最初の数回はここで引っかかります。

  • 初回はホーム全体のフルコピーなので、容量とドライブ速度によっては数時間かかる。差分で速くなるのは2回目以降。止まっているのか遅いだけなのかは、du -sk /Volumes/Backup/homeを時間を空けて2回打ち、数字が増えているかで判別する。
  • 異常に遅い・止まる場合は、まず標準の/usr/bin/rsync(openrsync)を叩いていないかwhich rsyncで確認する。そして上のクラウド系の除外が効いているかを見る。GoProなどをMTP接続してマウントしている場合、それもLibrary/CloudStorageに入り込んでタイムアウトの山を作るので、除外で一掃される。
  • Permission Deniedが出ても、rsyncはそのファイルを飛ばして続行する。多くはフルディスクアクセス不足か、root所有の触れないファイルだ。前者は設定で解決し、後者は無視してよいが、拒否されたのが自分の大事なデータでないかだけは確認する。

まとめ

この構成にも、諦めた点はあります。起動可能なクローンではないので、ディスクが飛んだら、まずmacOSを入れ直し、それからホームを書き戻す手間が残ります。アプリの再インストールも要ります。「つなげば即座に元通り」とはいきません。そこは割り切りです。

それでも私は、こちらを選びました。中身が見えて、いざとなれば一ファイルから引き出せて、仕組みを丸ごと自分の手で握れる。バックアップに一番ほしいのは、派手な復旧速度ではなく、この「見えている」という安心でした。OSやアプリの都合に振り回されないかぎり、この鏡像は週末ごとに静かに更新され続けてくれます。それだけで、私には十分です。