Fedora/LinuxでZFS on Linuxを使う

Computer, Linux 12月 26, 2017 #Fedora, #Linux, #ZFS
(Last Updated On: 2022年6月13日)

Fedora(Linux)でボリューム管理、スナップショット、圧縮、RAID5以上の性能と可能性を持つファイルシステムで”安定しているファイルシステム”となるとZFSしかありません。

機能的にはBtrfsも同等の機能を持っています。しかし、少なくともこのブログを書いている時点では、RAID56はカーネルがクラッシュした場合などにファイルシステムが壊れてしまう問題があります。2017年8月に修正パッチが提案されていますが、12月現在でも運用環境では使用しないように、と注意書きがあります。

ZFS, Btrfsは両方ともデータの整合性のチェックが可能です。一般にログファイルシステムと呼ばれているファイルシステム(NTFS, XFS2, Ext4など)はディレクトリ情報などのメタデータの整合性をログで維持しています。ZFS、Btrfsはメタデータの整合性に加え、データ自体の整合性もハッシュでチェックしています。冗長性を持たせていない場合でも、HDDのセクタ不良でどのファイルが壊れたのか確実に判ります。

HDDは普通一気に使えない状態になりません。RAID1で冗長性を持たせていても、どちらかのHDDのセクタ不良の場合はどちらのデータが正しいのか?判断できません。ZFSやBtrfsなら、どちらのデータが正しいのか判断できます。

データ整合性の保証はZFS、Btrfsの大きなメリットです。

※ Fedora用に記載していますが、UbuntuやCentOSなど、他のLinuxでZFSを使う場合も基本は同じです。

TL;DR;

ZFSインストール

$ sudo rpm -e --nodeps zfs-fuse
$ sudo dnf install http://download.zfsonlinux.org/fedora/zfs-release$(rpm -E %dist).noarch.rpm
$ sudo dnf install kernel-devel zfs
$ sudo reboot

上記のコマンドでFedoraにZFS on Linuxがインストールされます。ZFSカーネルモジュールのビルドに時間がかかります。

プールとデータセット作成

$ sudo wipefs -a /dev/disk/by-id/DISK-ID
$ sudo zpool create -f mypool /dev/disk/by-id/DISK-ID
$ sudo zfs create -o mountpoint=/home/myhome mypool/myhome

上記のコマンドでZFS on Linuxがインストールされ、mypoolが作成され、/home/myhomeにZFSのデータセットがマウントされます。プールもZFSで/にプール名でマウントされます。3:

ZFSを利用するハードウェア

システムに必要な最小メモリは2GB程度、推奨は8GB以上です。x86_64, x86両方をサポートしていますが、x86での利用は非推奨です。

FreeBSDベースのNAS OSSのFreeNASのハードウェア要求は一定の参考になります。最低限が8GBのメモリ、推奨が16GBです。ハードウェアRAIDは強く非推奨だとしています。実際、ZFSを利用するならハードウェアRAIDは無用長物どころか有害でさえあります。マザーボードなどのRAID機能は迷わず無効にし、冗長性の確保はZFSで行いましょう。

ZFSのFAQではエラー修正が可能なECCメモリを強く推奨しています。ZFSはかなり堅牢なファイルシステムですが、メモリエラーによる不整合発生は防げません。ファイルの安全性を確実にするにはECCメモリが必要です。ZFSに限ったことではありませんが、不良メモリでファイル/ファイルシステムは壊れます。

ZFSにはハードウェア/ソフトウェアRAIDは必要ありません。必要ない、というより在ると有害です。もし在っても使わないようにします。

ZFSのインストール

Fedoraで利用可能なZFSにはFUSE(ユーザースペースのファイルシステム)を利用したZFSとカーネルモジュールを利用したZFSの2種類が利用可能です。

ZFSのCDDLライセンスはLinuxカーネルのGPLライセンスと互換性がありません。FUSEはカーネルと直接ビルド/リンクしないモジュールをダイナミックにロードするのでライセンス互換性の問題を回避できます。FUSEを利用したzfs-fuseパッケージはFedoraのデフォルトリポジトリにも登録されています。このため、zfs-fuseは容易に利用可能です。しかし、FUSEのZFSは性能的に最適とは言えません。

ここではzfsonlinux.orgのカーネルモジュールを利用する方法を紹介します。

