Ubuntu Weekly Recipe

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

Ubuntu 19.10ではデスクトップのインストール時にファイルシステムとしてZFSを選択できる機能が実験的に導入されました。そこで今回はそのZFSを実際に使ってみましょう。

なぜZFSをデスクトップに?

Ubuntu Weekly Topicsの2019年8月9日号でも紹介しているように、Ubuntu 19.10からインストール時に「デスクトップ」のルートファイルシステムとして、ZFS on LinuxのZFSを選択できるようになりました[1]⁠。これはあくまで実験的な機能であり、ZFS特有の機能がすぐに活用できる状態になるわけでもありませんし、環境や使い方によっては不安定になる可能性もあります。将来の可能性に向けたお試し機能であることを十分に理解しておいてください。

ZFSそのものはUbuntu 15.10の頃から利用可能でした。その後、19.10に向けて次の対応が行われます。

  • GRUBからZFSなルートファイルシステムを簡単に選択できるようにする[2]
  • LinuxカーネルのパッケージにZFSのカーネルモジュールを同梱する[3]
  • インストーラー(Ubiquity)でZFSなルートファイルシステムを作成できるようにする

これらの対応により、デスクトップ環境において「インストール直後のルートファイルシステム」としてZFSを指定できるようになったのです。

UbuntuとZFSの関係やライセンスに関する話は、現在発売中のSoftware Design 2019年12月号に掲載されている「Ubuntu Monthly Report」『⁠⁠115】Ubuntu 19.10とZFS』にて、あわしろいくや氏がいろいろと言及していますので、そちらを参照してください。

ZFSは非常にメモリを使うファイルシステムです。ZFS on LinuxのFAQにもHardware Requirementsとして、⁠ECCの利用を強く推奨」⁠最良のパフォーマンスを得るためには8GB以上のメモリを。2GB以下でもきちんと動きはするが、重複削除(dedup)機能を使うより多くのメモリが必要になる」とあります。

最近ではPC用途でも8GB以上のメモリは一般的になったものの、ECC付きとなるとまだワークステーションやサーバークラスのマシンになってしまいます。ましてや、ZFSの強みはファイルシステムとしてRAIDやスクラビングによる故障検知、そして障害発生時のホットスワップなど、⁠複数のストレージデバイス」が前提になっている機能が大半です。

よってデスクトップやノートPCで「ZFS」と言われても、一般的なユーザー[4]には機能の使い道がなく、オーバーキル気味な印象を受けます。とは言えスナップショット・リストアや暗号化など個人でも使いやすい機能もたくさん存在するため、今後Ubuntuとしてどういうユースケースを提案していくのか楽しみです[5]⁠。

とにもかくにも食わず嫌いは良くありません。今回は実際にZFSを使っていろいろ試してみることにします。あらかじめ仮想マシンか何かに、Ubuntu 19.10をZFSでインストールしておいてください[6]⁠。

図1 インストーラーで「実験的機能」を選択する勇気
画像

インストール直後の状態確認

まずはインストール直後の状態を確認してみましょう。最初にパーティションレイアウトを見てみます。

$ sudo parted /dev/vda unit MiB print
モデル: Virtio Block Device (virtblk)
ディスク /dev/vda: 20480MiB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: gpt
ディスクフラグ:

番号  開始     終了      サイズ    ファイルシステム  名前                  フラグ
 1    1.00MiB  513MiB    512MiB    fat32             EFI System Partition  boot, esp
 2    513MiB   563MiB    50.0MiB   ext4
 3    563MiB   1486MiB   923MiB    linux-swap(v1)
 4    1486MiB  3534MiB   2048MiB   zfs
 5    3534MiB  20480MiB  16946MiB  zfs

最初のパーティションはEFI System Partition(ESP)です。今回はUEFIファームウェアを利用したため、EFI OSローダー(GRUBやセキュアブート用のshimなど)を保存するためのESPが作られています。ESPはFAT12/16/32のいずれかである必要があるため、ZFSで作ることはできません。

第2パーティションはGRUBの設定ファイルやモジュール類をインストールする領域です。GRUB自体はinsmod zfsを実行して初めてZFSの内容を確認できます。つまりGRUBがカーネルやinitramfsをZFSからロードするためには、GRUBのモジュール類はGRUBが標準でサポートしているファイルシステムに置かれている必要があるのです。このための領域をext4として作成しています。

