ZFS on Linuxを仮想CentOS7上で検証した


## はじめに
我が家のストレージサーバは[FreeNAS](http://www.freenas.org/)っていうソフトで運用してます。こいつはFreeBSD上で動くやつで、大変簡単に高価なストレージサーバレベルの機能を使用できる優れものです。FreeNASでは、RAIDを使うことはもちろんのこと、ZFSというファイルシステムを使用してファイルシステム上でRAID機能を実現することができます。今回はこのZFSについて簡単にメモしつつ、Linux上でZFSを使うことができるZFS on Linuxを使ってみようと思います。また、ZFSの機能を色々検証してみたのでそれもメモとして残しておきます。


## ZFSのすごいところ
ZFSはオラクルによって開発されたファイルシステムで、以下のような特徴があります。

* RAID機能がある
* スナップショットが取れる
* めっちゃ簡単、コマンド一行で終わる
* ホットスペア対応
* 重複排除機能がある
* 割とメモリー容量を欲する

実に便利そうですなあ。メモリー以外。メモリー容量が少ないとカーネルパニック起こすらしいので注意です。

### RAID-Zについて
ZFSが実装しているRAIDは、RAID-Zと呼ばれてます。
RAID-Z1がRAID5相当にあたります。いまはRAID-Z3までありトリプルパリティまでいけます。
あとRAIDでは「RAID5書き込みホール」問題というものがあります。データとパリティの間で不整合が起きる問題のことで、一見正常に動いているように見えていても徐々にデータが破壊されていくというオバケめいた問題です。この問題は停電やクラッシュで発生するようで、これを防ぐためにハードウェアRAIDではバッテリーを積んで防いでいるようです。おっかないです。RAID-Zを使用すると、この問題が起きないそうです。理由としてはコピーオンライト機能と可変ストライプ幅によるものと記載されていることが多いのですが、詳しいアーキテクチャについては難しくて謎です。特に可変ストライプ幅についてはいろいろ意見があるようなので特に謎です。

## ZFS on Linux
### 今回の仮想マシン
早速インストールしてみたいと思います。例にもよって仮想環境を構築しました。今回はこんな感じのスペックです。

* CPU：1ソケット2コア
* ハードディスク：8GB×5本
* NIC：VMXNET3
* OS：CentOS7 最小インストール

いつも邪魔ばっかしてくるSELinuxとかは既に止めてあります。
あ、あと下のコマンドラインに出てくる容量表示は今回の値とは異なってる場合があるんで気にしないでください。

### ZFS on Linuxのインストール
[公式ページ](http://zfsonlinux.org/)を参考にインストールしていきます。
まずはEPELという外部リポジトリをインストールします。

```sh
$ sudo yum localinstall --nogpgcheck https://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
読み込んだプラグイン:fastestmirror
epel-release-7-5.noarch.rpm                                                  |  14 kB  00:00:00
/var/tmp/yum-root-1fgyJT/epel-release-7-5.noarch.rpm を調べています: epel-release-7-5.noarch
/var/tmp/yum-root-1fgyJT/epel-release-7-5.noarch.rpm をインストール済みとして設定しています
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ epel-release.noarch 0:7-5 を インストール
--> 依存性解決を終了しました。

依存性を解決しました

====================================================================================================
 Package                 アーキテクチャー  バージョン     リポジトリー                         容量
====================================================================================================
インストール中:
 epel-release            noarch            7-5            /epel-release-7-5.noarch             24 k

トランザクションの要約
====================================================================================================
インストール  1 パッケージ

合計容量: 24 k
インストール容量: 24 k
Is this ok [y/d/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : epel-release-7-5.noarch                                             1/1
  検証中                  : epel-release-7-5.noarch                                             1/1

インストール:
  epel-release.noarch 0:7-5

完了しました!
```

次に、ZFSのRPMパッケージをダウンロードします。

```sh
$ sudo yum localinstall --nogpgcheck http://archive.zfsonlinux.org/epel/zfs-release.el7.noarch.rpm
読み込んだプラグイン:fastestmirror
zfs-release.el7.noarch.rpm                                                   | 4.3 kB  00:00:00
/var/tmp/yum-root-1fgyJT/zfs-release.el7.noarch.rpm を調べています: zfs-release-1-2.el7.centos.noarch
/var/tmp/yum-root-1fgyJT/zfs-release.el7.noarch.rpm をインストール済みとして設定しています
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ zfs-release.noarch 0:1-2.el7.centos を インストール
--> 依存性解決を終了しました。

依存性を解決しました

====================================================================================================
 Package             アーキテクチャー
                                    バージョン                リポジトリー                     容量
====================================================================================================
インストール中:
 zfs-release         noarch         1-2.el7.centos            /zfs-release.el7.noarch         2.4 k

トランザクションの要約
====================================================================================================
インストール  1 パッケージ

合計容量: 2.4 k
インストール容量: 2.4 k
Is this ok [y/d/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : zfs-release-1-2.el7.centos.noarch                                   1/1
  検証中                  : zfs-release-1-2.el7.centos.noarch                                   1/1

インストール:
  zfs-release.noarch 0:1-2.el7.centos

完了しました!
```

準備が整ったので、kernel-develというのとZFSをインストールします。

```sh
# sudo yum install kernel-devel zfs
読み込んだプラグイン:fastestmirror
epel/x86_64/metalink                                                         | 4.6 kB  00:00:00
epel                                                                         | 4.4 kB  00:00:00
zfs                                                                          | 2.9 kB  00:00:00
(1/3): epel/x86_64/group_gz                                                  | 169 kB  00:00:00
(2/3): epel/x86_64/primary_db                                                | 3.5 MB  00:00:01
(3/3): zfs/x86_64/primary_db                                                 |  31 kB  00:00:01
(1/2): epel/x86_64/updateinfo                                                | 365 kB  00:00:00
(2/2): epel/x86_64/pkgtags                                                   | 1.5 MB  00:00:00

～(省略)～

  spl-dkms.noarch 0:0.6.4.1-1.el7.centos            zfs-dkms.noarch 0:0.6.4.1-1.el7.centos

完了しました!
```

インストール完了です。簡単です。

ちゃんとインストールできているか確認してみましょう。

```sh
$ cat /proc/filesystems
nodev   sysfs
nodev   rootfs
nodev   bdev
nodev   proc
nodev   cgroup
nodev   cpuset
nodev   tmpfs
nodev   devtmpfs
nodev   debugfs
nodev   securityfs
nodev   sockfs
nodev   pipefs
nodev   anon_inodefs
nodev   configfs
nodev   devpts
nodev   ramfs
nodev   hugetlbfs
nodev   autofs
nodev   pstore
nodev   mqueue
        xfs
nodev   zfs
```

一番下に`zfs`と表示されています。問題なさそうです。

### ZFSプール作成
まず、現状のデバイス状況を確認してみます。

```sh
$ parted -l
モデル: VMware Virtual disk (scsi)
ディスク /dev/sda: 17.2GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos
ディスクフラグ:

番号  開始    終了    サイズ  タイプ   ファイルシステム  フラグ
 1    1049kB  525MB   524MB   primary  xfs               boot
 2    525MB   17.2GB  16.7GB  primary                    lvm


エラー: /dev/sdb: ディスクラベルが認識できません。
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdb: 17.2GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: unknown
ディスクフラグ:

エラー: /dev/sdc: ディスクラベルが認識できません。
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdc: 17.2GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: unknown
ディスクフラグ:

エラー: /dev/sdd: ディスクラベルが認識できません。
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdd: 17.2GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: unknown
ディスクフラグ:

エラー: /dev/sde: ディスクラベルが認識できません。
モデル: VMware Virtual disk (scsi)
ディスク /dev/sde: 17.2GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: unknown
ディスクフラグ:

モデル: Linux device-mapper (linear) (dm)
ディスク /dev/mapper/centos-root: 14.9GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: loop
ディスクフラグ:

番号  開始   終了    サイズ  ファイルシステム  フラグ
 1    0.00B  14.9GB  14.9GB  xfs


モデル: Linux device-mapper (linear) (dm)
ディスク /dev/mapper/centos-swap: 1720MB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: loop
ディスクフラグ:

番号  開始   終了    サイズ  ファイルシステム  フラグ
 1    0.00B  1720MB  1720MB  linux-swap(v1)
```
`sda`がLinuxがインストールされているデバイスのようです。
`sdb`とか`sdc`がまだ何もフォーマットされてないディスクたちです。

さっそくZFSプールを作成してみます。
ZFSでは基本的に`zfs`コマンドか`zpool`コマンドしか使いません。単純明快なところが助かります。今回は調子に乗ってトリプルパリティであるRAID-Z3にします。

```sh
$ zpool create tank raidz3 /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf
invalid vdev specification
use '-f' to override the following errors:
/dev/sdb does not contain an EFI label but it may contain partition
information in the MBR.
```

といった矢先にエラーが出た。EFIラベルが無いとかなんとかってかいてあります。なんか`-f`っていうオプションつけたらいけるって書いてあるのでやってみます。ちなみに`tank`というのはzfsでのプールにおける慣例的なディレクトリらしく、みんなそこにマウントするので私もそうします。

```sh
$ zpool create tank raidz3 /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf -f
$
```

何のエラーなく終わりました。行けたのかしら。
`zpool`コマンドで確認してみます。

```sh
$ zpool list
NAME   SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  63.5G   100K  63.5G         -     0%     0%  1.00x  ONLINE  -
```

ちゃんとできてるっぽいです！

```sh
ファイルシス            サイズ  使用  残り 使用% マウント位置
/dev/mapper/centos-root    14G  1.2G   13G    9% /
devtmpfs                  1.9G     0  1.9G    0% /dev
tmpfs                     1.9G     0  1.9G    0% /dev/shm
tmpfs                     1.9G  8.5M  1.9G    1% /run
tmpfs                     1.9G     0  1.9G    0% /sys/fs/cgroup
/dev/sda1                 497M  151M  347M   31% /boot
tank                       16G     0   16G    0% /tank
```

なんと今の一行のコマンド一つでマウントまでされました。簡単すぎて泣きそう。

以下のコマンドでZFSプールの状態を確認できます。

```sh
$ zpool status
  pool: tank
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz3-0  ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
            sde     ONLINE       0     0     0
            sdf     ONLINE       0     0     0

errors: No known data errors
```

### 重複排除の威力
ZFSファイルシステムは、標準装備で重複排除機能を持っています。
ですが、デフォルトだと無効になっているのでまずは重複排除を有効化します。

```sh
$ zfs set dedup=on tank
```

この状態で1GBのファイルを作成してみます。

```sh
$ if=/dev/zero of=/tank/bigfile count=1024 bs=1M
```

サイズを確認してみます。

```sh
$ ls -lha
合計 1.0G
drwxr-xr-x   2 root root    3  5月 18 11:57 .
drwxr-xr-x. 18 root root 4.0K  5月 18 11:44 ..
-rw-r--r--   1 root root 1.0G  5月 18 11:57 bigfile
```

ちゃんと1GBのファイルが作成されています。普通のファイルシステムであれば、ストレージの容量も1GB分減っているはずです。


```sh
$ zpool list
NAME   SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  39.8G   980K  39.7G         -     0%     0%  8192.00x  ONLINE  -
```

ALLOCというのが使用容量を示しています。なんと驚きの980KB!
今回の実験では高効率のゼロ埋めファイルですが、例えばメールデータのバックアップ先なんかにしたら文字情報しかない為に結構な高率で保存できると思います。

### ZFSスナップショット機能
次はスナップショットという機能を使ってみます。
ファイルを削除する前とかにスナップショットを取っておいて、なんかミスったらすぐに元に戻せるっていう機能です。
まずはスナップショットを作成します。

```sh
$ zfs snapshot tank@20150518
```

スナップショットを一覧するコマンドがあるので確認してみます。

```sh
$ zfs list -t snapshot
NAME            USED  AVAIL  REFER  MOUNTPOINT
tank@20150518  1020M      -  1020M  -
```

ちゃんと作成されていました。1GB使用済みって表示されているので、そんなに容量喰うのかと思って`zpool list`を再確認。

```sh
$ zpool list
NAME   SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank  39.8G  1.04M  39.7G         -     0%     0%  8192.00x  ONLINE  -
```

よかった、60KB位しか増えてないようです。とりあえずこれでスナップショットはとれました。
ここで作成した空ファイルを削除します。

```sh
$ rm bigfile
rm: 通常ファイル `bigfile' を削除しますか? y
```

これで`bigfile`は消えてなくなりました。
かわりに`smallfile`というファイルを作成しておきます。

```sh
$ dd if=/dev/zero of=/tank/smallfile count=128 bs=1M
128+0 レコード入力
128+0 レコード出力
134217728 バイト (134 MB) コピーされました、 0.142223 秒、 944 MB/秒
```

そして、さらにスナップショットを作成します。

```sh
$ zfs snapshot tank@20150518-2
```

なぜここでスナップショットを撮ったのか？
なんとZFSはスナップショット同士を比較することができるのです。！
以下のコマンドで何が変わったかを確認することができます。

```sh
# zfs diff tank@20150518 tank@20150518-2
M       /tank/
-       /tank/bigfile
+       /tank/smallfile
```

表示された識別子で、何が変化したかを表してます。以下に表を置いておきます。

|識別子|意味|
|:--:||:--:|
|M|変更|
|-|削除|
|+|追加|
|R|リネーム|

`/tank`ディレクトリが変更され、`bigfile`が削除され`smallfile`が追加されたことがわかります。
もちろんロールバックも簡単です。やってみます。

```sh
$ zfs rollback tank@20150518
```

ちゃんと元に戻ってるか確認してみます。

```sh
$ ll
合計 1044588
-rw-r--r-- 1 root root 1073741824  5月 18 17:19 bigfile
```

完璧に元に戻りました。***神ゲー***

### 耐障害性の実験
RAID機能の力を見る時がやってきました。
あらためてZFSプールの状態を確認しておきます。

```sh
$ zpool status
  pool: tank
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz3-0  ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
            sde     ONLINE       0     0     0
            sdf     ONLINE       0     0     0

errors: No known data errors
```

stateがONLINEと表示されています。問題ない状態です。

仮想マシンのいいところでHDDをぶっさすのも引っこ抜くものも自由自在です。さっそくハードディスクを1本引き抜いてみます。
![Imgur](http://i.imgur.com/GzQs4tW.png)

再度ZFSプールの状態を確認します。

```sh
$ zpool status
  pool: tank
 state: DEGRADED
status: One or more devices could not be used because the label is missing or
        invalid.  Sufficient replicas exist for the pool to continue
        functioning in a degraded state.
action: Replace the device using 'zpool replace'.
   see: http://zfsonlinux.org/msg/ZFS-8000-4J
  scan: none requested
config:

        NAME                      STATE     READ WRITE CKSUM
        tank                      DEGRADED     0     0     0
          raidz3-0                DEGRADED     0     0     0
            sdb                   ONLINE       0     0     0
            sdc                   ONLINE       0     0     0
            sdd                   ONLINE       0     0     0
            18248829720282346757  FAULTED      0     0     0  was /dev/sde1
            3834187477602051621   UNAVAIL      0     0     0  was /dev/sdf1

errors: No known data errors
```
stateがDEGRADEDにかわっています。一台死んでやばい状態です。
この状態でファイルが参照できるか確認してみます。

```sh
$ cd /tank/
$ ll
合計 1044588
-rw-r--r-- 1 root root 1073741824  5月 18 17:19 bigfile
```

問題なく参照できるようです。
vSphereクライアントから新しいハードディスクを追加して`parted -l`を確認します。

```sh
$ parted -l
～（省略）～
エラー: /dev/sde: ディスクラベルが認識できません。
モデル: VMware Virtual disk (scsi)
ディスク /dev/sde: 8590MB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: unknown
ディスクフラグ:
```

一台フォーマットしていないディスクが見えてきました。こいつをZFSプールに参加させます。

```sh
$ zpool replace tank /dev/sde -f
```

からのZFSプールの状態確認。

```sh
$ zpool status
  pool: tank
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz3-0  ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
            sde     ONLINE       0     0     0
            sdf     ONLINE       0     0     0
```

エラーが無くなりました。
パーフェクトだにゃん・・・

### おわり
素晴らしいZFSでした。非力なマシンだとかなりパフォーマンスが落ちるそうですが、マシンパワーに自信ニキであれば相当なパフォーマンスを発揮するそうです。耐障害性、堅牢性だけじゃなくて高速化を図るテクノロジーもいっぱい搭載してるらしい。
ちなみにホットスペアも検証してみました。そうしたらZFSファイルシステムが***壊れた。***しにてえ！以上です。

### 参考サイト様
>SIOS "OSSよろず" ブログ出張所
http://sios-oss.blogspot.jp/2012/02/zfs-on-linux1zfs-on-linux.html

>私をペロペロするがよい
http://lazy-dog.hatenablog.com/entry/2014/08/17/033038

>メモ帳みたいなもの
http://nsrzakki.hateblo.jp/entry/2013/04/09/010854

>Life with IT
http://l-w-i.net/t/solaris/zfs_001.txt

>ADWAYS ENGINEERS BLOG
http://blog.engineer.adways.net/archives/31692610.html

>[[zfs コマンド]] メモとかメモのようなものとか（By ルーキーの中のひと)
http://wiki.rookie-inc.com/serverapps/zfs/command

>hirotomium.com
http://hirotomium.com/memories/2011/11/zfsreplace.php