インストール手順

Fedoraにはzfs-fuseパッケージがあるので、依存関係によりzfs-fuseパッケージがインストールされている場合があります。しかも、zfs-fuseに依存するパッケージが多くあり普通に削除できないです。

そこで、zfs-fuseパッケージがインストールされている場合は依存性を無視してアンインストールします。

$ sudo rpm -e --nodeps zfs-fuse

後はzfsonlinux.orgのFedora用インストール手順に従ってインストールできます。

$ sudo dnf install http://download.zfsonlinux.org/fedora/zfs-release$(rpm -E %dist).noarch.rpm
$ gpg --quiet --with-fingerprint /etc/pki/rpm-gpg/RPM-GPG-KEY-zfsonlinux
pub 2048R/F14AB620 2013-03-21 ZFS on Linux <zfs@zfsonlinux.org>
Key fingerprint = C93A FFFD 9F3F 7B03 C310 CEB6 A9D5 A1C0 F14A B620
sub 2048R/99685629 2013-03-21

$ sudo dnf install kernel-devel zfs

zfsonlinux.orgのZFSはカーネルモジュールとしてインストールされる為、zfs-fuseに比べ高い性能を持っています。カーネルモジュールなので、カーネルのバージョンアップで使えなくなる場合もあります。/etc/dnf/dnf.confのinstallonly_limitを

installonly_limit=6

などとして古いカーネルが利用しやすいようにすると安全です。

ZFSプールの作成

ZFSはプールとデータセットを利用します。プールはデータセットの入れ物、データセットは大きな入れ物であるプールを小分けにして使いやすくした物、というイメージです。4

ZFSはミラーリング、ストライピング、RAID5のようなストライピングと冗長性を持たせられます。ここではRAIDZを作成します。RAIDZはRAID5と同等と考えても構いません。2台のHDD故障に耐えれるRAIDZ2(RAID6相当)や3台のHDD故障に耐えれるRAIDZ3もサポートしています。5

RAIDZプールの作成と管理

HDDを他のファイルシステム、特にLVMで使っていたHDDを使った場合、LVMが誤認識して起動時に認識しようとすることがあります。既にフォーマット済みのHDDの場合、全てのパーテションのファイルシステムシグニチャーを消しておいた方が良いです。

$ sudo wipefs -a /dev/sdX1
$ sudo wipefs -a /dev/sdX2
$ sudo wipefs -a /dev/sdX

パーテションがある場合、先にパーテションのシグニチャーを消し、最後にドライブ全体のシグニチャーを消します。

RAIDZのプールは以下のようなコマンドで作成します。

例:mypoolというraidzのZFSプールをID1からID3のデバイスで作成

$ sudo zpool create -f -o ashift=12 mypool raidz \
  /dev/disk/by-id/ID1 \
  /dev/disk/by-id/ID2 \
  /dev/disk/by-id/ID3

/mypoolディレクトリが作成され、mypool内全てのzfsデータセットは/mypoolの中に作られます。ashift=12 はブロックサイズを最適化するオプションです。

raidzは3台以上、raidz2は4台以上、raidz3は5台以上のデバイスが必要です。raidzやmirrorなどの指定なして単独のデバイスでプールを作ったり、複数のデバイスを単一のデバイスのように使えるようにもできます。

今のところRAIDZのデバイスにディスクを後から追加して拡張することはできません。オンラインでRAIDZを拡張できるようにするプロジェクトは始まっています。利用できるようになるのはまだ先の話です。

ミラーしたプールの作成

ミラーリングを利用したプールは以下のようなコマンドで作成します。

$ sudo zpool create -f -o ashift=12 mypool mirror \
  /dev/disk/by-id/ID1 \
  /dev/disk/by-id/ID2

ミラーには2台以上のHDDが必要です。

単独ドライブのプールは以下のようなコマンドで作成します。

$ sudo zpool create -f -o ashift=12 mypool /dev/disk/by-id/ID1

冗長性のないストライピングの作成

複数のドライブを束ねたプールは次のようにします。

$ sudo zpool create -f -o ashift=12 mypool /dev/disk/by-id/ID1 /dev/disk/by-id/ID2

ZFSプールの管理

作成済みプールの管理

作ったプールはlistやstatusコマンドで参照できます。

