Root on ZFS環境のUbuntu 24.04をリモートに保管しているスナップショットから復旧する

はじめに

みなさんはRoot on ZFSでインストールしたUbuntu Server、ロールバックしてますか?
同じマシンのロールバックならLiveDVDで起動してロールバックすればよいので、事前に手順を整備する必要はありますがそんなに困ることはありません。

ZFSを使っているという事はディスクをRAIDにしたりスナツプショットをリモートマシンにバックアップしていると思います。

ではお使いの機器が壊れた際にリモートに保管していたスナップショットからのロールバックは手順整備されているでしょうか?

この記事ではリモートマシンにバックアップしているファイルからZFSのルートパーティションと/bootのパーティションを復旧する手順をご説明します。

環境

・KVM配下の仮想マシンにRoot on ZFSでインストールしたUbuntu 24.04 LTS Server
・ディスクはシングルディスク(/dev/vda)、暗号化していない
・BIOSブート(さくらのVPSがUEFIブートの仮想マシンを作れない・・)
・zfsutils-linuxが動くLiveイメージ(Ubuntu ServerのLiveイメージから起動した場合はLive環境にapt-get install zfsutils-linuxするか、事前にCubic(Custom Ubuntu ISO Creator)でzfsutilsなど必要なツールを導入した起動イメージを作成する必要があります。)
・復旧したいマシンがインターネットに接続できると嬉しい(SSHで作業したりスナップショットのファイルをリモートからコピーしたりを復旧作業に必要なパッケージをapt-getで導入したりできる)

普段のバックアップ

“test1″にてZFSスナップショットを取得 → リモートの”test2” へバックアップ。

・rpoolのバックアップ
“zfs snapshot -r”でプール内のデータセット全てのスナップショットを取得し、”zfs send -R”でプール全体のレプリケーションをバックアップします。

・rpool全体のスナップショットをリモートのサーバにバックアップ
# zfs snapshot -r rpool@snapshot-all
# zfs send -R rpool@snapshot-all | gzip -c | ssh -i /home/ubuntu/.ssh/test2 ubuntu@test2 "cat > /some/good/place/test1-rpool@snapshot-all.gz"

・rpool全体の増分スナップショットをリモートのサーバにバックアップ
# zfs snapshot -r rpool@snapshot-i1
# zfs send -RI rpool@snapshot-all rpool@snapshot-i1 | gzip -c | ssh -i /home/ubuntu/.ssh/test2 ubuntu@test2 "cat > /some/good/place/test1-rpool@snapshot-i1.gz"

 

・bpoolのバックアップ
bpoolはtarでバックアップします。
bpoolを”zfs send” “zfs recv”でリストアしようとすると、なぜか一部のプロパティを取り込めないというエラーが発生してしまうため、リストア時はZFSのプールやデータセットを手動で作り直しtarを解凍して復旧します。

# cd /
# tar czf boot.tgz boot/
# scp -i /home/ubuntu/.ssh/test2 boot.tgz ubuntu@test2:/some/good/place/boot.tgz
# rm boot.tgz

リストア

基本的には、ZFSのプールを作り直す部分はOpenZFS公式のインストール手順と同じ、OSのインストールの代わりにレプリケーションを”zfs receive”し、receiveした環境にchrootしブートローダーを作り直す流れです。
この記事では、ディスクはシングル構成(/dev/vda)で暗号化は使用していませんが、mirrorやraidzの際はswapパーティションがmdadmを使用すること、ブートローダーは全てのディスクにインストールする必要があることを留意すれば大まかには同じ手順でリストア出来ると思います。


リモートの”サーバ: “test2″に保存したバックアップ → “test1″へのリストア

・UbuntuのLiveイメージで仮想マシンを起動

シェルを使うには・・
ServerのLiveイメージの場合は右上の[Help]→[Enter Shell]
Desktopの場合は[CTRL] + [ALT] + [F2]

 

・IPアドレスやGW設定
DHCPなどで自動的にネットワーク接続できない場合は手動でどうにかします。ここでの設定はLive環境限りで有効です。インターフェイス名やIPアドレス、デフォルトGWは必要に応じて読み替えてください。

# ip a
# ip link set up enp1s0
# ip a add 192.168.1.1/24 dev enp1s0
# ip route default via 192.168.1.254

# vi /etc/resolv.conf
→ nameserver 8.8.8.8 などとして上書き保存

 

・test1のLive環境にSSH出来るようにどうにかする(オプション)
test1がパブリックIPアドレスから直接アクセス可能なネットワークに存在する場合はあまりオススメできません。
# vi /etc/ssh/sshd_config
→PermitRootLogin yes
	
# passwd
→rootのパスワードを設定

 

・test2サーバのSSH秘密鍵インポート(オプション)
パスワード認証でログインするのであれば不要です。事前にtest2の秘密鍵と公開鍵をどこかに保存しておく必要があります。

