Ubuntu 16.04のZFSで/homeを冗長化
Ubuntu 16.04 から ZFS がサポートされたので、/home
をミラーリングした ZFS 上に作成して冗長化してみました。
これまで使っていたディスクが壊れてしまい、新しく Ubuntu 16.04 をインストールすることになりました。そこでせっかくなので、このリリースからサポートされた ZFS を使ってムフフなファイルたちを無くさないように大切な/home
を冗長化することにしました(注1)。
ここでは、ZFS をインストールして使用する方法を簡単にまとめます。ZFS ならばバックアップの作成も簡単ですが、ここでは触れません。
システム構成
CPU | Pentium G620 |
RAM | 16GiB |
Disk | 1TB x 2 |
OS | Ubuntu 16.04 Server |
- メモリ
- 最低 4GB。できれば 8GB で、多ければ多いほど良い。もっとも 4GB 未満でも動かないということもないようです。今回のマシンは、KVM ホストもしているので 16GB にしています。 ZFS のメモリというと ECC 付きメモリ or not という話は避けられません。ECC 付きのメモリでないとファイルが知らない間に壊れているかもという恐ろしい話があります。一方で、それは他のファイルシステムでも同じで、ZFS だから壊れやすいとはことでもないという話もあります。私は個人的な用途なので気にせず ECC 無しのメモリを使用しています。
- ディスク
- ミラーリングの場合は、ディスクが二台必要です。三台でミラーリングして、冗長度を上げることも可能です。また RAIDZ の場合は最低 3 台、RAIDZ2 の場合は最低 4 台のドライブが必要です(注2)。 ZFS を構成するドライブ(パーティション)のサイズは、Btrfs と異なり全て同一である必要があります。容量の小さなドライブが混じっていると、全体がその最低サイズに合わせられます(注3)。またディスクを入れ替える場合も、同じかそれ以上のサイズが必要です。
- OS
- Ubuntu 16.04 Server(64bit)。ZFS を使用できるのは、64bit 版のみです。もっとも現在の Ubuntu では Desktop と Serve 共に 64bit 版が標準なので、それほど気にする必要はありません。しかしアップグレードしてきた場合は 32bit 版を使っているかも知れないので、その場合は注意が必要です。
システムをインストール
grub
ブートローダにはちょっとした問題(注4)があるので、ZFS はデータ用のファイルシステムとして使用することをオススメします。/(root)
の復旧は/etc
とインストール済みのパッケージ一覧があれば/(root)
の復旧は簡単ですので、システム全体を ZFS にして/(root)
自体も冗長化する利点はそれほど大きくありません。
そこで今回は/(root)
には冗長性を持たせないで、/home
だけに RAID1 の冗長性を持たせることにしました。その代わり復旧に大切な/etc
とパッケージ一覧、crontab
をcron
により適期的に ZFS 上にコピーしておきます。もちろん ZFS の snapshot を作成して過去に遡れるようにして。
まずsda
に小さなパーティション(8GiB で十分)を作成して、そこに通常通りシステムをインストールします。耐久性はそれほど要求されないので、USB フラッシュメモリにシステムをインストールしても良いかも知れません。
ZFS をインストール
ZFS に必要なカーネルモジュール zfs.ko はカーネルに同梱されているので、必要なユーティリティをインストールするだけで完了です。このモジュールがコンパイル済みでカーネルに含まれているので、これまでの DKMS による zfs.ko のコンパイルを待つ面倒臭さがなくなりました。
$ sudo apt install zfsutils-linux
ユーティリティのインストールが完了したら、ZFS に必要なカーネルモジュールがロードされファイルシステムが使えるようになっているか一応確認しておきます。
$ lsmod |grep zfs
zfs 2813952 3
zunicode 331776 1 zfs
zcommon 57344 1 zfs
znvpair 90112 2 zfs,zcommon
spl 102400 3 zfs,zcommon,znvpair
zavl 16384 1 zfs
$ cat /proc/filesystems |grep zfs
nodev zfs
ミラーリング(RAID1)プールの作成
まずインストールしてあるディスクを確認します。
今後ディスクにアクセするときは、sda
などの名前ではなく/dev/disk/by-id
に表示される名前を使用します(注5)。この名前は、インターフェイス(ata)とディスクのモデル名とシリアル番号の一部から作成されます。そのため今後ディスクを交換する時に、交換が必要なディスクを識別しやすくなります。
$ ls /dev/disk/by-id | grep ^ata
ata-ST31000524AS_9VPCZPQV
ata-ST31000524AS_9VPCZPQV-part1
ata-TOSHIBA_DT01ACA100_36D1KYUNS
ディスクを確認したら、それぞれに同じ容量のパーティションを作成します。
$ sudo cfdisk /dev/disk/by-id/ata-ST31000524AS_9VPCZPQV
$ sudo cfdisk /dev/disk/by-id/ata-TOSHIBA_DT01ACA100_36D1KYUNS
$ ls /dev/disk/by-id | grep ^ata
ata-ST31000524AS_9VPCZPQV
ata-ST31000524AS_9VPCZPQV-part1
ata-TOSHIBA_DT01ACA100_36D1KYUNS
ata-TOSHIBA_DT01ACA100_36D1KYUNS-part1
なぜかブートドライブに作成したパーティションが見えないのでudevadmin trigger
コマンドを試してみます。ダメなときは再起動します。
$ ls /dev/disk/by-id/ | grep ^ata
ata-ST31000524AS_9VPCZPQV
ata-ST31000524AS_9VPCZPQV-part1
ata-ST31000524AS_9VPCZPQV-part2
ata-TOSHIBA_DT01ACA100_36D1KYUNS
ata-TOSHIBA_DT01ACA100_36D1KYUNS-part1
パーティションを作成せずにディスク丸ごと ZFS にすることも可能で、この方がパフォーマンスが良いとのことです。しかし同じ 1TB として売られているディスクが、メーカーや製品が違ってもセクター数が同じという自信がありません。そこで私はいつも最大サイズより少し小さいパーティションを作成して使っています。
また最近のディスクはセクターサイズが 4KiB なので、パーティションの区切りを 4KiB のセクターサイズに合わせます。もっとも最近のfdisk
などは、そのことを考慮して配置してくれるので特に気にする必要はありません。
ディスクにパーティションを作成したら、プールを作成します。
$ sudo zpool create -o ashift=12 -o autoexpand=on \
tank mirror \
ata-ST31000524AS_9VPCZPQV-part2 \
ata-TOSHIBA_DT01ACA100_36D1KYUNS-part1
$ sudo zpool status
pool: tank
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ata-ST31000524AS_9VPCZPQV-part2 ONLINE 0 0 0
ata-TOSHIBA_DT01ACA100_36D1KYUNS-part1 ONLINE 0 0 0
errors: No known data errors
-o ashift=12
は、セクターサイズの指定です。
この指定によりセクターサイズを 2^12B(4KiB)だと指定しています。通常は指定する必要はありませんが、512B/sec のディスクを使用していると将来 4KiB/sec のディスクしか手に入らなくなった時に困ったことになります。
-o autoexpand=on
は、将来ディスクを交換した時に可能ならプールのサイズを自動的に拡張する指定です。
このオプションがなくても特に困ることはありません。必要ならば、ディスクの交換後にプールのサイズをマニュアルで拡張することが可能です。ただし拡張できるのは、いずれの場合も冗長構成のディスク全てが現在のプールサイズよりも大きくなった場合に限られます。
tank
は、プールの名前です。
/(root)
直下のディレクトリ名(bin など)でなければ自由に選べますが、tank
やpool
が慣習として使われます。
また-O compression=lz4
を一緒に指定してファイルシステム全体を圧縮することも可能です。後で見るように、ファイルシステムを作る時に個別に圧縮の有無と方式を指定することも可能です。
ファイルシステムを作成
プールを作成すると自動的に/
にプール全体がマウントされますが、目的に応じてファイルシステムを切り出します。今回は、/home
用に切り出します。
$ sudo zfs create -o atime=on -o relatime=on \
-o compression=lz4 tank/home
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
tank 372K 868G 96K /tank
tank/home 96K 868G 96K /tank/home
-o atime=on -o relatime=on
は、ファイルのアクセスタイムの更新方式を指定します。
Linux はファイルにいつアクセスしたかという情報を記録しており、ファイルにアクセスするたびにファイルの情報を書き換えています。しかしこの情報が使われることはほとんどありません。そこで-o atime=off
としてアクセスタイムの更新を止めてしまうと、ファイルアクセスのパフォーマンスが上がります。しかし一部このアクセス情報を使用するプログラムもあるので、だいたい問題ない程度に更新の頻度を減らすようにします。
-o compression=lz4
は、ファイルシステムを圧縮する方法を指定します。
ただ将来さらに優れた圧縮方法が導入されたら自動的にそれを使うように-o compression=on
としておいたほうが良かったかも知れません。zfs set
コマンドで後から変更できます。
ファイルシステムを作成したら/home
の内容を移動させて置き換えます。
$ sudo rsync -av /home/ /tank/home
$ cd /
$ sudo rm -rf /home/*
$ sudo zfs set mountpoint=/home tank/home
ZFS はファイルシステムをマウントする時に/etc/fstab
を見ないので、/etc/fstab
を書き換える必要はありません。
swap on ZFS
スワップをわざわざ ZFS 上に取る必要はないのですが、お試し的にスワップ領域を ZFS 上に取ってみました。
スワップ領域を作成
スワップ領域を ZFS 上に作成します(注6)。
$ sudo zfs create -V 16G \
-b $(getconf PAGESIZE) tank/swap
$ sudo zfs set com.sun:auto-snapshot=false tank/swap
$ sudo zfs set sync=always tank/swap
ブロックサイズをページサイズと同じくすることで空きメモリが少ない状況で大きなブロックの書き換えを防ぎます。
sync=always
とすることで、キャッシュをすぐに空にしてメモリの使用量を抑えます。
スワップを有効にする
スワップボリュームを作成したら、後は通常のスワップ領域を追加するときと同じです。
まず/etc/fstab
を編集して、次のエントリーを追加します。
# swap
/dev/zvol/tank/swap none swap default 0 0
次に実際にスワップ領域をフォーマットしてシステムに追加します。
$ sudo mkswap -f /dev/zvol/tank/swap
$ sudo swapon --all --verbose
swapon /dev/zd32
swapon: /dev/zd32: found swap signature: version 1d, page-size 4, 同じ byte order
swapon: /dev/zd32: pagesize=4096, swapsize=17179869184, devsize=17179869184
fstab
を設定してあるので、再起動しても自動的にスワップ領域が有効になります。
参照と脚注
- Linuxで使用できるディスクの冗長化には、歴史のあるmdや既に安定期に入りつつ有るBtrfsを使用できます。なのにZFSをなぜ選択したか? 単純にmdやBtrfsよりZFSのコマンドが私にとって分かりやすかったからです。
- RAIDZやRAIDZ2に必要なドライブの数は、次の式が成り立つ必要があります。ここで128KiBは、ZFSのデフォルトレコードサイズです。 128KiB % (ドライブ数 - パリティ数) == 0
%は、剰余を求める演算記号 よって、RAIDZはパリティ1なので3,5,9台、RAIDZ2はパリティ2なので4,6,10台となります。 - Btrfsは、容量の異なるディスク(パーティション)を組み合わせても、だけ有効に使ってZFSよりも容量を稼げることがあります。またファイルシステムに空きが有る場合は、現在よりも容量の小さいドライブに置き換えることも可能です。
- 少し手間はかかりますが、
/(root)
自体をZFSにすることも可能です。
ただしgrub
ブートローダに問題があり、/(root)
の入ったプールを作成する時にいくつかのZFS機能を無効にする必要があります。最初の時だけ気にするなら良いのですが、zpool status
コマンドを実行するとzpool upgrade
してくださいというメッセージが親切にも表示されます。そこでzpool upgrade
してしまうと、システムが起動できなくなってしまいます。
もちろん開発中のgrub
ではこの制限が取り除かれています。それでもZFSに新しい機能が導入されるたびにブートできなくなる不安がつきまといます。 - ZFSのプールを作成する時に
/dev/sda
などの名前を使用した場合は、zpool import
コマンドでインポートし直せば/dev/disk/by-id
での名前に変更可能です。ZFSパッケージ登録記念:UBUNTU 15.10にZFS ON LINUX - HOWTO use a zvol as a swap device
スワップボリューム – archlinux
Using a zvol for a swap device – FAQ zfsonlinux