$ sudo zpool list
$ sudo zpool status

何らかの理由、例えばプールのデバイス名が変更されてしまった時など、でプールが初期化できなかった場合、importで利用できるプール名を参照できます。

$ sudo zpool import

表示されたプールはimportコマンドでインポートできます。

$ sudo zpool import <poolname>

プールを削除する場合はdestroyを利用します。

$ sudo zpool destroy mypool

パーテションの利用とデバイス名

ここではZFSプールに追加するデバイスはHDDデバイス全体ではなくパーテション、例えば/dev/sdb1など、も指定できます。

ZFSインストールの紹介サイトにはHDDデバイス全体をZFSプールに追加する例が数多く紹介されています。これはOpen ZFSのFAQの解説が理由でしょう。

ディスク全体を使うとパーテションのアライメントを考慮しなくて済みます。この場合、/dev/disk/by-id/のシンボリックリンクを使うと良いです。

異るサイズのディスクを使い、raidzXやmirrorを構成した場合、小さいサイズのディスクサイズまで使えるようになります。後で小さいサイズのディスクを大きくすれば、使っていなかった領域も使えるようになります。

LogファイルのSSDキャッシュ

ZFSはLog File Systemと言われるタイプのファイルシステムで、ディレクトリ情報などのメタデータの整合性をログファイルで維持できるようになっています。このログを高速なSSDなどにキャッシュするし、高速化できます。このログはZIL(ZFS Intent Log)と呼ばれ待ています。

SSDを使っても十分以上に効果があります。速ければ速いほど良いので、バッテリーバックアップ付きのDRAMなどを使うとより効果的です。

Logファイルはあまり大きくなりません。1GB程度のパーテションでも十分ですが、メモリキャッシュ程度の大きさが最大限になるようです。デフォルトではシステムメモリの半分をARC(Adaptive Replacement Cache)と呼ばれるキャッシュに利用します。ARCキャッシュに8GBを割り当て、SSDに4GBのパーテションを作って利用していますが、使い切るようなケースは無いようです。

$ sudo zpool add mypool log /dev/disk/by-id/ID

Logが壊れるとファイルが壊れます。このためLogはミラーするのがベストプラクティスです。複数のSSDドライブを利用できる場合、ミラーすると安全性が増します。

$ sudo zpool add mypool log mirror \
  /dev/disk/by-id/ID1 \
  /dev/disk/by-id/ID2

logを削除する場合、removeを使います。

$ sudo zpool remove <device>

データのSSDキャッシュ

HDDよりSSDの方がより高速です。このキャッシュはL2ARCと呼ばれます。ARC(メモリキャッシュ)があるのでZILよりは効果が低いです。ZILは少ないSSD領域で大きな効果があります。どちらか1つのキャッシュを作るならZILをキャッシュする方が良いです。

$ sudo zpool add mypool cache /dev/disk/by-id/ID

十分にキャッシュ領域を確保できるなら、基本的にはLogとCacheを作って使う方が良いです。

cacheを削除する場合、removeを使います。

$ sudo zpool remove <device>

ZFSデータセットの作成と管理

ZFSデータセットはzfsコマンドで作成します。作ったボリュームは作成時に指定したディレクトリに自動マウントされます。

$ sudo zfs create -o mountpoint=/home mypool/home

このコマンドはmypoolにhomeを作成し、起動時に/homeにマウントします。

マウントポイントはデータセット作成後にzfs setでも設定できます。

$ sudo zfs set mountpoint=/home mypool/home

作成したデータセットはzfs listで参照できます。

$ sudo zfs list

何らかの理由で自動マウントされなかったZFSデータセットはzfs mountでマウントできます。

$ sudo zfs mount -a

-aオプションは全てのデータセットをマウントします。

圧縮の有効化

プール作成時に-oオプションで圧縮も指定できますが、ここでは後で指定することにします。圧縮方法は複数ありますが、大抵の場合はlz4で十分でしょう。

$ sudo zfs set compression=lz4 mypool/home

圧縮率はcompressratioで取得できます。私の環境の場合、vmwareディスクイメージを保存しているZFSの圧縮率は1.6〜1.7程度です。

$ zfs get compressratio mypool/home

全てのパラメーターは

$ zfs get all

で取得できます。これらのパラメータの多くがsetコマンドで値を設定できます。