# cd /root/.ssh
# touch test2
# touch test2.pub
# chmod 600 test2*

# vi test2
# vi test2.pub
→test2(秘密鍵)とtest2.pub(公開鍵)を編集

 

・ディスク消去
KVM上の仮想マシンであれば”/dev/vda”だと思いますが、環境に応じて読み替えてください。

確認
# fdisk -l /dev/vda

パーティション情報など消去
# systemctl stop zed
# swapoff --all
# wipefs -a /dev/vda
# blkdiscard -f /dev/vda
# sgdisk --zap-all /dev/vda

確認
# fdisk -l /dev/vda

 

・ZFSプールの作成

UEFIブート用の領域に512M、BIOSブート用の領域に1000K、スワップ領域に5GB、bpool(/boot)の領域に2GB、rpoolの領域は残り全部としています。基本的にはインストールした時と同じ設定ですが、参考リンクのドキュメントを参考に環境に応じて考えます。

参考 Step 2: Disk Formatting Ubuntu 22.04 Root on ZFS - OpenZFS Documentation
https://openzfs.github.io/openzfs-docs/Getting%20Started/Ubuntu/Ubuntu%2022.04%20Root%20on%20ZFS.html#step-2-disk-formatting 
bootloader partition
# sgdisk -n1:1M:+512M -t1:EF00 /dev/vda
# sgdisk -a1 -n5:24K:+1000K -t5:EF02 /dev/vda

swap
# sgdisk -n2:0:+5G -t2:8200 /dev/vda

boot pool
# sgdisk -n3:0:+2G -t3:BE00 /dev/vda

root pool
# sgdisk -n4:0:0 -t4:BF00 /dev/vda

Create the boot pool
# zpool create \
    -o ashift=12 \
    -o autotrim=on \
    -o cachefile=/etc/zfs/zpool.cache \
    -o compatibility=grub2 \
    -o feature@livelist=enabled \
    -o feature@zpool_checkpoint=enabled \
    -O devices=off \
    -O acltype=posixacl -O xattr=sa \
    -O compression=lz4 \
    -O normalization=formD \
    -O relatime=on \
    -O canmount=off -O mountpoint=/boot -R /mnt \
     bpool /dev/vda3

Create the root pool
# zpool create \
    -o ashift=12 \
    -o autotrim=on \
    -O acltype=posixacl -O xattr=sa -O dnodesize=auto \
    -O compression=lz4 \
    -O normalization=formD \
    -O relatime=on \
    -O canmount=off -O mountpoint=/ -R /mnt \
     rpool /dev/vda4

 

・rpoolをレプリケートから復旧

rpool全体のレプリケーションを復旧
# ssh -C -i /root/.ssh/test2 ubuntu@test2 "cat /some/good/place/test1-rpool@snapshot-all.gz" | gzip -d | zfs recv -F -d rpool

rpoolレプリケーションの差分バックアップもあればそれも復旧
# ssh -C -i /root/.ssh/test2 ubuntu@test2 "cat /some/good/place/test1-rpool@snapshot-i1.gz" | gzip -d | zfs recv -F -d rpool
	
rpoolを/mntに仮マウントする
# ls -alh /mnt
# zpool export rpool
# zpool import -f -R /mnt rpool

 

・bpoolの復旧

bpoolのデータセット作成
# zfs create -o canmount=off -o mountpoint=none bpool/BOOT
# zfs create -o mountpoint=/boot bpool/BOOT/ubuntu

bpoolを/mntに仮マウントする
# zpool export bpool
# zpool import -f -R /mnt bpool

どうにかしてboot.tgzをコピーしてきてbpoolの領域に解凍する
# scp -i /root/.ssh/test2 ubuntu@test2:/some/good/place/boot.tgz /mnt/boot.tgz 
# cd /mnt
# tar xvzf boot.tgz
# ls -alh /mnt/boot/

 

・zsysが作ったdatasetを消す(オプション)
grub再構築の時にrpool/ROOT/配下のデータセットを探して全部ブートメニューに並べられてしまう・・
Ubuntuをインストールした時にrpool/ROOT配下に自分で作成したデータセットを残して消します。
"vj4k88"は環境にあわせて読み替えてください。
# zfs destroy -R rpool/ROOT/ubuntu_vj4k88

※補足
OpenZFSのドキュメントStep3の2.の手順では、urandomで作った適当な文字列をUUIDとしてデータセットに使用しますが、zsysが後から作るデータセットも同様の命名規則のため、リストア時に使用中だったルートのデータセットを”zfs list”で確認しても分かりづらくなってしまいます。
そのため、インストール時はurandomではなく分かりやすい名前を手動で付与するか、”_$UUID”を付与しない”rpool/ROOT/ubuntu”という名前にしておけばこういう時に混ざらずに済みます。

OpenZFSのドキュメント
# UUID=$(dd if=/dev/urandom bs=1 count=100 2>/dev/null |
     tr -dc 'a-z0-9' | cut -c-6)
 
