Ubuntuをzfsルートにしたときのメモ
ただのzfsルートだとUbuntu 24.04のインストーラでできるが、raidzもやろうとするとできない。
インストーラが複数メディアへのインストールを想定していないよう。
なのでOpenZFSのドキュメントを見ながら手動でUbuntuをインストールした。
Ubuntu 22.04 Root on ZFS — OpenZFS documentation
debootstrapから構築したのいつぶりだろう。
基本的にはドキュメント通りにやっていけばよい。英文を読むとRAIDを組むときにコマンドをいじる部分が書いてある。自分はzfsのネイティブ暗号化で構築した。
成功すると↑のようにログインより前にディスクの暗号鍵を訊かれる
ただしブートシステムがlegacyかUEFIか、シングルディスクかマルチディスクかで選択になるコマンドを間違って叩くと破滅する(一敗)。
どれがクリティカルだったのか分からないが、ミスって実行したのは2つ。
1つは /boot もraidに入れてるのにシングルディスク向けのコマンドを叩いてしまった点
mkdir /boot/efi/grub /boot/grub
echo /boot/efi/grub /boot/grub none defaults,bind 0 0 >> /etc/fstab
mount /boot/grub
もう1つはUEFIにしたいのにlegacyのコマンドを叩いてしまった点。
apt install --yes grub-pc linux-image-generic zfs-initramfs zsys
どっちも元に戻したつもりだったが再起動したら動かなかった。多分grub-pcをインストールするときにMBRを書き換えてしまったとかそういう系だと思う。
あ、あと手順の中で再起動するときにファイルシステムをアンマウントするコマンドが動かなかった
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | \
xargs -i{} umount -lf {}
zpool export -a
exportでrpoolがbusyって言われる。シェルのカレントディレクトリが/mnt/に入ってて使用中判定されたかなと思ったけどそれでもなかった。
よくわからないのでそのまま再起動したらinitramfsに落ちた。exportできてないのでimportできなかったらしい。強制importが必要。initramfsでもzpoolが使えるので zpool import -alf
かそんな感じのコマンドを叩いたら直った。
ほかにハマった点だとネットワークの設定が上手くいかずに再起動後にaptをインストールできなかった。
仕方ないのでインストールメディアからchrootで入って作業した。その状態でubuntu-desktopさえ入れてしまえばNetworkManagerが入るのでネットワーク設定しなくても自動で管理してくれる。
でもインストールメディアからchrootで入るとDNSが解決できなかったので困った。一旦chrootする前にpingなどでarchive.ubuntu.comのIPアドレスを確かめてから/etc/apt/souces.list.d/ubuntu.sourcesの中の archive.ubuntu.comをIPアドレスに置換した。IPv6はURLでは http://[..]/...
のように[]
で囲んで書く。そうしないとアドレス中の :
がポート番号(など)の指定と区別がつかない。
で、btrfsでやってたバックアップの仕組みをzfsでも再現しようとして調べ物をした。
やっていたのは2つ。
- 1時間ごとにスナップショットをとる
- 1週間ごとに最新のスナップショットからtarを作って~/Dropboxに放り込む
これができるかを調べた。
後者はスナップショットの中身がファイルシステムとして見えればあとはどうにかなる。
で、実際にそれはできる。
一旦ホームディレクトリのdatasetに対してlistsnapshotsをonにしておく。
zpool set listsnapshots=on rpool/USERDATA/name_UUID
すると ~/.zfs
からアクセスできるようになる。
$ ls ~/.zfs
shares snapshot
$ ls ~/.zfs/snapshot
autozsys_0d9if2 autozsys_65gxqu autozsys_blltdr autozsys_itlpj1 autozsys_mbrnwy autozsys_t1o84s autozsys_ys2zk3 test
autozsys_38ce9m autozsys_8030et autozsys_f4jwq4 autozsys_ivitc6 autozsys_mqoe9w autozsys_ulzf75 autozsys_zfpc9b
autozsys_3qv38f autozsys_aavh0k autozsys_f8vell autozsys_jsun4n autozsys_oq1m7f autozsys_vaj3ma autozsys_zqa5ba
autozsys_55tp0o autozsys_b61cav autozsys_i31d62 autozsys_len7sx autozsys_p8r8dm autozsys_wuvhse autozsys_zt5hxj
この autozsys_xxx
とついているのがスナップショットの名前で、さらにこの中を ls
するとスナップショットをとったときのツリーがそのまま見える。これにて後者は解決。
不思議なことに ls
にはこの .zfs
はでてこない。まあ、出てきても困るんだけど、同名ディレクトリとか作ったらどうなるんだろうね。
$ ls -a ~/ | grep zfs
$
で、↑で出てきた autozsys_xxx
なんだけど、そもそもUbuntu側で勝手にスナップショットを撮ってるみたい。zsysという仕組み。
システムディレクトリに関してはブートに成功したりaptとかでシステムに変更を加えたりのタイミングで、ユーザデータに関しては1時間ごとにスナップショットを撮っている。
この仕組みはsystemdのタイマーで設定されている。
$ systemctl --user list-timers zsys-user-savestate
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2024-04-28 10:08:11 JST 32s Sun 2024-04-28 09:08:10 JST 59min ago zsys-user-savestate.timer zsys-user-savestate.service
1 timers listed.
Pass --all to see loaded but inactive timers, too.
$ ls /etc/systemd/user/timers.target.wants
launchpadlib-cache-clean.timer zsys-user-savestate.timer
[Unit]
Description=Save current user state periodically
# We can't run it in a container
ConditionVirtualization=!container
[Service]
Type=oneshot
ExecStart=/sbin/zsysctl state save
[Unit]
Description=Save current user state periodically
ConditionUser=!@system
[Timer]
OnStartupSec=1min
OnUnitActiveSec=1h
[Install]
WantedBy=timers.targe
そしてzsysの設定ファイルはデフォルトでは存在しない。バイナリにこの設定が埋め込まれている。
設定を変えたかったらコピペして /etc/zsys.conf
に置いていじることになるが、デフォルト設定が変わったときの挙動とかで影響受けそうだし明示的に置いといてほしかったなという思いも。
とりあえず、1時間ごとにスナップショットをとる仕組みは存在するので考えることは減った。
あとは
- 最新のスナップショットを取得したい
-
~/Dropbox
はzsysに管理してほしくない
をやっていきたい。前者はなんか頑張ればできそう。
後者はzsysの仕組みの理解が必要。
zsysは解説ブログがある。↓の記事から始まる一連の記事。
その中でもこの記事が多分欲しかった情報。
ROOT BOOT USERDATA下にあるデータセットはzsysが管理するので、管理から外したければそれ以外に置けばいい。記事中でPersistent datasetsと呼ばれているもの。
今まだDropboxが同期中で作業できないので後で試す。
因みに最新のスナップショットを取得したいについてもある程度は答えてくれていた。
プロパティにcreationがあるのでこれをみれば最新が分かる。問題はどうやってシェルスクリプトで取得するかだけどな!
creationが日本語で表示されるのはロケールの問題なんだろうと思ったけど LANG=C
でも日本語だった。多分これ値を入れる時点で日本語で入れてるな
$ LANG=C zfs get creation rpool/USERDATA/shun_wfj4mu@autozsys_0d9if2
NAME PROPERTY VALUE SOURCE
rpool/USERDATA/shun_wfj4mu@autozsys_0d9if2 creation 日 4月 28 2:12 2024 -
いや、さすがにそんなことなかった。zfs getに-pつけると数値で出してくれた
$ zfs get -p creation rpool/USERDATA/shun_wfj4mu@autozsys_0d9if2
NAME PROPERTY VALUE SOURCE
rpool/USERDATA/shun_wfj4mu@autozsys_0d9if2 creation 1714237933 -
zfs list
に -S
を渡すとその値でソートしてくれるからそれ使えば最新のを取得はできそう。
$ zfs list -S creation -o name | grep -m1 "rpool/USERDATA/${USER}_.*@autozsys_.*"
rpool/USERDATA/shun_wfj4mu@autozsys_gcfdtp
Dropboxの同期が終わったのでデータセットを別のものにしてみた。
これを先にやってからDropboxの同期すればよかったな。
最初にこれをやったら思ったとおりにならなかった。
$ sudo zfs create -p -ocanmount=off rpool/home/shun/Dropbox
$ sudo zfs set canmount=on rpool/home/shun/Dropbox
/home/shun
に rpool/home/shun
と rpool/USERDATA/shun
両方がマウントしようとしてしまうし、 rpool/home/shun/Dropbox
は rpool/home/shun
にしかマウントしなかった。 ``rpool/home/shun` はcanmount=offのはずなんだけどなあ。結局上で作ったやつは全部destroyして以下で作り直した。
$ sudo zfs create -omountpoint=/home/shun/Dropbox rpool/home-shun-Dropbox
既にディレクトリがある状態なのでちょっと怪しいかも
$ mv Dropbox Dropbox.bk
したあとに新しく作った Dropbox
のマウントポイントが生えてきた。
そのあとは mv
した。
$ mv Dropbox.bk/* Dropbox
autozsysのスナップショットのせいでディスクスペースが足りなくなったので先にスナップショットは削除しといた方がいい。
ディスクスペースで詰まったあとはmv先にディレクトリがあったりしてmvできなくなるのでrsyncを使った。
$ rsync -r --remove-source-files --verbose -p ./Dropbox2/ Dropbox/
ただ、これだとmtimeとかが上書きされてしまうので -a
オプションをつけるべきだった。
最後、この記事の内容をzfsに移植する。
最新のスナップショットからtarを作るというは骨子は変わらないのでそこまで変更はない。
#!/bin/sh
# templated by http://qiita.com/blackenedgold/items/c9e60e089974392878c8
usage() {
cat <<HELP
NAME:
$0 -- backup home
SYNOPSIS:
$0 [-h|--help]
$0 [--verbose]
DESCRIPTION:
Backup home from the latest snapshot. It logs to $LOG_FILE
-h --help Print this help.
--verbose Enables verbose mode.
HELP
}
main() {
SCRIPT_DIR="$(cd $(dirname "$0"); pwd)"
while [ $# -gt 0 ]; do
case "$1" in
--help) usage; exit 0;;
--verbose) set -x; shift;;
--) shift; break;;
-*)
OPTIND=1
while getopts h OPT "$1"; do
case "$OPT" in
h) usage; exit 0;;
esac
done
shift
;;
*) break;;
esac
done
NAME=shun
mv -f /home/${NAME}/Dropbox/backup/home.tar.xz /home/${NAME}/Dropbox/backup/home.old.tar.xz
mv -f /home/${NAME}/Dropbox/backup/home.tar.xz.sha1 /home/${NAME}/Dropbox/backup/home.old.tar.xz.sha1
chown -f ${NAME}:${NAME} /home/${NAME}/Dropbox/backup/home.old.tar.xz /home/${NAME}/Dropbox/backup/home.old.tar.xz.sha1
latest_snapshot="$(zfs list -S creation -o name | grep -m1 "rpool/USERDATA/${NAME}_.*@autozsys_.*" | grep -o 'autozsys.*')"
nice tar cvf /home/${NAME}/Dropbox/backup/home.tar.xz \
--sparse \
--use-compress-prog=pixz \
-p --xattrs \
--exclude=./Dropbox \
--exclude=./.cache \
-C "/home/${NAME}/.zfs/snapshot/$latest_snapshot/" \
.
sha1sum /home/${NAME}/Dropbox/backup/home.tar.xz > /home/${NAME}/Dropbox/backup/home.tar.xz.sha1
chown ${NAME}:${NAME} /home/${NAME}/Dropbox/backup/home.tar.xz /home/${NAME}/Dropbox/backup/home.tar.xz.sha1
}
set -e
export PATH=/usr/bin:/usr/sbin
LOG_FILE=/var/log/backup-home.log
start_time="$(date +%s)"
main "$@" 2>&1 > $LOG_FILE
end_time="$(date +%s)"
echo "$(($end_time - $start_time)) seconds" >> $LOG_FILE
あとはlogrotateも書く。zfsを作るときにcompressionをonにしているのでcompressはつけない。
/var/log/backup-home.log {
weekly
missingok
rotate 7
notifempty
copytruncate
}
ところで前まで気付いてなくてやられたんだけど、tarのexcludeは部分一致っぽい。
こういうディレクトリを用意して、
$ tree -a dir
dir
├── .cache
├── hoge
│ └── .cache
└── test
dir/.cache
を除外するつもりで --exclude=.cache
と書いていた。しかしこれだと hoge/.cache
まで除外されてしまう。
$ tar cvf dir.tar --exclude=.cache -C dir .
./
./hoge/
./test
正しくは ./.cache
だった。
$ tar cvf dir.tar --exclude=./.cache -C dir .
./
./hoge/
./hoge/.cache
./test
ところでzfsにしてからやたらメモリ使用量が多い。
slabtop -sc
でトップに zfs_znode_cache
が上にくるから恐らくzfsのキャッシュのせいだろう。
多分これでトピックは終わりかな。次あるとしたらPC移行時だろうけど、そのときはまた別スクラップを作ろう。