atimeの調整

ZFSはデフォルトatimeを記録します。xfsやbtrfsのデフォルトであるrealtimeと同等の動作に変更します。

$ sudo zfs set atime=off mypool
$ sudo zfs set relatime=on mypool

レコードサイズの調整

データベースシステムを利用する場合、ZFSのレコードサイズとデータベースのレコードサイズ(ブロックサイズ)を合わせるとより良い性能を期待できます。PostgreSQL、InnoDBの場合、デフォルトで8KBです。

$ sudo zfs set recordsize=8K mypool

ディスクサイズの拡張

RAIDZ1/2/3のボリュームはメンバーのHDDサイズを大きくすれば拡張できます。ただし、自動拡張オプションがオフなのでオンにする必要があります。

$ sudo zpool set autoexpand=on mypool

スナップショット

今日のスナップショットを取得

$ sudo zfs snapshot mypool/home@`date --iso-8601`

スナップショット一覧

$ zfs list -t snapshot

スナップショット削除

$ sudo zfs destroy mypool/home@2017-12-26

ZFSを使うならスナップショットをフル活用すべきです。1時間毎のスナップショットを96時間、一日毎のスナップショットを90日、月毎を13ヶ月、といった感じです。

ZFSのスナップショットマネージャー Sanoidを使う

ローカルに保存されたスナップショットはバックアップの完全な代替にはなりません。しかし、ZFSにはsend/receive機能があります。Sanoidに付属しているsyncoidを利用するとZFSスナップショットを効率的に別マシンに送信/保存でき、バックアップ用途に利用できます。送信先でもSanoidを使えばスナップショットによる世代管理が可能です。

メンテナンス

zpool状態の確認

$ zpool status 10
$ zpool iostat 10
$ zpool iostat -v mypool 10

ZFSプールの一覧

$ zpool list

より詳しいZFS情報

$ zdb

インポート可能なプールの確認(認識&マウントできていないプールの確認)

$ sudo zpool import

整合性のチェック ー ZFSプールの整合性チェックはオンラインで可能

$ sudo zpool scrub mypool

scrubの進捗

$ zpool status

ZFSデータセットの一覧

$ zfs list
$ zfs list -t snapshot

ZFSデータセットのチェックサム状態 – データ整合性が必要ない場合、チェックサムを無効にすると少し性能が向上する

 $ zfs get checksum

ZFS ARC統計情報

$ cat /proc/spl/kstat/zfs/arcstats

エラーが起きたディスクのエラー修正

zpool clear mypool /dev/disk/by-id/ID

エラーが起きたディスクの交換

zpool replace mypool /dev/disk/by-id/OLDID /dev/disk/by-id/NEWID

エラーが起きたディスクの取り外し

zpool detach mypool /dev/disk/by-id/FAILED_DISK

新しいディスクの追加 – 可能ならzpool replaceで交換した方が良い

zpool attach mypool /dev/disk/by-id/FAILED_DISK

メモリ管理

ZFSはデフォルトで搭載メモリの半分をARC用に使用します。ファイルサーバーでないマシンのメモリを半分ディスクキャッシュに利用するのは多すぎでしょう。/etc/modprobe.d/zfs.confを作成し、8GBをARCに使う設定をしました。

options zfs zfs_arc_max=8294967296

ZFSを使うとスワップをほとんど行わなくなります。どうしても必要な時にだけスワップします。恐らくARCがスワップされることを防ぐ為だと思われます。もう少しスワップした方が効率的では?と思えるほどスワップしません。気休めですが/etc/sysctl.confにvm.swappinessを設定しています。

vm.swappiness=99

最新のカーネルでは問題ないかも知れませんが、vm.nr_hugepages=512を書いておくと、全くスワップが使われませんでした。メモリが逼迫するとOOM Killerが動作してシステムが動作しなくなりました。特に必要のないvm設定は行わない方が安全かも知れません。

カーネルモジュールとDKMS

zfsonlinux.orgのZFSはカーネルモジュールです。カーネルがバージョンアップするとソースコードからZFS用のモジュール(zfs.koとspl.ko)をコンパイルしてインストールしなければなりません。

DKMSはZFSモジュールのようにソースコードで配布されるモジュールを自動コンパイル、自動インストールできるようにする仕組みです。正常に動作している場合は問題ありませんが、不具合などで手動でビルド/インストールしなければならない場合もあります。

