ZFS(Linux)のセットアップ手順

#セットアップ
##インストール(Ubuntu 20.04)
```
sudo apt install -y zfsutils-linux
```

##パーティションテーブルと必要に応じてパーティションの準備
###ドライブ一覧確認
```
ls /dev/disk/by-id
```

###ドライブごと（パーティションテーブルごと）zpoolに割り当てる場合
パーティションテーブルをGPTで作ります。台数分実行します。パーティションは作りません。zpoolを作成するときに自動的に作られます。

```
# HDD全体が論理消去されるため注意
dev=/dev/disk/by-id/xxx && # ドライブを指定
sudo sgdisk -Z -o $dev

  # -Z 既存のGPTとMBRを消去
  # -o GPT（GUIDパーティションテーブル）を作成
```

###パーティションをzpoolに割り当てる場合
ドライブごとのとき同様にパーティションテーブルをGPTで作った後、以下のようにZFS用のパーティションを作ります。台数分実行します。

```
dev=/dev/disk/by-id/xxx && # ドライブを指定
sudo sgdisk -a 8 -n 1:40: -t 1:bf01 $dev

  # -a 8 4Kセクターに合わせる (4096/512=8)
  # -n 1:40: 第1パーティションを作成（40セクターから）
  # -t 1:bf01 第1パーティションのタイプを設定（通常のext4なら8300）

sudo sgdisk -p $dev && # GPTを確認
sudo sgdisk -i 1 $dev # 第1パーティションを確認
```

パーティションテーブルとパーティションを一気に作成する場合は次のとおりです。

```
# HDD全体が論理消去されるため注意
dev=/dev/disk/by-id/xxx && # ドライブを指定
sudo sgdisk -Z -o -a 8 -n 1:40: -t 1:bf01 $dev
```

##zpoolに指定するIDの確認
###PARTUUIDで指定する場合
パーティションのUUIDを使います。ドライブごとzpoolに割り当てる場合には使えません。ランダムな文字列で分かりにくいですが、ドライブの接続方法が変わっても不変のため安定性があります。

```
array1=() &&
for partuuid in $(find /dev/disk/by-partuuid -type l); do
    dev=$(readlink -f "${partuuid}")
    id2=""
    for id in $(find /dev/disk/by-id -type l); do
        dev2=$(readlink -f "${id}")
        if test $dev = $dev2
        then
            id2="${id2} ${id}"
        fi
    done
    array1=("${array1[@]}" "${dev} ${partuuid}${id2}")
done&&
orig_ifs=$IFS; IFS=$'\n' &&
array2=($(echo "${array1[*]}" | sort)) &&
echo "${array2[*]}" &&
IFS=$orig_ifs
```
丸ごとコピーアンドペーストして実行してください。

###by-idで指定する場合
ドライブのシリアル番号を使います。個別のパーティションにも-part1、-part2とIDが振られるため、ドライブごとzpoolに割り当てる場合にも、パーティションをzpoolに割り当てる場合にも使えます。ドライブとの対応関係は分かりやすいですが、RAIDカードやUSB外付けボックスによって変わる可能性があります。

```
ls /dev/disk/by-id
```

##zpoolを作成
/data/<tankname>にマウントするが、canmount=offでそのマウントポイント自体はマウントから除外し、tankのひとつ下に作るファイルシステムから利用する設定（用途別に割り当て容量を制限する目論見）。

```
TANK=tankname &&
sudo mkdir -p /data &&
sudo zpool create -o ashift=12 -m /data/$TANK $TANK <raidz|mirror> /dev/disk/<id> /dev/disk/<id> &&
    # -o ashift=12でAFT対応。FreeBSDと違い自動判定してくれないため注意。
    # HDD1台のときはraidzを無指定にすること。複数台のときに無指定にするとストライピングになるため注意。
    # zpool createの直後に-fを付ける必要があるかもしれないが、慎重に付けること。
sudo zfs set compression=lz4 $TANK && # lz4による圧縮は高速なため有効化
sudo zfs set atime=off $TANK && # いちいち書き込みが必要になるわりにメリットが薄い最終アクセス時刻記録を無効化
sudo zfs set canmount=off $TANK && # 親ファイルシステムはマウントしない
sudo zfs set snapdir=visible $TANK && # スナップショットが.zfsディレクトリから見えるように

sudo zfs set acltype=posixacl $TANK && # ACLを有効化
sudo zfs set xattr=sa $TANK && # ACLに必要？

# 一括版
sudo mkdir -p /data &&
sudo zpool create -o ashift=12 -O compression=lz4 -O atime=off -O snapdir=visible -O canmount=off -O acltype=posixacl -O xattr=sa <tankname> <raidz|mirror> /dev/disk/<id> /dev/disk/<id>
sudo zfs set mountpoint=/data/<tankname> <tankname>
```