# zfs create -o mountpoint=/ \
    -o com.ubuntu.zsys:bootfs=yes \
    -o com.ubuntu.zsys:last-used=$(date +%s) rpool/ROOT/ubuntu_$UUID


本環境
# zfs create -o mountpoint=/ \
    -o com.ubuntu.zsys:bootfs=yes \
    -o com.ubuntu.zsys:last-used=$(date +%s) rpool/ROOT/ubuntu
データセットが混ざってしまって見つからない場合は、"/mnt/boot/grub/grub.cfg"の「menuentry 'Ubuntu 24.04 LTS'」から始まる「linux "/BOOT/ubuntu@/vmlinuz-asdfqwerty-generic" root=ZFS="rpool/ROOT/ubuntu"~」の設定を確認すると見つかります。
(略)
menuentry 'Ubuntu 24.04 LTS' --class ubuntu --class gnu-linux --class gnu --class os ${menuentry_id_option} 'gnulinux-rpool/ROOT/ubuntu-6.8.0-39-generic' {
        recordfail
        load_video
        gfxmode ${linux_gfx_mode}
        insmod gzio
        if [ "${grub_platform}" = xen ]; then insmod xzio; insmod lzopio; fi
        insmod part_gpt
        insmod zfs
        search --no-floppy --fs-uuid --set=root 0be2b09e124e02c0
        linux   "/BOOT/ubuntu@/vmlinuz-6.8.0-39-generic" root=ZFS="rpool/ROOT/ubuntu" ro init_on_alloc=0  ←この行
        initrd  "/BOOT/ubuntu@/initrd.img-6.8.0-39-generic"
}
(略)

 

・zfs recvした環境へとchrootする

# mount --make-private --rbind /dev /mnt/dev
# mount --make-private --rbind /proc /mnt/proc
# mount --make-private --rbind /sys /mnt/sys
# chroot /mnt

 

・SWAP設定

# mkswap -f /dev/vda2
# swapon -a

 

・initramfs再構築(任意)
起動時にinitramfsが”Begin: Running /scripts/local-premount”で30秒待ち状態になる対策
https://askubuntu.com/questions/1034359/boot-hangs-for-30-seconds-at-begin-running-scripts-local-premount

# vi /etc/initramfs-tools/conf.d/resume
→新規作成しRESUME=noneと書いて上書き

# update-initramfs -u

 

・GRUB再構築


UEFIブート用のESPパーティション作成
# mkdosfs -F 32 -s 1 -n EFI /dev/vda1
# mount /boot/efi

Put /boot/grub on the EFI System Partition:
# mkdir /boot/efi/grub /boot/grub

grubの再構築
# mount /boot/grub
# update-grub

BIOS BOOTの場合
# grub-install /dev/vda

UEFI BOOTの場合
# grub-install --target=x86_64-efi --efi-directory=/boot/efi \
	    --bootloader-id=ubuntu --recheck --no-floppy

grubがZFSを認識しているか確認
# grub-probe /boot
zfs   ←認識していなければ"unknown filesystem"と出力される

 

・再起動

chroot環境から離脱
# exit

リブート
# reboot
→仮想マシンのDVDイメージを取り出してEnterキーを押す

 

・起動したら

リカバリが完了していれば無事に起動するはずですが、初回の起動はinitramfsがzpool import出来ずに止まります。(Failed to import pool 'rpool'. Manually import the pool and exit.とメッセージが出る)
(initramfs) zpool import -f rpool
(initramfs) exit

 

理由は分かりませんが、強制的にメンテナンスモードに入ってしまうことがあります。毎回ではなくきちんとmulti-user.targetで起動してくることもあります・・
メンテナンスモードで起動した際に「(or press Control-D to continue): 」でCTRL+Dを押しても「default.target is not inactive. Please review the default.target settings. Fallback to the single-user shell.」 とメッセージが出力されます。

確認
# systemctl get-default

デフォルトのターゲットを「multi-user」に変更
# systemctl set-default multi-user

確認
# systemctl get-default	

今のセッション限りで起動ターゲットを変更する
# systemctl isolate multi-user

起動ターゲットを設定出来たらrebootする
# reboot

 

bpoolをマウントしないと”/boot”が見えなくなるので・・

# zpool import -f bpool

 

ここまで出来たらサービスが無事動いているかを確認していきましょう。

2024/9/19追記

起動ターゲットが狂ってメンテナンスモードで起動してしまうのはbpool(/boot/)をマウントしていない状態で発生します。bpoolをマウントすれば次回からはいつも通りmulti-user.targetで起動するはずです。
また、rsyslogなどmulti-user.targetで起動するはずだったサービスがOS再起動後も止まったままになっていることがあります。
レプリケート前後で”systemctl list-dependencies”を確認して動いていてほしいサービスに差異が無いかを確認しましょう。