DKMS状態の確認

$ sudo dkms status

コンパイルは/var/lib/dkmsで行われます。ディストリビューションのアップグレードなどでDKMSでインストールされたZFS設定が無くなる場合があります。この場合、手動でdkms addする必要があります。

$ sudo dkms add -m zfs -v [ZFS Version. e.g. 2.1.4] 
$ sudo dkms autoinstall

現在のカーネルのzfs.koとspl.koのビルド/インストール

$ sudo dkms --force autoinstall -k `uname --kernel-release`

カーネルをバージョンアップする際にDKMSが動作し、モジュールをビルド&インストールします。ZFSインストール前よりカーネルインストールの時間がかなり多く必要になります。気長に待ちましょう。

ZFSのバージョンが上がった場合に、古いカーネルでも最新のZFSモジュールを使うにはdkmsコマンドを使った手動ビルトとインストールが必要になります。

稀にdepmodが終わらない場合もあるようです。仕方無くリブートした場合などは、リブート後に再度実行すればOKです。

$ sudo depmod -a

その後、modprobeを実行するとZFSが利用できるようになります。

$ sudo modprobe zfs

ZFS関連モジュールがロードされていればOKです。

$ sudo lsmod | grep zfs

dkms.confが見つからないとエラーになる場合

古いモジュールの/var/lib/dkms/<module>/<version>/source/kdms.confが見つからない、とエラーになることが稀にあります。この場合、/var/lib/dkms/<module>/<version>を削除してからモジュールをビルド&インストールすると解決します。

メジャーバージョンアップでモジュールが正常にビルド出来なかった場合

メジャーバージョンアップの場合、ZFSモジュールが正常にビルド&インストールされない場合があります。この場合、手動でモジュール追加します。例えば、以下のような感じで強制的にモジュールをインストールします。

$ sudo dkms install add --force zfs/0.8.0
$ sudo depmod -a 
$ sudo modprobe zfs
$ lsmod | grep zfs
$ zpool import

最後のlsmod | grep zfs でZFSモジュールが在ればOKです。

zpoolがインポートされていない状態になることがありますが問題ありません。zpool import <pool_name> でインポートできます。その後はzfs mount -a とすればZFSがマウントできます。

2つのバージョンのZoLが存在してビルドエラーになる場合は古い方(削除されている方)のビルドファイルを削除してからビルドます。

$ sudo rm -rf /var/lib/dkms/zfs/0.8.0
$ sudo dkms autoinstall
$ sudo depmod -a
$ sudo modprobe zfs
$ lsmod | grep zfs
$ zpool import

DockerとZFS

DockerはZFSを正式にサポートしていませんが、普通に利用できます。時々問題が発生しますが対処方法があります。

#!/bin/bash

# A simple script to get information about mount points and pids and their
# mount namespaces.