##zfsを作成
tankのひとつ下にファイルシステムを作る。

```
sudo zfs create <tankname>/<fsname>
sudo zfs set quota=1T <tankname>/<fsname> #割り当て容量を制限できる
```

##状況確認
```
zpool status #zpoolの確認
zfs list #zfsの確認
zfs list -t filesystem,snapshot,volume #同上（スナップショットを含む）

zpool get all #zpoolプロパティーの確認
zpool get all <tankname> #同上（特定のzpool）
zfs get all #zfsプロパティーの確認
zfs get all <tankname>/<fsname> #同上（特定のzfs）

zfs get compressratio #圧縮率の確認
zfs list -t snapshot -o name,creation #スナップショットの作成日時を確認
```

##削除（やり直したいとき）
```
sudo zfs destroy <tankname>/<fsname>
sudo zpool destroy <tankname>
```

#ファイル周り
##所有者とパーミッションを変更
```
path='/data/<tankname>/<fsname>'
sudo find $path -type d -name .zfs -prune -exec chown user:group {} + &&
sudo find $path -type d -name .zfs -prune -or -type d -exec chmod 770 {} + &&
sudo find $path -type d -name .zfs -prune -or -type f -exec chmod 660 {} +
```
スナップショットは読み取り専用で変更できないため、触りにいかないよう除外しています。

##ファイルをコピー後ハッシュをチェック
ZFS固有のものではありませんが、ついでに書いておきます。
###ハッシュを取得
```
#Windows上のMobaXterm
cd /drives/c/Users/suzuki/Documents &&
find . -type f -exec md5sum {} \; > ./Documents.md5sum

#Linux
cd /data/tank/suzuki/Documents &&
find . -type f -exec md5sum {} \; > ./Documents.md5sum
```

###テキストを整形してdiffを取る
正規表現置き換えでs/^([^ ]+)  (.+)$/\2 \1/としてハッシュとファイル名をひっくり返します（ファイル名が先になるように）。その後、行をソートして、diffを取ります。

#チューニング・障害対応
##ARC（メモリキャッシュ）の設定
FreeNASではZFSを扱うとき8GBのメモリーを搭載していることを条件にしているようです。そのためここでは8GBからシステム用に1GB引いた7GBに設定しています。

###状況確認
```
cat /proc/spl/kstat/zfs/arcstats #pがARCの使用量、c_maxが上限
```

###上限設定
```
echo 7516192768 > /sys/module/zfs/parameters/zfs_arc_max #ARCの上限を7GBに設定
```

###上限設定（永続化）
再起動しても上限設定が続くようにするには/etc/modprobe.d/zfs.confに下記の行を書き込みます。

```
echo 'options zfs zfs_arc_max=7516192768' > /etc/modprobe.d/zfs.conf
```

###自動トリムをオンにする（デフォルトはオフ）
SSDでは基本的にオンにすべきです。Western DigitalのDM-SMRドライブはHDDながらトリムがサポートされていますが、データ損失の問題を軽減するために自動トリムはオフにすることが推奨されています。 [Update: WD Red SMR Drive Compatibility with ZFS | TrueNAS Community](https://www.truenas.com/community/threads/update-wd-red-smr-drive-compatibility-with-zfs.88413/)

```
sudo zpool set autotrim=on <tankname>
```

###ドライブによるトリムのサポート有無やトリムの実行状況を確認する
```
zpool status -t
```


##zpool statusで一部のHDDがUNAVAILになったとき
ドライブ自体が/devから確認できるなら、下記で復旧できる場合があります。

```
sudo zpool online <tankname> /dev/disk/by-partuuid/<id>
```

##cronで異常を通知
/etc/cron.hourly/zpool_statusに下記の内容でファイルを作成します。異常の際、標準出力に現状を出力します。標準出力があればメールが飛ぶようにcronを設定しておいてください。

```
#!/bin/bash
status=$(zpool status -x 2>&1)
if [ 'all pools are healthy' != "$status" -a 'no pools available' != "$status" ]; then
    echo $status
fi
```
ファイルを作成したら実行属性を付けます。

```
chmod u=rwx,g=rx,o=rx /etc/cron.hourly/zpool_status
```