Ubuntu Weekly Recipe

第597回 UbuntuのルートファイルシステムをZFSにしてみる

この記事を読むのに必要な時間:およそ 13 分

スナップショットとロールバック

ZFSの便利な機能のひとつが「スナップショット」「ロールバック」です。openSUSEのBtrfs/Snapper/YaSTを組み合わせた「何かあったらロールバックする」仕組みが羨ましくて,枕を涙で濡らした夜もあるでしょう。ZFSとコマンドを駆使すれば,Ubuntuでもこの仕組みを実現できる可能性がないとは言い切れない状態にはなんとか持っていけます※8⁠。

※8
かなり言葉を濁していますが,要するにSnapperほどお手軽ではないものの,注意深く使えばそれなりに使えるだろうと考えています。

たとえばUbuntuのインストール直後はアップグレードが必要なパッケージがたくさん溜まっていますよね。アップグレード前にスナップショットを取って,アップグレードした上で,ロールバックしてみましょう。

$ apt list --upgradable
(中略)
uno-libs3/eoan-updates 6.3.3-0ubuntu0.19.10.1 amd64 [6.3.2-0ubuntu2 からアップグレード可]
ure/eoan-updates 6.3.3-0ubuntu0.19.10.1 amd64 [6.3.2-0ubuntu2 からアップグレード可]
whoopsie/eoan-updates,eoan-security 0.2.66ubuntu0.3 amd64 [0.2.66 からアップグレード可]
zfs-zed/eoan-updates 0.8.1-1ubuntu14.1 amd64 [0.8.1-1ubuntu14 からアップグレード可]

スナップショットはzfs snapshot データセット名@スナップショット名です。今回は日時を付けてみます。

$ sudo zfs snapshot -r rpool@`date +%Y%m%d%H%M%S`

上記のように-rオプションを付けることで,下位のデータセットも再帰的にたどってスナップショットを取ってくれます。今回はrpool以下すべてのスナップショットを取っていますが,本来はもう少し制限すべきです。たとえばopenSUSEは/var/logや/var/runなどはスナップショットの除外対象にしています

作成されたスナップショットはzfs listコマンドに-t snapshotオプションを付けることで確認できます。

$ zfs list -t snapshot
NAME                                                              USED  AVAIL     REFER  MOUNTPOINT
rpool@20191130232337                                                0B      -       96K  -
rpool/ROOT@20191130232337                                           0B      -       96K  -
rpool/ROOT/ubuntu_08z65m@20191130232337                             0B      -     3.49G  -
rpool/ROOT/ubuntu_08z65m/srv@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr/local@20191130232337                   0B      -      104K  -
rpool/ROOT/ubuntu_08z65m/var@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/games@20191130232337                   0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib@20191130232337                     0B      -      379M  -
rpool/ROOT/ubuntu_08z65m/var/lib/AccountServices@20191130232337     0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib/NetworkManager@20191130232337      0B      -      136K  -
rpool/ROOT/ubuntu_08z65m/var/lib/apt@20191130232337                 0B      -     77.5M  -
rpool/ROOT/ubuntu_08z65m/var/lib/dpkg@20191130232337                0B      -     35.6M  -
rpool/ROOT/ubuntu_08z65m/var/log@20191130232337                   692K      -     2.07M  -
rpool/ROOT/ubuntu_08z65m/var/mail@20191130232337                    0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/snap@20191130232337                    0B      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/spool@20191130232337                   0B      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/www@20191130232337                     0B      -       96K  -
rpool/USERDATA@20191130232337                                       0B      -       96K  -
rpool/USERDATA/root_e8xd45@20191130232337                           0B      -      112K  -
rpool/USERDATA/shibata_e8xd45@20191130232337                        0B      -     3.50M  -

USEDがほぼ0Bになっていますね。ZFSはデータセット単位のCopy-on-Writeに対応しているので※9⁠,スナップショットを作成したタイミングではストレージを消費しません。

※9
Btrfsと異なりファイル単位のCopy-on-Writeには対応していません。よってcpコマンドに--reflinkオプションを付加しても残念ながら速くなりません。

アップグレード前のスナップショットを取得したので,次はアップグレードしてみましょう。

$ sudo apt full-upgrade -y
$ apt list --upgradable
一覧表示... 完了

無事にアップグレードが完了しましたね。この状態でスナップショットを表示してみましょう。

$ zfs list -t snapshot
NAME                                                              USED  AVAIL     REFER  MOUNTPOINT
rpool@20191130232337                                                0B      -       96K  -
rpool/ROOT@20191130232337                                           0B      -       96K  -
rpool/ROOT/ubuntu_08z65m@20191130232337                           753M      -     3.49G  -
rpool/ROOT/ubuntu_08z65m/srv@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr/local@20191130232337                  56K      -      104K  -
rpool/ROOT/ubuntu_08z65m/var@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/games@20191130232337                   0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib@20191130232337                  21.2M      -      379M  -
rpool/ROOT/ubuntu_08z65m/var/lib/AccountServices@20191130232337     0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib/NetworkManager@20191130232337    100K      -      136K  -
rpool/ROOT/ubuntu_08z65m/var/lib/apt@20191130232337               516K      -     77.5M  -
rpool/ROOT/ubuntu_08z65m/var/lib/dpkg@20191130232337             8.36M      -     35.6M  -
rpool/ROOT/ubuntu_08z65m/var/log@20191130232337                  1.01M      -     2.07M  -
rpool/ROOT/ubuntu_08z65m/var/mail@20191130232337                    0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/snap@20191130232337                   72K      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/spool@20191130232337                  64K      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/www@20191130232337                     0B      -       96K  -
rpool/USERDATA@20191130232337                                       0B      -       96K  -
rpool/USERDATA/root_e8xd45@20191130232337                           0B      -      112K  -
rpool/USERDATA/shibata_e8xd45@20191130232337                      988K      -     3.50M  -