if [ $# -ne 1 ];then
echo "Usage: $0 <devicemapper-device-id>"
exit 1
fi

ID=$1

MOUNTS=`find /proc/*/mounts | xargs grep $ID 2>/dev/null`

[ -z "$MOUNTS" ] &&  echo "No pids found" && exit 0

printf "PID\tNAME\t\tMNTNS\n"
echo "$MOUNTS" | while read LINE; do
PID=`echo $LINE | cut -d ":" -f1 | cut -d "/" -f3`
# Ignore self and thread-self
if [ "$PID" == "self" ] || [ "$PID" == "thread-self" ]; then
  continue
fi
NAME=`ps -q $PID -o comm=`
MNTNS=`readlink /proc/$PID/ns/mnt`
printf "%s\t%s\t\t%s\n" "$PID" "$NAME" "$MNTNS"
done

Dockerコンテナが削除できなくなる

ZFSボリュームがbusy状態になりアンマウントできなくなる事は頻繁にあります。ボリュームを利用している(アクセスしている)プロセスを再起動すればbusy状態を解消できます。多くの場合はsystemdで管理しているサービスが原因です。次のスクリプトで見つけられます。

#!/bin/bash

# A simple script to get information about mount points and pids and their
# mount namespaces.

if [ $# -ne 1 ];then
echo "Usage: $0 <devicemapper-device-id>"
exit 1
fi

ID=$1

MOUNTS=`find /proc/*/mounts | xargs grep $ID 2>/dev/null`

[ -z "$MOUNTS" ] &&  echo "No pids found" && exit 0

printf "PID\tNAME\t\tMNTNS\n"
echo "$MOUNTS" | while read LINE; do
PID=`echo $LINE | cut -d ":" -f1 | cut -d "/" -f3`
# Ignore self and thread-self
if [ "$PID" == "self" ] || [ "$PID" == "thread-self" ]; then
  continue
fi
NAME=`ps -q $PID -o comm=`
MNTNS=`readlink /proc/$PID/ns/mnt`
printf "%s\t%s\t\t%s\n" "$PID" "$NAME" "$MNTNS"
done

シャットダウン時にumountに失敗すると次のエラーのようなエラーが起きることがごく稀にあります。

ERROR: for netdata_web_1  Driver zfs failed to remove root filesystem 98909f15be4a460d6cb9825b85da2a732302d64ddd8c536be93e05234fd68f4f: exit status 1: "/usr/sbin/zfs zfs destroy -r datastore/docker/6b103ede080658c15133083bfadf9858eec89ce42ceb3000b12190b1b95a6053" => cannot open 'datastore/docker/6b103ede080658c15133083bfadf9858eec89ce42ceb3000b12190b1b95a6053': dataset does not exist

といった感じでコンテナが削除できません。この場合、存在しないとエラーになっているボリュームを空ボリュームとして作成すれば削除できます。

$ sudo zfs create datastore/docker/6b103ede080658c15133083bfadf9858eec89ce42ceb3000b12190b1b95a6053
$ sudo zfs create datastore/docker/6b103ede080658c15133083bfadf9858eec89ce42ceb3000b12190b1b95a6053-init
$ sudo docker container rm <container id/name>

NVMe SSDとZFS

NVMe SSDにはかなり高速な製品があります。読み書き3GB/s程度のSSDでPostgreSQLのベンチマークをすると遅いHDDに最適化されたZFS/Btrfsはext4/XFSに比べて1/2から1/3程度の性能でした。

ZFSの場合、ZIL/L2ARCを使ったRAIDZ1(HDD5台)を使った方が、単純にNVMe SSDパーティションをZFSでフォーマットした場合に比べ、2倍以上高速でした。

NVMe SSDを利用する場合、どのファイルシステムにするか、ベンチマークして選択する方が良いです。

Fedoraリリースのバージョンアップ

ZFS on Linuxを利用している場合、新しいFedoraリリースが公開されても直ぐにバージョンアップするのは良くないです。zfsonlinux.orgやgithubのリポジトリなどを見て、新しいリリースに対応しているか確認してからバージョンアップしましょう。

ZFSはLinux Kernelとは独立したプロジェクトなので不安に感じる方もいると思います。ZFSを開発しているのはLLNLという米国政府の研究機関です。Fedoraは公式なサポート対象OSになっています。

既知の問題

GitHubのIssueが参考になります。単なる問い合わせ/質問に近いモノも多いです。もし質問する場合はメーリングリストを利用しましょう。リストのアーカイブも公開されているので、アーカイブを検索するとよいです。

参考


    1. LVM + XFS + mdraidでそれなりのモノは作れます。しかし、XFSに”データ領域整合性保証がない”、mdraidに2つHDDが異る値を返してきた時に”どちらが正しい情報なのか判別する仕組みがない”です。これは信頼性/可用性を大きく損う原因になっています。 
    1. ArchLinuxのXFS解説に書いてある通り、Note: Unlike Btrfs and ZFS, the CRC32 checksum only applies to the metadata and not actual data. です。データベースなど整合性が重要なシステムでは、データ整合性確認は特に重要です。 
    1. プールのマウントポイントも指定可能です。 
    1. 正しいとは言いきれませんがイメージとしてはこんな感じです。プール自体もZFSのファイルシステムです。0.7.5になってから(?)プールをマウントしたディレクトリの中身がみえませんが、以前のバージョンだとデフォルトの状態でzfs createで作ったファイルシステムを参照できました。 
  1. 16台を越えるデバイスをraidzXで使うことは推奨されていないようです。多数のデバイスを使う場合、raidzXのデバイスを束ねて使うと記述されています。 

投稿者: yohgaki