Gentoo Linux で Btrfs+Snapper

気がついたら最後のポストから1年経ってました。

Gentoo で Snapper 入れて壊れたらロールバックできるように設定したメモです。

Snapper は openSUSE 由来のスナップショット管理ツールです。 スナップショットの取得を自動化したり、スナップショットに名前をつけて管理したり、溜まったスナップショットを cleanup したりする機能があります。

スナップショットを取るためにはファイルシステムが Btrfs であるか、LVMスナップショットが取れるようにストレージが LVM で構成されている必要があります。 なお openSUSE では Btrfs の方を推奨しているようです。 今回は Btrfs を使います。

Btrfs サブボリューム設計

サブボリュームとは、一つの Btrfs ファイルシステムを分割して使う仕組みです。 分割したサブボリュームは他のファイルシステムと同様に mount コマンドでマウントして使うことができます。

Snapper が利用する Btrfs のスナップショット機能はこのサブボリューム機能を利用しています。 サブボリュームでファイルシステムを分割することで、スナップショットに含めたいディレクトリと含めたくないディレクトリに分けることができます。

以下は openSUSE 15.0 デフォルトのファイルシステムを参考にした Gentoo 向けのサブボリュームレイアウトです。 サブボリュームはすべて @ という名前のサブボリュームの下に作成しています。 Snapper のスナップショットに含めたくないディレクトリはすべてサブボリュームにします。 なお /.snapshots は Snapper が自動で作ります。

UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /                     btrfs     defaults                         0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /.snapshots     btrfs   subvol=@/.snapshots     0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /home              btrfs        subvol=@/home              0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /tmp               btrfs        subvol=@/tmp               0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /usr/local      btrfs   subvol=@/usr/local      0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/cache      btrfs   subvol=@/var/cache      0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/ccache     btrfs   subvol=@/var/ccache     0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/lib/libvirt/imagesbtrfs            subvol=@/var/lib/libvirt/images 0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/log           btrfs        subvol=@/var/log           0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/spool      btrfs   subvol=@/var/spool      0 0
UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /var/tmp           btrfs        subvol=@/var/tmp           0 0

ちなみに私は最初サブボリューム設定をせずに Gentoo をインストールしてしまったため、上記のレイアウトとなるよう、SystemRescueCD でブートして、一つづつ btrfs subvolume create しては既存ファイルシステムから rsync してデータを引っ越す作業をしてました。

Snapper のインストール

Gentoo では emerge -a app-backup/snapper でインストールできます。 Snapper の設定については Gentoo Wiki を参照してください。

snapper create-config / すると、 /etc/snapper/configs/root に以下のようなファイルができるはずです。

# subvolume to snapshot
SUBVOLUME="/"

# filesystem type
FSTYPE="btrfs"


# btrfs qgroup for space aware cleanup algorithms
QGROUP="1/0"


# fraction of the filesystems space the snapshots may use
SPACE_LIMIT="0.5"


# users and groups allowed to work with config
ALLOW_USERS="jsaito"
ALLOW_GROUPS=""

# sync users and groups from ALLOW_USERS and ALLOW_GROUPS to .snapshots
# directory
SYNC_ACL="no"


# start comparing pre- and post-snapshot in background after creating
# post-snapshot
BACKGROUND_COMPARISON="yes"


# run daily number cleanup
NUMBER_CLEANUP="yes"

# limit for number cleanup
NUMBER_MIN_AGE="1800"
NUMBER_LIMIT="50"
NUMBER_LIMIT_IMPORTANT="10"


# create hourly snapshots
TIMELINE_CREATE="yes"

# cleanup hourly snapshots after some time
TIMELINE_CLEANUP="yes"

# limits for timeline cleanup
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="10"
TIMELINE_LIMIT_DAILY="10"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="10"
TIMELINE_LIMIT_YEARLY="10"


# cleanup empty pre-post-pairs
EMPTY_PRE_POST_CLEANUP="yes"

# limits for empty pre-post-pair cleanup
EMPTY_PRE_POST_MIN_AGE="1800"

スナップショットを取る

スナップショットを取るのは簡単です。 snapper create とするだけです。 またデフォルトでは timeline スナップショットが一定時間間隔が取られるようになっています。 Portage にフックを仕込む と emerge でインストールするときに自動的にスナップショットを取るようにもできます。おすすめ。

snapper list で取ったスナップショットの一覧が出ます。 取ったスナップショットは /.snapshots から中身を確認できます。

システムをロールバックする

Snapper はもともと openSUSE 用に開発されたものであるため、Gentoo で snapper rollback するには少々コツが入ります。

準備1

/etc/fstab に以下の1行を入れておきます。これがないとスナップショット時点に戻る際に Snapper がスナップショットの一覧を取ってこれず、詰みます。

UUID=bd55811e-ea43-4f8d-8709-0058480b64bf            /.snapshots     btrfs           subvol=@/.snapshots     0 0

準備2

Snapper はデフォルトサブボリューム (btrfs subvolume get-default / で出てくるやつ) から起動する前提のようです。

しかし grub-mkconfig は気が利くので /boot/grub/grub.cfg に現在 / にマウントしているサブボリュームを rootflags=subvol=@ など書いてしまい、デフォルトサブボリュームの設定が効きません。なのでカーネル更新等でうっかり grub-mkconfig すると突然ロールバックがうまく行かなくなったりします。

対策として、/etc/grub.d/10_linux の subvol 設定部分を以下のようにコメントアウトしておきます。

case x"$GRUB_FS" in
#    xbtrfs)
#    rootsubvol="`make_system_path_relative_to_its_root /`"
#    rootsubvol="${rootsubvol#/}"
#    if [ "x${rootsubvol}" != x ]; then
#        GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
#    fi;;
    xzfs)
        rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
        bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
        LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs}"
        ;;
esac

復旧したい時点のスナップショットでブート

なるべく openSUSE 公式の手順 に沿って実行していきます。

まずロールバックしたいサブボリュームで起動する必要があります。 openSUSE ならブートメニューにスナップショットの一覧が出るのですが、Gentoo では出してくれないので自分でカーネルオプションを指定してブートします。

GRUBの画面で e キー入力して起動スクリプトのエディット画面に入り、カーネルのブートオプションに rootflags=subvol=@/.snapshots/<ロールバックしたいスナップショット番号>/snapshot をつけます。

linux        /vmlinuz-4.19.23-gentoo root=UUID=bd55811e-ea43-4f8d-8709-0058480b64bf ro  dobtrfs rootflags=subvol=@/.snapshots/144/snapshots

入力できたら F10 キーでブートします。

スナップショットをルートファイルシステムにすると、 / はリードオンリーでマウントされます。 ここでスナップショットの中身を確認してロールバックして問題ないか確認します。

ロールバック実行

snapper rollback を実行すると、2つのスナップショットが作成されます。

  1. ロールバック前のスナップショット(バックアップ)
  2. 現在ブートしているファイルシステムのスナップショット (復旧時点のスナップショットから派生した新しいサブボリューム)

Snapper はこのときできた 2. のスナップショットをデフォルトサブボリュームとして設定することで、ロールバックを実現します。 btrfs subvolume get-default / すると、デフォルトが snapper rollback したときに作られたサブボリュームにセットされていることがわかります。

あとは再起動してロールバック終わりです。

追記

スナップショット消してからなんか btrfs-(cleaner|transaction) がめっちゃCPU喰らう

Btrfs のクォータ実装の known issue です。

btrfs quota disable / すると収まります。 こういうお茶目さが btrfs の愛いところ。

social