ただしこれはESPと分離する意味があまりないため、Ubuntu 20.04 LTSからはESPに統合されます。結果としてBIOSモードでも(UEFIが動かない環境でも⁠⁠、ESP相当のGRUBモジュールがインストールされるパーティションが作られます。

第3パーティションは見てのとおりスワップパーティションです。Ubuntuは17.04からスワップパーティションを作らずに、ファイルシステム上のスワップファイルを利用することになりました。ZFSはスワップファイルを使えないため、明示的にスワップパーティションを作っています。ちなみにZFS上にスワップ用のボリュームを作ることも可能です[7]⁠。この部分は将来的に変わるかもしれません。

第4パーティションと第5パーティションがZFS用のパーティションです。前者が起動カーネルなどを保存する「/boot」用、後者がそれ以外のデータを保存するルートファイルシステム用に用意されています。

ちなみにリリースノートで指摘されていたインストーラーがストレージをフォーマットする際にZFSがなくext4だけ表示される問題は、パーティションフォーマットの確認ダイアログで、第3パーティション以降が表示されないことを意味しています。

図2 vda上にZFSを作ったのにESPとext4しか表示されない
画像

ZFSパーティションのレイアウトを計算するのは、このパーティション確認ダイアログが表示されるタイミングよりも後ろだったのです。つまり第1と第2しか表示されないので、⁠ZFSを選択したのにext4を作ろうとしている」と勘違いするような表記になっていました。これがリリースノートで「動作としては正しいのですが、利用者に混乱を引き起こす表示」と記載された理由となります。

Ubuntu 20.04 LTSのインストーラーでは、ダイアログが表示されるタイミングでZFSのパーティション情報も表示するように修正されています。

次に/etc/fstabがどうなっているか確認してみましょう。

$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# /boot/efi was on /dev/vda1 during installation
UUID=8977-6786  /boot/efi       vfat    umask=0077      0       1
UUID=0a9b50b8-7a66-4ae1-8a7d-35796345aca0       /boot/grub      ext4    errors=remount-ro       0       1
UUID=892fa4dc-22ef-49fa-9340-2676ce19cbd7       none    swap    discard 0       0

あれ、ルートファイルシステムのエントリーが存在しませんね。実はZFSにはそのファイルシステム情報の中に「マウントポイント」を記録するフィールドが存在します。

ストレージプールのレイアウト

それでは今度は実際にZFSの情報を表示してみましょう。最初にZFSには「ストレージプール」「データセット」というふたつの概念が存在します。

ストレージプールはいわゆるボリューム管理のような機能です。複数のストレージデバイス・パーティションをまとめて、ひとつのボリュームのように扱います。Ubuntu 19.10のインストーラーでは単一のパーティションから単一のプールを作成していますが、複数のストレージデバイスをひとつのプールとしてストライピングやミラーリングしたり、RAIDのように信頼性・可用性を向上させることも可能になっています。また、ZFSとしてのログデバイスやキャッシュを別のより少量・高速なデバイスに指定するなど、かなり柔軟な構成が可能です。

ストレージプールの操作はzpoolコマンドで行います。

$ zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
bpool  1.88G   159M  1.72G        -         -      -     8%  1.00x    ONLINE  -
rpool  16.5G  3.98G  12.5G        -         -     5%    24%  1.00x    ONLINE  -

先ほども説明したように、Ubuntuではふたつのストレージプールを用意しています。詳細はzpool statusコマンドで確認できます。

$ zpool status
  pool: bpool
 state: ONLINE
status: The pool is formatted using a legacy on-disk format.  The pool can
        still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
        pool will no longer be accessible on software that does not support
        feature flags.
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        bpool       ONLINE       0     0     0
          vda4      ONLINE       0     0     0

errors: No known data errors

  pool: rpool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          vda5      ONLINE       0     0     0

errors: No known data errors

Software Design 2019年12月号でも詳しく説明しているように、bpoolは少し特殊です。bpool(/boot)はカーネルやinitramfsが保存される領域なので、GRUBから見えなくてはなりません。しかしながらGRUBのZFSモジュールは、ZFSのすべての機能を実装しているとは限りません。

そこでbpoolについてはより保守的な使い方であることを明示するために、featureフラグをすべて落としてバージョン名を明記しているのです。これが上記で言うところの「a legacy on-disk format」です。zpool get allコマンドでversionプロパティを見ても、bpoolだけバージョンが設定されていることがわかります。

$ zpool get all | grep version
bpool  version                        28                             local
rpool  version                        -                              default

よってbpoolについては、アップグレードは実行しないでください。スナップショットやバックアップといった基本的な機能は使えるので、そのままでも実用上問題はないはずです。

ちなみにUbuntu 20.04 LTSでは、必要なfeatureフラグがセットされたbpoolが作られます。つまりUbuntu 20.04 LTSのインストーラーを用いてインストールした環境だと、上記の警告はでなくなるはずです。

データセットのレイアウト

ストレージプールは論理ボリュームのようなものです。ここから実際に読み書きできる領域として切り出したものが「データセット」です。データセットには「ファイルシステム」⁠ボリューム」⁠スナップショット」⁠ブックマーク」の4種類が存在します。

ファイルシステムはその名の通りファイルシステムです。実際にファイルシステムのどこかにマウントされ、読み書きる状態になります。もっとも基本的な種類です。ZFSではストレージプールをブロックデバイスとして切り出せます。これが「ボリューム」です。たとえばKVMなどの仮想マシン用のストレージデバイスを作りたい際に利用します。

ファイルシステムやボリュームは、ある時点の状態を「スナップショット」として保存し、必要に応じてリストアできます。それに対して「ブックマーク」「ある時点」のみを記録するラベルのような仕組みです。差分バックアップを生成する際に有用になります。

Ubuntuインストール直後に使われているデータセットはファイルシステムだけです。データセットのリストはzfs listコマンドで確認できます。

$ zfs list
NAME                                               USED  AVAIL     REFER  MOUNTPOINT
bpool                                              159M  1.59G      176K  /boot
bpool/BOOT                                         158M  1.59G      176K  none
bpool/BOOT/ubuntu_08z65m                           158M  1.59G      158M  /boot
rpool                                             3.98G  12.0G       96K  /
rpool/ROOT                                        3.98G  12.0G       96K  none
rpool/ROOT/ubuntu_08z65m                          3.98G  12.0G     3.49G  /
rpool/ROOT/ubuntu_08z65m/srv                        96K  12.0G       96K  /srv
rpool/ROOT/ubuntu_08z65m/usr                       200K  12.0G       96K  /usr
rpool/ROOT/ubuntu_08z65m/usr/local                 104K  12.0G      104K  /usr/local
rpool/ROOT/ubuntu_08z65m/var                       495M  12.0G       96K  /var
rpool/ROOT/ubuntu_08z65m/var/games                  96K  12.0G       96K  /var/games
rpool/ROOT/ubuntu_08z65m/var/lib                   492M  12.0G      379M  /var/lib
rpool/ROOT/ubuntu_08z65m/var/lib/AccountServices    96K  12.0G       96K  /var/lib/AccountServices
rpool/ROOT/ubuntu_08z65m/var/lib/NetworkManager    128K  12.0G      128K  /var/lib/NetworkManager
rpool/ROOT/ubuntu_08z65m/var/lib/apt              77.5M  12.0G     77.5M  /var/lib/apt
rpool/ROOT/ubuntu_08z65m/var/lib/dpkg             35.6M  12.0G     35.6M  /var/lib/dpkg
rpool/ROOT/ubuntu_08z65m/var/log                  1.96M  12.0G     1.96M  /var/log
rpool/ROOT/ubuntu_08z65m/var/mail                   96K  12.0G       96K  /var/mail
rpool/ROOT/ubuntu_08z65m/var/snap                  112K  12.0G      112K  /var/snap
rpool/ROOT/ubuntu_08z65m/var/spool                 112K  12.0G      112K  /var/spool
rpool/ROOT/ubuntu_08z65m/var/www                    96K  12.0G       96K  /var/www
rpool/USERDATA                                    3.44M  12.0G       96K  /
rpool/USERDATA/root_e8xd45                         112K  12.0G      112K  /root
rpool/USERDATA/shibata_e8xd45                     3.24M  12.0G     3.24M  /home/shibata

多いですね。Ubuntuでは大雑把な重要度と用途に合わせてデータセットを分けているようです。データセットの名前は「ストレージプール/名前」で指定されます。名前のubuntu_root_shibata_の後ろの英数字はインストール時に自動生成したランダムな6文字となります。

注目すべきは「MOUNTPOINT」です。これはデータセットのmountpointプロパティの値であり、ZFSをマウントしたときに自動的にマウントポイントとして利用されるパスになります。

つまりZFSは/etc/fstabにルートファイルシステムを明示的に書かなくても、ZFS配下のデータセットが適切な場所にマウントされるというわけです。

ちなみにGRUBは、カーネルについてはbpool以下のデータセットのパスを、ルートファイルシステムについてはrpoolも含めたパスを指定します。

$ cat /proc/cmdline
BOOT_IMAGE=/BOOT/ubuntu_08z65m@/vmlinuz-5.3.0-23-generic root=ZFS=rpool/ROOT/ubuntu_08z65m ro quiet splash vt.handoff=1

上記のroot=がルートファイルシステムの指定です。

GRUBのデータセットの指定方法は、update-grub実行時にzfsコマンドを駆使して行います。詳細は/etc/grub.d/10_linux_zfsのスクリプトを確認してください。

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

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

たとえば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]⁠、スナップショットを作成したタイミングではストレージを消費しません。

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