主にルートディレクトリのデータセット(rpool/ROOT/ubuntu_08z65m)を中心にUSEDがぐっと増えました。

スナップショット同士,もしくはファイルシステムとスナップショット間で差分を確認することも可能です。

$ sudo zfs diff rpool/ROOT/ubuntu_08z65m@20191130232337 rpool/ROOT/ubuntu_08z65m
M       /etc
M       /etc/apparmor.d
M       /etc/apport
M       /etc/bash_completion.d
M       /etc/cron.daily
M       /etc/default
M       /tmp
M       /var/cache/apt
M       /var/cache/apt/archives/partial
M       /etc/firefox
(後略)

今回は多くのパッケージがアップグレードされてしまったため,差分がかなり多くなっていますが,日常のアップグレード程度なら比較しやすいでしょう。

次にアップグレード前,つまりスナップショット取った環境にロールバックします。注意すべき点として,現在のZFSには再帰的にロールバックする仕組みが存在しません。よってスクリプトで存在するスナップショットをすべてロールバックすることにします。

$ for i in `zfs list -t snapshot | grep 20191130232337 | awk '{print $1}'`; do
sudo zfs rollback -r -R -f $i;
done

zfs rollbackに付加しているオプションはそれぞれ次のような意味になります。

  • -r⁠:指定したスナップショットより新しいスナップショットとブックマークを削除する
  • -R⁠:指定したスナップショットより新しいスナップショットとブックマークとそれらのクローンを削除する
  • -f⁠:クローンされたデータセットがマウントされていたら,削除する前にアンマウントする

zfs rollback「最も新しいスナップショット」以外にロールバックしようとするとエラーになります。それを無視して強制的にロールバックするのが-rオプションです。当然,ロールバック先より新しいスナップショットは意味をなさなくなるので,破棄されます。

zfs cloneコマンドを使うと,任意のデータセットを別名でクローンできます。クローンしたデータセットはクローン元と親子関係が発生するため,ロールバックによってクローン元が破棄されると困ります。クローンも一緒に削除してしまうのが-Rオプションです。

ロールバックが成功したら,スナップショットの様子を見ていましょう。

$ zfs list -t snapshot
NAME                                                              USED  AVAIL     REFER  MOUNTPOINT
rpool@20191130232337                                                0B      -       96K  -
rpool/ROOT@20191130232337                                           0B      -       96K  -
rpool/ROOT/ubuntu_08z65m@20191130232337                             8K      -     3.49G  -
rpool/ROOT/ubuntu_08z65m/srv@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/usr/local@20191130232337                   0B      -      104K  -
rpool/ROOT/ubuntu_08z65m/var@20191130232337                         0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/games@20191130232337                   0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib@20191130232337                     8K      -      379M  -
rpool/ROOT/ubuntu_08z65m/var/lib/AccountServices@20191130232337     0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/lib/NetworkManager@20191130232337      8K      -      136K  -
rpool/ROOT/ubuntu_08z65m/var/lib/apt@20191130232337                 0B      -     77.5M  -
rpool/ROOT/ubuntu_08z65m/var/lib/dpkg@20191130232337                8K      -     35.6M  -
rpool/ROOT/ubuntu_08z65m/var/log@20191130232337                    80K      -     2.07M  -
rpool/ROOT/ubuntu_08z65m/var/mail@20191130232337                    0B      -       96K  -
rpool/ROOT/ubuntu_08z65m/var/snap@20191130232337                    0B      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/spool@20191130232337                   0B      -      112K  -
rpool/ROOT/ubuntu_08z65m/var/www@20191130232337                     0B      -       96K  -
rpool/USERDATA@20191130232337                                       0B      -       96K  -
rpool/USERDATA/root_e8xd45@20191130232337                           0B      -      112K  -
rpool/USERDATA/shibata_e8xd45@20191130232337                        8K      -     3.50M  -

USEDがスナップショット前と同じくらいに減っている,つまり現在の状態とスナップショットの状態にほぼ差がなくなっていることがわかります。

さらにapt listコマンドを実行すると,⁠アップグレード前」に戻っていることがわかるはずです。

$ apt list --upgradable
(中略)
uno-libs3/eoan-updates 6.3.3-0ubuntu0.19.10.1 amd64 [6.3.2-0ubuntu2 からアップグレード可]
ure/eoan-updates 6.3.3-0ubuntu0.19.10.1 amd64 [6.3.2-0ubuntu2 からアップグレード可]
whoopsie/eoan-updates,eoan-security 0.2.66ubuntu0.3 amd64 [0.2.66 からアップグレード可]
zfs-zed/eoan-updates 0.8.1-1ubuntu14.1 amd64 [0.8.1-1ubuntu14 からアップグレード可]

今回はシステム全体をロールバックしましたが,前述したように本来はデータセットごとにスナップショットを取るか,ロールバックするかを判断すべきです。

zsnapdパッケージとして提供されているzfs-snap-managerなどのように,自動的にスナップショットを作成するソフトウェアも存在します。スナップショット・ロールバックを本格的に使う際は,これらの自動化ツールを活用すると良いでしょう。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。