BtrfsによるNASの運用記録

# 概要

これまでの，数値計算の結果がHDDを圧迫しており，データの引っ越しやバックアップを含め運用方法を検討し[btrbk](https://github.com/digint/btrbk)を用いたsoftware RAIDによるNASの構築ということに至ったのでその運用記録である．必要に応じて加筆を続ける．

# 背景
## ハードウェアRAID か ソフトウェアRAIDか

RAIDの選択しとしてハードウェアRAID(RAID card)とソフトウェアRAIDの選択肢があるが，以下の理由にソフトウェアRAIDを選択した．

* check sumによる整合性がとられていない場合がある
* raid levelの変更が容易ではない
* 新品は高い(ebayで中古なら数千円)

とくにcheck sumによるファイル整合性については[Level1Tech Pert1.](https://www.youtube.com/watch?v=yAuEgepZG_8&list=LL3nlomW9_2matntzw2WWMbw&index=18&t=0s)と[Pert2.](https://www.youtube.com/watch?v=pv9smNQ5fG0)で興味深い考察(実験)がされている．そこで，言及されているように，ハードウェアRAID(youtubeではmega raid)はcheck sumによるファイル整合性がとられていない場合が有るようだ．

端的に言うと，RAID1で運用して，何らかのイベント(宇宙線，電磁ノイズ，高温，あるいは内・外的要因)で片方のデータが書き換えられてしまった場合，どちらのデータが正しいかチェックする機構がないとのこと．RAID5でも同様で，parityからデータを復元する際にどのHDDを信頼すればよいか判断できないという事が指摘されている．

これらの問題を解決したハードウェアRAIDコントローラがあってもおかしくないと思うが，私は知らないし実在しても安価にではないと思われる．

## zfs か btrfsか

主要なソフトウェアRAIDにzfs, btrfがしばし挙げられる．

## zfs

[zfs](https://wiki.archlinux.jp/index.php/ZFS)はOracle Solarisのファイルシステムで，[zfs on linux](https://zfsonlinux.org/)としてlinuxでも使用することができるが，ライセンス上の問題があるとしてしばし議論されているが，こことでは立ち入らない．

ubuntu server 18.04ではapt経由でzfsを標準のリポジトリからインストールすることができ，ubuntu 19.10や20.04ではroot on zfsがexperimentalとしてインストーラーに同梱されている．

zfsはドキュメントも多く[ArchWiki](https://wiki.archlinux.jp/index.php/ZFS)やある[Blog](http://blog.programster.org/zfs-create-disk-pools)あるいは[Oracle](https://docs.oracle.com/cd/E19253-01/819-6260/index.html)が参考になるとおもう．

結果としてNASではzfsを使用しなかった．主な理由として，

1. RAID-Zはraid poolをdestroyしないとRAIDレベル変更ができない
2. strip pool (raid0)ではディクス追加可能だがデフラグメント機能がない
3. 容量の異なるHDDを扱えない
4. zfs-auto-snapshotによるスナップショットの自動取得はあるものの，backupの自動化ツールが無さそう．

4に関しては，znapzendというのも有るらしいが，1.-3.の要件を満たさないのでNASのファイルシステムをzfsで運用することは無かった．

ただ，desktop環境ubuntu 20.04のexperimental機能である，root on zfsを導入して見たところ，非常に使い勝手が良くメモリもそれほど使用しないので(oracleとの訴訟問題になるかもしれないが)ubuntuのzfsサポートには期待したいところである．ただしroot on zfsのinstall時に，ディレクトリ全体に暗号化をかけるというオプションはなかった．スナップショットによるrevertはランサムエウェアなどのクリプトマルエウェアへの強力な対策なので，rootディレクトリの暗号化にも対応してもらえるとより強力になると思うので，ライセンスの問題と含めて今後の進展に期待している．

一応良い点を挙げておくと，

1. 高信頼性 (btrfsにはraid5/6に既知の問題がある)
2. Btrfsよりperfomanceが優れるらしい．たとえば[ZFS, BTRFS, XFS, EXT4 and LVM with KVM – a storage performance comparison](http://www.ilsistemista.net/index.php/virtualization/47-zfs-btrfs-xfs-ext4-and-lvm-with-kvm-a-storage-performance-comparison.html?start=6)．(ただし[Linux 5.4 EXT4 / XFS / Btrfs RAID Performance On Four HDDs](https://www.phoronix.com/scan.php?page=article&item=linux54-hdd-raid&num=3)をみると環境とテストに依存しそう)．
3. キャッシュによるパフォーマンスの向上が望める[ZFSはどう活用できるか](https://www.atmarkit.co.jp/ait/articles/0905/28/news108.html)
4. 複数の圧縮アルゴリズムが利用可能
5. 暗号化やファイル割当(quota)が容易
6. nfsなどがzfsの側の設定で可能

ただ，2, 3，に関してはローカルネットワークが1GBase-Tなので定格で125MB/sec程度しか転送できないので，その恩恵は体感できなかった．

使用したことはないが[freeNAS](https://www.freenas.org/)はZFSを採用している．

## btrfs

btrfsはzfsよりずっと新しいファイルシステムで[ArchWiki](https://wiki.archlinux.jp/index.php/Btrfs)や公式の[Wiki](https://btrfs.wiki.kernel.org/index.php/Main_Page)が参考になる．

良い点として

1. ライセンスの問題がない
2. 柔軟にRAIDレベルの変更ができる
3. ディスク追加やRAIDレベル変更した際のデフラグメントをサポート
4. 任意のタイミングで圧縮アルゴリズムをでフラグメントによって変更可能
4. btrbkによる自動スナップショット，自動バックアップが容易

悪い点として

1. RAID5/6に[write holeの問題](https://btrfs.wiki.kernel.org/index.php/RAID56)がある
2. ssdをキャッシュデバイスとして使用するためにはbcacheと組み合わせる必要がある(関係が有るのかどうか知らないがbcachefsというファイルシステムが現れた)
3. CentOS 8 でディスコンにされた
4. 公式のWikiが分かりづらい
5. ファイル割当(quota)に不具合があった(後述)

ただし私の環境では

* btrfsに限らずRAID5/6はrebuild時に，HDDに高負荷な影響を与え，故障する確率が高くなるそうなので利用しない．
* NASの場合，1GBase-TのNetworkがボトルネックとなっているので，必要性を感じない．

というと事からデメリットは現在のところあまり感じていない．

なお，使用したことはないが[unraid](https://unraid.net/)はbtrfsを採用している．
(macはapplefsにsnapshotがサポートされた．ファイルシステムははbtrfsと同じB treeアルゴリズム)

# 環境

NAS用HDDとして[東芝 MNシリーズ](https://toshiba.semicon-storage.com/jp/storage/product/internal-specialty/nas/articles/mn07aca-series.html)が安価なので利用する．恐らくToshiba MG06ACA800Eが最もコストパフォーマンスが良く，10TBより大きな容量はヘリウム充填となり高価になる．なおWestan degital RADがNAS用HDDとして売られているのにもかかわらず，[RAIDに不向きなSMR方式を採用している問題が指摘](https://gigazine.net/news/20200416-wd-red-hdd-smr/)されているが，東芝が[SMR方式を採用しているHDDのリストは公開](https://toshiba.semicon-storage.com/ap-en/company/news/news-topics/2020/04/storage-20200428-1.html)されており，消去法的にMNシリーズはRAIDに有利なCMR方式を採用しているように読める．

debian系でbtrfsのinstall は

```
# apt install btrfs-progs
```

本番環境

* OS : Debian 10
* CPU: intel core i3-6100
* RAM: DDR4 16GT (非ECC)
* HDD1: TOSHIBA MN06ACA10T HDD [10TB] 
* HDD2: TOSHIBA MN06ACA10T HDD [10TB]
* HDD3: Seagate ST8000DM004-2CX188 [8TB]

HDD1, HDD2 をraid 0としてlzo圧縮をかけdata poolとしてnfsを運用．ちなみにcore i3はなぜかECC対応だったりする．現環境では非ECCだが．

```
# btrfs filesystem show /data/
Label: 'data'  uuid: a5eeeef7-4f23-48c8-8eaf-a357fc0e6783
	Total devices 2 FS bytes used 5.01TiB
	devid    1 size 9.10TiB used 5.01TiB path /dev/sdd
	devid    2 size 9.10TiB used 5.01TiB path /dev/sda
```

HDD3はbackup用にzstd圧縮をかけ運用

```
$ btrfs filesystem show /backup/
Label: 'backup'  uuid: aeca9446-e211-469c-b4f2-1356be6b02bc
	Total devices 1 FS bytes used 5.96TiB
	devid    1 size 7.28TiB used 6.05TiB path /dev/sdb
```


なお圧縮レベルはいずれもデフォルトで圧縮によるperformanceは[What are the differences between compression methods?](https://btrfs.wiki.kernel.org/index.php/Compression)や[btrfs: Add zstd support
](https://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs.git/commit/?h=next&id=5c1aab1dd5445ed8bdcdbb575abc1b0d7ee5b2e7) が詳しい．

snapshotの取得とbackupは[btrbk](https://github.com/digint/btrbk)を使う(aptでinstall可能)．


実験環境として

* OS : Debian 10
* CPU: AMD E2-3200 (2010年ごろのものだと思われる)
* RAM: DDR3 4GB
* HDD1: Western Digital Green WD30EZRX [3TB]
* HDD2: Hitachi Ultrastar A7K2000 [1TB]
* HDD3: Seagate Barracuda 3.5 ST2000DM006 [2TB]
* HDD4: HGST HCC541010A9E680 [1TB]

HDD1-HDD4をRAID10としてlstd圧縮でsamba poolとして運用．この場合，1TBのデバイスとしてマウントされる．

```bash
# btrfs filesystem show /samba-pool/
Label: 'btrfs-pool'  uuid: 7b1f20bf-5944-48a6-98e1-6e267168d0b3
	Total devices 4 FS bytes used 900.24GiB
	devid    1 size 931.51GiB used 452.00GiB path /dev/sdb
	devid    2 size 931.51GiB used 450.01GiB path /dev/sdc
	devid    3 size 2.73TiB used 453.01GiB path /dev/sdd
	devid    4 size 1.82TiB used 451.00GiB path /dev/sde
```


# 運用記録

## quotaのトラブル

実験環境下でquota を使ってsubvolumeの上限を100Gにかえる

```
# btrfs qgroup limit 100G <subvol_path>
```

ということをしたところ，btrfs-transacti(on?)とbtrfs-cleanerがCPU使用率100%になってまともに書き込みできなくなり，その後kernel crashするという問題が生じた．8GBへ換装後，クラッシュすることは無くなったがbtrfs-transactiが大量に発生しパフォーマンスが低い状態となるので，quotaは使用しないほうが良いかもしれない．

実際ArchWikiの[quota](https://wiki.archlinux.jp/index.php/Btrfs#.E3.82.AF.E3.82.A9.E3.83.BC.E3.82.BF)では

>警告: Qgroup はまだ安定状態ではなくサブボリュームのスナップショットとクォータを組み合わせると操作によってパフォーマンスに問題をきたします (スナップショットの削除など)。さらに 既知の問題 が存在しています。

とあるので，quotaを無効化しておく．

```
# btrfs quota disable <subvol_path>
```

quotaを使わなければ実験環境の低スペックマシンでも問題なく運用することができた．

## zstd圧縮によるトラブル

本番環境

```
# btrfs filesystem show /data/
Label: 'data'  uuid: a5eeeef7-4f23-48c8-8eaf-a357fc0e6783
	Total devices 2 FS bytes used 5.01TiB
	devid    1 size 9.10TiB used 5.01TiB path /dev/sdd
	devid    2 size 9.10TiB used 5.01TiB path /dev/sda
```

現在はlzo圧縮で運用しているが，圧縮アルゴリズムをzstdに変更すると5TBのデータが2TB弱まで圧縮される．これ幸いとzstd圧縮で数ヶ月運用していたが，nfsとの相性が良くなかった．

具体的には，data以下のディレクトリをnfsを非同期オプションで(async)で運用していた．リモート先でdataをマウントしてプログラムファイルの編集をし，異なるリモートで実行したところ，ファイルの更新がされていないという状況が頻発した．ファイルをcatするなどすると更新される．nfsを同期(sync)オプションで運用したところ，前述の問題は生じなかったもののパフォーマンスが著しく低下してしまった．

btrfsの圧縮アルゴリズムをlzoにしたところ，nfsを非同期オプションで(async)で運用してもファイルの更新に時間が掛かるの問題は現在起きていないのでlzoで運用することにした．

## load averageが高い

ある日を境に，本番環境のload average が15程度ある．

```
# iostat -x
Linux 4.19.0-10-amd64 (eular) 	08/19/2020 	_x86_64_	(4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.98    0.82   10.64   66.12    0.00   20.44

Device            r/s     w/s     rkB/s     wkB/s   rrqm/s   wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util
nvme0n1          4.94   10.53     37.18    142.95     0.00     5.21   0.00  33.09    0.02    0.81   1.01     7.53    13.58  64.44  99.66
sda             79.81    3.50  23940.68    221.91   164.24     2.53  67.30  41.94   34.94   12.10   2.58   299.96    63.38   2.39  19.95
sdd             89.33    3.44  24979.15    221.91   179.70     2.59  66.80  42.94   36.33   16.43   3.08   279.64    64.48   3.10  28.74
sdb            142.77   43.87  29283.33   4244.05   129.90    70.13  47.64  61.52   51.04  166.47  14.19   205.11    96.74   5.30  98.87
```

をみるに%iowaitが60%強もあり高め．具体的なプロセスを確認するため

```
# pidstat -dhIl
Linux 4.19.0-10-amd64  	08/19/2020 	_x86_64_	(4 CPU)

# Time        UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
02:55:23 PM     0         1  77675.45     14.06      0.06       3  /sbin/init 
03:01:52 PM     0      2492      1.03      0.32      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:02:01 PM     0      2511   1118.26      0.00      0.00 4293272  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200812T0017 
(省略)
```

をながめるとbtrfs関係のコマンドでiodelayの数値が大きくなっている．よくよく見ると

```
# pidstat -dhIl | grep -e "btrbk" -e "btrfs"
# Time        UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
(省略)
03:01:52 PM     0      2492      1.03      0.32      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0      6430      0.00      0.04      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0      7025      0.07      0.29      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0     11531      0.04      0.26      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0     16036      0.04      0.19      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0     19403      0.01      0.02      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0     20587      0.04      0.22      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:01:52 PM     0     31189      0.01      0.05      0.00       0  /usr/bin/perl /usr/sbin/btrbk -c /etc/btrbk/btrbk.conf -q run 
03:02:01 PM     0      2511   1118.26      0.00      0.00 4293272  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200812T0017 
03:02:01 PM     0      6445    712.17      0.00      0.00 3500343  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200817T0017 
03:02:01 PM     0      7040   1295.01      0.00      0.00 3099643  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200813T0017 
03:02:01 PM     0     11546   1260.44      0.00      0.00 3177812  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200814T0017 
03:02:01 PM     0     16051    817.53      0.00      0.00 4124371  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200815T0017 
03:02:01 PM     0     19417    481.58      0.00      0.00 1787004  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200818T0017 
03:02:01 PM     0     20601    791.50      0.00      0.00 3951755  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200816T0017 
03:02:01 PM     0     31204     68.50      0.00      0.00  752956  btrfs send -p /data/snapshots/nfs.20200803T0017 /data/snapshots/nfs.20200819T0017 
```

btrbkで1時間毎にsnapshotを作成しているのだが，nfs.20200803T0017のスナップショットが別の名前で何度もsendされている．なぜこの様な状況になっているのか，理解できないがprocess をkillしないとsnapshotを削除できないので

```
# pidstat -dhIl | grep "btrfs send" | awk '{print $4}'
```

して問題を起こしたsnapショットを削除しておく．

```
# btrfs subvolume delete -c /data/snapshots/nfs.20200803T0017
Delete subvolume (commit): '/data/snapshots/nfs.20200803T0017'
```

これでload averageが3程度まで落ち着いた．それでも普段(1以下)より高く

```
# pidstat -dhIl | grep -e "btrbk" -e "btrfs"
# Time        UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
04:00:49 PM     0       542      0.00      0.00      0.00       0  btrfs-delalloc
04:00:49 PM     0       572      0.00      0.03      0.00       0  btrfs-delalloc
04:00:49 PM     0       721      0.01      0.10      0.00     502  btrfs-cleaner
04:00:49 PM     0       722      0.07      4.86      0.00   26411  btrfs-transacti
04:00:49 PM     0      1434      0.21      0.98      0.00   26850  kworker/u8:6-btrfs-freespace-write
04:00:49 PM     0      1724      0.13      0.23      0.00    3423  btrfs-cleaner
04:00:49 PM     0      1725      1.74     85.49      0.00  280897  btrfs-transacti
04:00:49 PM     0      1736      2.09      2.58      0.00   67700  btrfs-cleaner
04:00:49 PM     0      1737     12.79    175.22      0.00 18215402  btrfs-transacti
04:00:49 PM     0      5003      0.06      0.37      0.00    9639  kworker/u8:15-btrfs-endio-meta
04:00:49 PM     0      6124      0.05      0.25      0.00    6927  kworker/u8:16-btrfs-freespace-write
```

btrfs-transacti(on?)が足をひっぱている気がする．似たような事例でhttps://myn.meganecco.org/1480038240.html でbtrfs のiowaitについて調査してあるのを発見する．dstatというコマンドがあることはが知らなかったので，導入してみると

```
# dstat --top-bio -d -n -c 
----most-expensive---- -dsk/total- -net/total- --total-cpu-usage--
  block i/o process   | read  writ| recv  send|usr sys idl wai stl
systemd      75M   14k|  83M 5227k|   0     0 |  3  10  21  66   0
badblocks    10M    0 |  10M   62M|  70B  826B|  1   2  57  40   0
badblocks  5120k    0 |5328k 2016k|  70B  466B|  2   1  73  24   0
badblocks  6144k    0 |6304k 2272k|  70B  338B|  1   1  62  36   0
...
```

badblocksには思いたるふしがって，backup用HDD

* HDD3: Seagate ST8000DM004-2CX188 [8TB]

が不良セクタが生じたのでその調査でbadblocksコマンドをbackgroundで動かしていたのを忘れていた．
load average が3程度あるのはおそらくその影響だろう．実際bacblocksが終了するとload averageが1以下に落ち着いた．

## HDD不良

上述でも言及したがSegete ST8000DM004に不良セクタが生じた．smartctlの主要な情報を抜き出すと

```
SMART Attributes Data Structure revision number: 10
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
  5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -       0
  9 Power_On_Hours          0x0032   087   087   000    Old_age   Always       -       11585 (151 153 0)
197 Current_Pending_Sector  0x0012   100   100   000    Old_age   Always       -       0

SMART Error Log Version: 1
ATA Error Count: 8 (device log contains only the most recent five errors)

SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Interrupted (host reset)      00%     11538         -
# 2  Short offline       Completed without error       00%     11507         -
# 3  Extended offline    Completed: read failure       50%     11492         -
```

Extended offline testで不良セクタがあると報告されてCurrent_Pending_SectorのRAW_VALも10程度となっていたはずだが，short offlineテストをpassしたせいか Current_Pending_SectorのRAW_VALが0になっていた(単調増加だと思っていが・・・)．しかしながら，btrfsからデバイスの状態を見ると，

```
# btrfs device stats /backup/
[/dev/sdb].write_io_errs    0
[/dev/sdb].read_io_errs     2
[/dev/sdb].flush_io_errs    0
[/dev/sdb].corruption_errs  13
[/dev/sdb].generation_errs  0
```

となっているので異常が出ているのは間違いない．約1年半ほぼ無休運用．やはりNAS用を買うべきだったかもしれない．8TBあると[badblocks](https://wiki.archlinux.jp/index.php/Badblocks)コマンドがそのまま動いてくれないので

```
# badblocks -b 4096 -vs -o badblocks.txt /dev/sdb
```

として不良セクタの調査をする．得られたデータから

```
# fsck -l badblocks.txt /dev/sdb
```

とすれば不良セクタのマーキンが完了する．なお上述のコマンドは

```
# fsck -t -y -f -c /dev/sdb
```

とすれば一行で終わらせる・・・．というのが一般的だが，btrfsの場合fsckを実行すると

```
# fsck -t -y -f -c /dev/sdb
fsck from util-linux 2.33.1
If you wish to check the consistency of a BTRFS filesystem or
repair a damaged filesystem, see btrfs(8) subcommand 'check'.
```

となる．mountしたままだとcheckコマンドが動かない．不良セクタを出したHDDではraidを組んでいなかったので，umountしてcheckを実行する．

```
# sudo umount /backup 
# btrfs check /dev/sdb 
Opening filesystem to check...
Checking filesystem on /dev/sdb
UUID: aeca9446-e211-469c-b4f2-1356be6b02bc
[1/7] checking root items
```

実行後にbtrfs checkコマンドについて調べて見たところ，ArchWikiに[btrfs check](https://wiki.archlinux.jp/index.php/Btrfs#btrfs_check)に

> 警告: Btrfs はまだ開発途上であり、特に btrfs check コマンドについては仕様が固まっていないので、--repair スイッチを付けてbtrfs check を実行するときはあらかじめ Btrfs のドキュメントを読んでバックアップを作成することを強く推奨します。

との記述を発見．repairをかける分けじゃないし，まあ壊れてもよいバックアップディスクなので，とりあえず様子見．その結果，foregroundでもbackgroundで動かしても

```
[1/7] checking root items
[2/7] checking extents
```

あたりでプロセスがkillされてbtrfs checkを完了できない．google先生によるとOOM(メモリが足りな)でkillされる場合があるようだ．muninではそれらしい兆候は確認できなかったが念の為low memroy modeで動かして様子見．

```
$ btrfs check -p --mode=lowmem /dev/sdb
``