$ 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などのように、自動的にスナップショットを作成するソフトウェアも存在します。スナップショット・ロールバックを本格的に使う際は、これらの自動化ツールを活用すると良いでしょう。

バックアップとリストア

一般ユーザーにおけるZFSのもうひとつの利点が、バックアップとリストアが簡単なことです。まずはzfs sendコマンドでホームディレクトリのデータセットのバックアップを取ってみましょう。なお、zfs sendの対象はスナップショットかマウントされていないデータセットとなりますので、とりあえず現在のスナップショットを作成してからバックアップします。

$ echo "backuped data" > ~/backup.dat
$ zfs snapshot rpool/USERDATA/shibata_e8xd45@backup1
$ sudo zfs send rpool/USERDATA/shibata_e8xd45@backup1 > /var/tmp/home_backup.bin
$ ls -lh /var/tmp/home_backup.bin
-rw-rw-r-- 1 shibata shibata 13M 12月  1 23:21 /var/tmp/home_backup.bin

zfs sendはバイナリストリームを出力しますので、任意のファイルにリダイレクトできます。たとえばパイプを使って| gzip -c > backup.gzのように圧縮することも可能です。

バックアップデータができたので、今度はそれをzfs recvコマンドでリストアしましょう。今回は単純に「別の名前」でリストアします。

$ sudo zfs recv rpool/USERDATA/shibata_backup1 < /var/tmp/home_backup.bin
$ zfs list rpool/USERDATA/shibata_backup1
NAME                             USED  AVAIL     REFER  MOUNTPOINT
rpool/USERDATA/shibata_backup1  3.59M  11.1G     3.52M  /shibata_backup1
$ cat /shibata_backup1/backup.dat
backuped data
$ sudo zfs destroy -r rpool/USERDATA/shibata_backup1

リストアしたデータセットは自動的にセットされたマウントポイントにマウントされます。ここから先は個々のバックアップポリシーに依存しますが、たとえば今回の場合だと単にファイルのコピーで完了ですね。リストアしたデータセットはzfs destroyで削除できます。ただし、今回の例だとデータセットの中にスナップショットも含まれるので、-rオプションも付けておかないと削除できません。

zfs recvは標準入力からバイナリストリームを受け取ってそれをリストアしています[10]⁠。つまりパイプを駆使すれば、バックアップとリストアをひとつのコマンド列で実行できるということです。

よくあるのは、別のZFSが使われているマシンにSSH越しにリストアする例です。

$ sudo zfs send rpool/USERDATA/shibata_e8xd45@backup1 | \
  ssh foo.local sudo zfs recv rpool/USERDATA/shibata_backup

自動化するならスナップショット・リストアする際のオプションをある程度決めないと失敗するかもしれません。詳細はzfsのmanページを参照してください。

たとえばあらかじめ次のようなコマンドで、ホームディレクトリのクォータを設定しておけば、リモートマシン側であふれることも防げるでしょう。

$ sudo zfs set quota=20G prpool/USERDATA/shibata_e8xd45

前項でも取り上げたzsnapdパッケージとして提供されているzfs-snap-managerには定期的なバックアップとレプリケーション機能も備わっているようです。

ZFSのメモリの使用量

ZFSを使う上で気になるのがメモリの使用量です。ZFSはprocfsやsysfs経由で数多くの情報を提供し、設定をカスタマイズ可能です。ただあまりに多すぎて把握するのが大変でもあります。

そこでざっくりと情報を把握するのに便利なのがarc_summaryコマンドです。早速使ってみましょう。zfsutils-linuxパッケージに含まれているので、Ubuntuなら最初からインストールされているはずです。

$ arc_summary -g

    ARC: 922.5 MiB (46.9 %)  MFU: 415.3 MiB  MRU: 453.8 MiB  META: 133.6 MiB (1.4 GiB) DNODE 20.0 MiB (147.5 MiB)
    +----------------------------------------------------------+
    |FFFFFFFFFFFFRRRRRRRRRRRRRO                                |
    +----------------------------------------------------------+

ARC(Adaptive Replacement Cache)はメモリ上に配置しているZFS用のディスクキャッシュです。つまりZFSのキャッシュだけで1GiB程度使っているということです。

ARCの最大サイズは環境にも依存しますが、たとえばarc_maxはメモリの半分が初期値のようです。他にも高速なストレージデバイス上にキャッシュを構築するL2ARCもありますが、これはさらにメモリを使用するとのこと。Ubuntuでは、初期状態では設定されていません。

$ arc_summary | grep L2ARC
L2ARC not detected, skipping section

なんらかの理由でARCの最大サイズを変更したい場合は、次のようにモジュールパラメーターをセットした上で、initramfsを再構築し、システムを再起動してください。

$ echo 'options zfs zfs_arc_max=1073741824' | sudo tee /etc/modprobe.d/zfs.conf
$ sudo update-initramfs -u -k all

この方法は他のモジュールパラメーターにも有効です。

おすすめ記事

記事・ニュース一覧