RAID-Z しんどなってきたので

Introduction

あけましておめでとうございます。
正月に帰省もせず家に引きこもって狂ったようにストレージサーバの構築を模索しておりました。

大容量ストレージサーバを構築する際に RAID を用いるケースはよくあると思います。
ですが、複数台の Disk 故障、突然の電源断、あるいはオペミス等が原因で Disk Array 自体に回復不能な障害が発生する可能性もゼロではありません。
RAID-Z は、ライトホール問題をも完封する極めて高い堅牢性を備えた ZFS のソフトウェア RAID ですが、10TB を超える Disk Array ともなってくると RAID の操作には慎重にならざるを得ません。
いくら RAID-Z といえども、RAID 崩壊によるデータの全消失はなんとしても避けたいものです。
そこで、SnapRAIDmergerfs を使用した、柔軟かつ堅牢なストレージサーバを検証することにします。

というよりも、FreeBSD が分からなすぎて XigmaNAS 使い続けるのが怖いというのが大きいかもです。
ZFS on Linux という選択もアリでしたが、この際だから RAID もやめっかと思い立った次第です。
リアルタイム同期でなくても特に困るワケでもないですからね。

SnapRAID

SnapRAID は、RAID (5, 6, あるいはそれ以上の冗長性) を模倣する Disk Array のバックアップツールです。(模倣と記述した通り、厳密には RAID ではないことに留意すべきです)
Disk Array 全体の Parity を別の異なる Disk へとバックアップすることで、最大で6台の Disk 障害から回復することができます。
SnapRAID は JBOD で構成されるため、個々の Disk に対して直接アクセスすることが可能です。そのため、仮に Disk の故障が Parity Disk で許容できなかった場合でも、Disk Array 全体が回復不能になることはありません。

Pros

  • 高い柔軟性
    • Data/Parity Disk を後から追加できる
    • 容量の異なる Disk を Data Disk に含められる
  • 高い堅牢性
    • 最大6台の Disk 障害から回復可能
    • Parity で許容できない Disk 故障が発生しても、回復不能になるのは該当する故障 Disk のみである

Cons

  • Parity 更新はリアルタイムではない
    • snapraid sync コマンドによる手動更新が必要
    • Parity を更新するまで、新規に追加/変更されたファイルは保護されない

環境準備, インストール

使用する OS は Rocky Linux 8.4 とし、Vagrant と VirtualBox にて検証していきます。
https://github.com/voltaxic-rift/snapraid-mergerfs-sandbox

RPM とか無いので (あっても古い) ソースからビルドしましょう
https://github.com/amadvance/snapraid/blob/master/INSTALL

1
2
3
4
5
6
7
8
sudo dnf install -y gcc make
curl -LO https://github.com/amadvance/snapraid/releases/download/v12.0/snapraid-12.0.tar.gz
tar zxvf snapraid-12.0.tar.gz
cd snapraid-12.0/
./configure
make
make check
sudo make install

Install 後は Sample の設定ファイルを /etc に設置します。

1
2
3
4
5
sudo cp ./snapraid.conf.example /etc/snapraid.conf

# ソース削除
cd ..
rm -rf ./snapraid-*

Disk セットアップ

SnapRAID で使用するための Disk を準備します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sudo parted -a optimal -s /dev/sdb -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdc -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdd -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sde -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdf -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdj -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1

sudo mkdir -p /mnt/data/disk{1..5}
sudo mkdir -p /mnt/parity/disk1

sudo mkfs.xfs /dev/sdb1
sudo mkfs.xfs /dev/sdc1
sudo mkfs.xfs /dev/sdd1
sudo mkfs.xfs /dev/sde1
sudo mkfs.xfs /dev/sdf1
sudo mkfs.xfs /dev/sdj1

sudo blkid -s UUID -o value /dev/sd{b..k}1 | head -5 | xargs -I% echo "UUID=% /mnt/data/disk xfs defaults 0 2" | awk '{print $1,$2 NR,$3,$4,$5,$6}' | sudo tee -a /etc/fstab
sudo blkid -s UUID -o value /dev/sd{b..k}1 | tail -1 | xargs -I% echo "UUID=% /mnt/parity/disk xfs defaults 0 2" | awk '{print $1,$2 NR,$3,$4,$5,$6}' | sudo tee -a /etc/fstab

sudo mount -a

SnapRAID 設定

/etc/snapraid.conf の中身を、下記のように変更します。
data/disk{6..8}, parity/disk2 は後ほど追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
parity /mnt/parity/disk1/snapraid.parity
# 2-parity /mnt/parity/disk2/snapraid.2-parity

content /var/snapraid.content
content /mnt/data/disk1/snapraid.content
content /mnt/data/disk2/snapraid.content
content /mnt/data/disk3/snapraid.content
content /mnt/data/disk4/snapraid.content
content /mnt/data/disk5/snapraid.content
# content /mnt/data/disk6/snapraid.content
# content /mnt/data/disk7/snapraid.content
# content /mnt/data/disk8/snapraid.content

data d1 /mnt/data/disk1/
data d2 /mnt/data/disk2/
data d3 /mnt/data/disk3/
data d4 /mnt/data/disk4/
data d5 /mnt/data/disk5/
# data d6 /mnt/data/disk6/
# data d7 /mnt/data/disk7/
# data d8 /mnt/data/disk8/

exclude *.unrecoverable
exclude /lost+found/

blocksize 64
# hashsize 16

parity

Parity ファイルの保存先になるため、Parity 用の Disk を指定します。

2-parity

N-Parity による冗長性を持たせる場合は 2-parity, 3-parity などを使用して Parity を冗長化させます。
Parity の冗長化は、最大で6台 (6-parity) まで設定することができます。
N-Parity を設定することで、最大で N 台の Disk を回復させることができます。

content

SnapRAID の Content ファイルの保存先を指定します。
Content には、SnapRAID 管理下にある Disk の UUID と全てのファイルのリストとハッシュ, タイムスタンプ等が格納されています。

data

SnapRAID で扱う Data Disk を全て記述します。
d1 というのは Disk の識別名に過ぎず、名前は何でも構いません。
ここに記載した Data Disk に保存されているファイルが SnapRAID による保護の対象になります。

exclude

SnapRAID にて保護しないファイルを Glob Pattern で記述します。
exclude にマッチするファイルは SnapRAID による保護を受けず、Parity 計算の対象にはなりません。

blocksize

デフォルトは 256 ですが、この設定は SnapRAID Disk Array に対してどのようなファイルを格納するかによって適切に変更すべきです。
単位は KiB となっており、保護対象のファイルサイズとファイル数に応じて Parity 領域を Blocksize 単位で確保します。
例えば、ひとつの Data Disk に 10GB のファイルを10個置いた場合、 ((1024^4 * 10) / (1024^2 * 256)) * 10 = 409600 個の Block が確保され、107374182400Byte (100GB) が消費されます。
ただし、10台の Data Disk にファイルを分散させた場合は 10G しか消費されません。Parity のサイズは、消費しているデータ量が最も多い Disk と同じ分を消費します。そのため Parity Disk の容量は、単一の Data Disk の最大容量と同等かそれ以上である必要があります。

2Bytes のファイルを10個置いた場合はどうでしょうか。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@localhost disk1]# hexdump -C ../../parity/disk1/snapraid.parity 
00000000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00010010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00020000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00020010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00030000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00030010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00040000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00040010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00050000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00050010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00060000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00060010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00070000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00070010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00080000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00080010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00090000 61 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |a...............|
00090010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000a0000
[root@localhost disk1]# stat -c '%s' ../../parity/disk1/snapraid.parity
2621440

1ファイル毎に 256KiB が確保されるため、 ((1024^2 * 256) * 10) / 1024 = 2621140 (2.5MB) が Parity として確保されます。
このような小さなファイルが大量に保存されている環境では、Blocksize を小さくしないと Parity のサイズが著しく肥大化してしまいます。
Parity のサイズが Parity Disk の容量を超過すると、それ以降に保存されたファイルは SnapRAID による保護の対象になりません。
保存するファイルの特性に応じて、Blocksize は適切に決定する必要があります。
ただし、Blocksize を小さくすると Parity の計算に時間が掛かったり、消費するメモリ量が多くなったりします。
SnapRAID 実行時に消費されるメモリ量は、下記の式で表すことができます。

1
2
3
4
5
6
7
8
TS*(1+HS)/BS

TS: Data Disk 全体の合計使用量 (Bytes)
HS: Hashsize (Bytes)
BS: Blocksize (Bytes)

# 14TB 8台、Hashsize 16B, Blocksize 64Kib で消費される最大メモリ量
((1024^4 * 14 * 8) * (1 + 16) / (1024*64)) / 1024^3 = 29.75GB

今回は Blocksize を 64KiB としています。

hashsize

各ファイルのハッシュを Blocksize 単位で計算する際に出力するハッシュサイズです。
単位は Byte で、デフォルトは 16、つまり 128bit でハッシュされます。
Hashsize を短くしてしまうと整合性の検証に支障が出てしまう恐れがあります。
デフォルトの 16 以上に増やすことはできないため、このままにしておきましょう。

動作確認

この時点で snapraid sync による Disk Array のスナップショットの作成が可能になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid sync
Self test...
Loading state from /var/snapraid.content...
WARNING! Content file '/var/snapraid.content' not found, trying with another copy...
Loading state from /mnt/data/disk1/snapraid.content...
WARNING! Content file '/mnt/data/disk1/snapraid.content' not found, trying with another copy...
Loading state from /mnt/data/disk2/snapraid.content...
WARNING! Content file '/mnt/data/disk2/snapraid.content' not found, trying with another copy...
Loading state from /mnt/data/disk3/snapraid.content...
WARNING! Content file '/mnt/data/disk3/snapraid.content' not found, trying with another copy...
Loading state from /mnt/data/disk4/snapraid.content...
WARNING! Content file '/mnt/data/disk4/snapraid.content' not found, trying with another copy...
Loading state from /mnt/data/disk5/snapraid.content...
No content file found. Assuming empty.
WARNING! With 5 disks it's recommended to use two parity levels.
Scanning...
Scanned d3 in 0 seconds
Scanned d2 in 0 seconds
Scanned d1 in 0 seconds
Scanned d4 in 0 seconds
Scanned d5 in 0 seconds
Using 0 MiB of memory for the file-system.
Initializing...
Resizing...
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Verifying...
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
Nothing to do
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
[rocky@localhost ~]$ echo 'UNCHI!' | sudo tee /mnt/data/disk1/gomi.txt
UNCHI!
[rocky@localhost ~]$ ll /mnt/data/disk1
total 8
-rw-r--r--. 1 root root 7 Jan 2 03:46 gomi.txt
-rw-------. 1 root root 88 Jan 2 03:43 snapraid.content
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid sync
Self test...
Loading state from /var/snapraid.content...
WARNING! With 5 disks it's recommended to use two parity levels.
Scanning...
Scanned d5 in 0 seconds
Scanned d3 in 0 seconds
Scanned d4 in 0 seconds
Scanned d2 in 0 seconds
Scanned d1 in 0 seconds
Using 0 MiB of memory for the file-system.
Initializing...
Resizing...
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Verifying...
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
Using 48 MiB of memory for 128 cached blocks.
Selecting...
Syncing...
100% completed, 1 MB accessed in 0:00

d1 12% | *******
d2 0% |
d3 0% |
d4 0% |
d5 9% | *****
parity 0% |
raid 2% | *
hash 0% |
sched 83% | ***************************************************
misc 0% |
|______________________________________________________________
wait time (total, less is better)

Everything OK
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Verifying...
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
[rocky@localhost ~]$ sudo hexdump -C /var/snapraid.content
00000000 53 4e 41 50 43 4e 54 32 0a 03 00 00 7a 00 00 84 |SNAPCNT2....z...|
00000010 78 81 63 6b 92 e3 4d 52 9f 7a 01 f3 4b 83 16 2c |x.ck..MR.z..K..,|
00000020 62 08 9c e3 4d 82 64 31 80 60 77 ff 45 01 ff a4 |b...M.d1.`w.E...|
00000030 30 66 65 38 62 34 36 30 2d 62 36 66 65 2d 34 63 |0fe8b460-b6fe-4c|
00000040 34 61 2d 61 65 63 37 2d 37 31 33 66 32 34 35 35 |4a-aec7-713f2455|
00000050 62 63 33 34 50 80 60 77 ff 44 01 ff a4 64 63 66 |bc34P.`w.D...dcf|
00000060 38 65 37 63 62 2d 62 34 34 66 2d 34 39 37 38 2d |8e7cb-b44f-4978-|
00000070 39 65 34 62 2d 63 64 65 31 62 36 63 61 62 61 30 |9e4b-cde1b6caba0|
00000080 63 66 80 87 1d 40 44 0e 86 6c 07 6b ad 04 81 88 |cf...@D..l.k....|
00000090 67 6f 6d 69 2e 74 78 74 62 80 81 50 55 53 fe 93 |gomi.txtb..PUS..|
000000a0 56 b2 5c 20 ca fc 58 63 16 26 16 68 80 81 4f 69 |V.\ ..Xc.&.h..Oi|
000000b0 30 40 44 0e 86 81 89 80 4e 4f b0 eb bd |0@D.....NO...|
000000bd
[rocky@localhost ~]$ sudo hexdump -C /mnt/parity/disk1/snapraid.parity
00000000 55 4e 43 48 49 21 0a 00 00 00 00 00 00 00 00 00 |UNCHI!..........|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000

mergerfs

mergerfs は、mhddfs, aufs, overlayfs のように、複数のディレクトリを透過的に重ねることができるファイルシステムです。
SnapRAID を使用するだけでは、ファイルにアクセスする際に個々のマウントポイントを直接見に行く必要があり非常に面倒です。
そのため、mergerfs を使用して SnapRAID の Data Disk を統合してしまいましょう。

ちなみに、SnapRAID にも pool という設定項目が存在し、Data Disk を一つのディレクトリに束ねることができますが、各 Disk から symlink を貼るだけなので使い勝手はあまり良くありません。

インストール

GitHub の Releases に RPM が用意されています。(.deb もあります)
https://github.com/trapexit/mergerfs/releases
ありがたく使わせてもらいましょう。

1
sudo dnf install -y https://github.com/trapexit/mergerfs/releases/download/2.33.3/mergerfs-2.33.3-1.el8.x86_64.rpm

Mount

マウントポイントを作成し、/etc/fstab に下記を追加

1
sudo mkdir /storage
1
2
# /etc/fstab
/mnt/data/* /storage fuse.mergerfs allow_other,use_ino,cache.files=partial,moveonenospc=true,dropcacheonclose=true,category.create=mfs,fsname=mergerfsPool 0 0
1
sudo mount /storage
1
2
3
4
5
6
7
8
[rocky@localhost storage]$ df -h | grep -P '^(mergerfsPool|/dev/sd[b-j]1)' | sort
/dev/sdb1 128G 946M 128G 1% /mnt/data/disk1
/dev/sdc1 128G 946M 128G 1% /mnt/data/disk2
/dev/sdd1 128G 946M 128G 1% /mnt/data/disk3
/dev/sde1 128G 946M 128G 1% /mnt/data/disk4
/dev/sdf1 128G 946M 128G 1% /mnt/data/disk5
/dev/sdj1 128G 946M 128G 1% /mnt/parity/disk1
mergerfsPool 640G 4.7G 636G 1% /storage

640GB の /storage が作成されました。
mergerfs の Mount Option についての説明は GitHub の README.md を確認しましょう。

確認

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[rocky@localhost ~]$ sudo chmod 775 /storage
[rocky@localhost ~]$ sudo chown root:rocky /storage
[rocky@localhost ~]$ cd /storage/
[rocky@localhost storage]$ ll
total 8
-rw-r--r--. 1 root root 7 Jan 2 08:07 gomi.txt
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content
[rocky@localhost storage]$ echo "UNKO!" > kuso.txt
[rocky@localhost storage]$ ll
total 12
-rw-r--r--. 1 root root 7 Jan 2 08:07 gomi.txt
-rw-rw-r--. 1 rocky rocky 6 Jan 2 08:14 kuso.txt
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content
[rocky@localhost storage]$ ll /mnt/data/*
/mnt/data/disk1:
total 8
-rw-r--r--. 1 root root 7 Jan 2 08:07 gomi.txt
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content

/mnt/data/disk2:
total 4
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content

/mnt/data/disk3:
total 4
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content

/mnt/data/disk4:
total 4
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content

/mnt/data/disk5:
total 8
-rw-rw-r--. 1 rocky rocky 6 Jan 2 08:14 kuso.txt
-rw-------. 1 root root 190 Jan 2 08:08 snapraid.content

/storage に格納されたファイルが Data Disk に分散されていることが確認できます。

Data Disk 追加

RAID-Z では難しかった Disk Array の拡張を試します。
Mount していない data/disk{6..8} を追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo parted -a optimal -s /dev/sdg -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdh -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo parted -a optimal -s /dev/sdi -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1

sudo mkdir -p /mnt/data/disk{6..8}

sudo mkfs.xfs /dev/sdg1
sudo mkfs.xfs /dev/sdh1
sudo mkfs.xfs /dev/sdi1

sudo blkid -s UUID -o value /dev/sd{g..i}1 | xargs -I% echo "UUID=% /mnt/data/disk xfs defaults 0 2" | awk '{print $1,$2 NR+5,$3,$4,$5,$6}' | sudo tee -a /etc/fstab

sudo mount /mnt/data/disk6
sudo mount /mnt/data/disk7
sudo mount /mnt/data/disk8

snapraid.conf を開いて、content と data のコメントアウトを外します。

1
2
3
4
5
6
7
content /mnt/data/disk6/snapraid.content
content /mnt/data/disk7/snapraid.content
content /mnt/data/disk8/snapraid.content

data d6 /mnt/data/disk6/
data d7 /mnt/data/disk7/
data d8 /mnt/data/disk8/

その後は snapraid sync を実行するだけでよいです。
Data Disk 追加後は mergerfs を再 Mount しないと、新しい Disk にファイルが保存されないので気をつけましょう。

1
2
3
sudo /usr/local/bin/snapraid sync
sudo umount /storage
sudo mount /storage

ディレクトリへのアクセスがあると Unmount できないので、一旦入り直すなどしてアクセスを切りましょう。

1
2
3
4
5
6
7
8
[rocky@localhost storage]$ sudo umount /storage
umount: /storage: target is busy.
[rocky@localhost storage]$ sudo lsof /storage
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 2867 rocky cwd DIR 0,44 46 5424983562661234939 /storage
sudo 9744 root cwd DIR 0,44 46 5424983562661234939 /storage
lsof 9746 root cwd DIR 0,44 46 5424983562661234939 /storage
lsof 9747 root cwd DIR 0,44 46 5424983562661234939 /storage
1
2
3
4
5
6
7
8
9
10
[rocky@localhost ~]$ df -h | grep -P '^(mergerfsPool|/dev/sd[b-i]1)'
/dev/sdb1 128G 3.3G 125G 3% /mnt/data/disk1
/dev/sdc1 128G 3.3G 125G 3% /mnt/data/disk2
/dev/sdd1 128G 3.3G 125G 3% /mnt/data/disk3
/dev/sde1 128G 3.3G 125G 3% /mnt/data/disk4
/dev/sdf1 128G 3.3G 125G 3% /mnt/data/disk5
/dev/sdg1 128G 955M 128G 1% /mnt/data/disk6
/dev/sdh1 128G 955M 128G 1% /mnt/data/disk7
/dev/sdi1 128G 955M 128G 1% /mnt/data/disk8
mergerfsPool 1.0T 20G 1005G 2% /storage

1TB まで拡張されました。
/storage の権限を変更すると配下の Disk に対しても権限の変更が適用されますが、新たに Disk を追加した際は権限を再度変更する必要があります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[rocky@localhost storage]$ ls -la /mnt/data/
total 8
drwxr-xr-x. 10 root root 4096 Jan 3 09:57 .
drwxr-xr-x. 4 root root 4096 Jan 3 09:48 ..
drwxrwxr-x. 2 root rocky 46 Jan 3 09:57 disk1
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk2
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk3
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk4
drwxrwxr-x. 2 root rocky 46 Jan 3 09:57 disk5
drwxr-xr-x. 2 root root 30 Jan 3 09:57 disk6
drwxr-xr-x. 2 root root 30 Jan 3 09:57 disk7
drwxr-xr-x. 2 root root 30 Jan 3 09:57 disk8
[rocky@localhost storage]$ sudo chmod 775 /storage
[rocky@localhost storage]$ sudo chown root:rocky /storage
[rocky@localhost storage]$ ls -la /mnt/data/
total 8
drwxr-xr-x. 10 root root 4096 Jan 3 09:57 .
drwxr-xr-x. 4 root root 4096 Jan 3 09:48 ..
drwxrwxr-x. 2 root rocky 46 Jan 3 09:57 disk1
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk2
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk3
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk4
drwxrwxr-x. 2 root rocky 46 Jan 3 09:57 disk5
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk6
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk7
drwxrwxr-x. 2 root rocky 30 Jan 3 09:57 disk8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
[rocky@localhost storage]$ for i in `seq 100`; do echo $i > file${i}.txt; done
[rocky@localhost storage]$ ls -la /mnt/data/disk{6..8}
/mnt/data/disk6:
total 60
drwxrwxr-x. 2 root rocky 262 Jan 3 10:01 .
drwxr-xr-x. 10 root root 4096 Jan 3 09:57 ..
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file17.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file25.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file33.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file3.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file41.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file49.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file57.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file65.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file73.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file81.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file89.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file97.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file9.txt
-rw-------. 1 root root 285 Jan 3 09:57 snapraid.content

/mnt/data/disk7:
total 60
drwxrwxr-x. 2 root rocky 262 Jan 3 10:01 .
drwxr-xr-x. 10 root root 4096 Jan 3 09:57 ..
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file16.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file24.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file2.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file32.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file40.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file48.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file56.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file64.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file72.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file80.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file88.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file8.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file96.txt
-rw-------. 1 root root 285 Jan 3 09:57 snapraid.content

/mnt/data/disk8:
total 60
drwxrwxr-x. 2 root rocky 262 Jan 3 10:01 .
drwxr-xr-x. 10 root root 4096 Jan 3 09:57 ..
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file15.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file1.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file23.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file31.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file39.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file47.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file55.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file63.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file71.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file79.txt
-rw-rw-r--. 1 rocky rocky 2 Jan 3 10:01 file7.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file87.txt
-rw-rw-r--. 1 rocky rocky 3 Jan 3 10:01 file95.txt
-rw-------. 1 root root 285 Jan 3 09:57 snapraid.content

新しい Disk にもファイルが分散されることが確認できました。

Parity Disk 追加

Parity も後から追加できます。

1
2
3
4
5
sudo parted -a optimal -s /dev/sdk -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo mkdir -p /mnt/parity/disk2
sudo mkfs.xfs /dev/sdk1
sudo blkid -s UUID -o value /dev/sdk1 | xargs -I% echo "UUID=% /mnt/parity/disk xfs defaults 0 2" | awk '{print $1,$2 NR+1,$3,$4,$5,$6}' | sudo tee -a /etc/fstab
sudo mount /mnt/parity/disk2

snapraid.conf2-parity のコメントアウトを外します。

1
2-parity /mnt/parity/disk2/snapraid.2-parity

Parity を追加する際は、snapraid sync -F を実行して Parity を完全に再計算する必要があります。
既存の Parity は同期が完了するまで更新されないため、同期中もファイルの保護が継続されます。

1
/usr/local/bin/snapraid sync -F
1
2
3
4
5
6
7
8
9
10
11
12
[rocky@localhost storage]$ df -h | grep -P '^(mergerfsPool|/dev/sd[b-k]1)' | sort
/dev/sdb1 128G 946M 128G 1% /mnt/data/disk1
/dev/sdc1 128G 946M 128G 1% /mnt/data/disk2
/dev/sdd1 128G 946M 128G 1% /mnt/data/disk3
/dev/sde1 128G 946M 128G 1% /mnt/data/disk4
/dev/sdf1 128G 946M 128G 1% /mnt/data/disk5
/dev/sdg1 128G 946M 128G 1% /mnt/data/disk6
/dev/sdh1 128G 946M 128G 1% /mnt/data/disk7
/dev/sdi1 128G 946M 128G 1% /mnt/data/disk8
/dev/sdj1 128G 947M 128G 1% /mnt/parity/disk1
/dev/sdk1 128G 947M 128G 1% /mnt/parity/disk2
mergerfsPool 1.0T 7.4G 1017G 1% /storage

Data Disk 復旧

意図的に disk1 (/dev/sdb) をブチ抜き、障害を再現させます。
(VirtualBox でやっているので、適当に抜いてみてください)

1
2
[rocky@localhost disk1]$ cat gomi.txt 
cat: gomi.txt: Input/output error
1
2
3
[rocky@localhost storage]$ sudo /usr/local/bin/snapraid sync
Self test...
Error accessing 'content' dir '/mnt/data/disk1' specification in '/etc/snapraid.conf' at line 5

新しい Disk を追加すると /dev/sdl が生えたので、フォーマットして Mount します。

1
2
3
4
5
6
7
[rocky@localhost storage]$ sudo parted -a optimal -s /dev/sdl -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
[rocky@localhost storage]$ sudo mkfs.xfs /dev/sdl1
[rocky@localhost storage]$ sudo blkid -s UUID /dev/sdl1
/dev/sdl1: UUID="19b373e5-b98b-45e9-864e-af90435557ce"
# disk1 の UUID を書き換え
[rocky@localhost storage]$ sudo vim /etc/fstab
[rocky@localhost storage]$ sudo mount /mnt/data/disk1

snapraid fix を実行してデータを回復させます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -d --filter-disk: 対象の Disk を指定
# -l --log: ログの保存先
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid -d d1 -l fix.log fix
Self test...
Loading state from /var/snapraid.content...
UUID change for disk 'd1' from 'e85bc923-5a55-45d9-ac4b-1d9e2640af27' to '9e2ac5c0-00f9-49d0-83cd-57b85a9eaffd'
Searching disk d1...
Searching disk d2...
Searching disk d3...
Searching disk d4...
Searching disk d5...
Searching disk d6...
Searching disk d7...
Searching disk d8...
Selecting...
Using 0 MiB of memory for the file-system.
Initializing...
Selecting...
Fixing...
recovered gomi.txt
recovered file46.txt
recovered file14.txt
recovered file22.txt
recovered file30.txt
recovered file38.txt
recovered file54.txt
recovered file62.txt
recovered file70.txt
recovered file78.txt
recovered file86.txt
recovered file94.txt
100% completed, 1 MB accessed in 0:00

12 errors
12 recovered errors
0 unrecoverable errors
Everything OK

snapraid check を実行してデータが回復したか確認します。

1
2
3
4
5
6
7
8
9
10
11
12
# -a --audit-only: Parity をガン無視してデータのみを検証する
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid -d d1 -a check
Self test...
Loading state from /var/snapraid.content...
UUID change for disk 'd1' from '3f866f6e-10a8-4551-a460-d6264482d54d' to '19b373e5-b98b-45e9-864e-af90435557ce'
Selecting...
Using 49 MiB of memory for the file-system.
Initializing...
Selecting...
Hashing...
100% completed, 2415 MB accessed in 0:00
Everything OK

snapraid sync を実行して復旧完了です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid sync
Self test...
Loading state from /var/snapraid.content...
UUID change for disk 'd1' from 'e85bc923-5a55-45d9-ac4b-1d9e2640af27' to '9e2ac5c0-00f9-49d0-83cd-57b85a9eaffd'
Scanning...
Scanned d2 in 0 seconds
Scanned d1 in 0 seconds
Scanned d3 in 0 seconds
Scanned d4 in 0 seconds
Scanned d5 in 0 seconds
Scanned d6 in 0 seconds
Scanned d8 in 0 seconds
Scanned d7 in 0 seconds
WARNING! UUID is changed for disks: 'd1'. Not using inodes to detect move operations.
Using 0 MiB of memory for the file-system.
Initializing...
Resizing...
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Saving state to /mnt/data/disk6/snapraid.content...
Saving state to /mnt/data/disk7/snapraid.content...
Saving state to /mnt/data/disk8/snapraid.content...
Verifying...
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
Verified /mnt/data/disk6/snapraid.content in 0 seconds
Verified /mnt/data/disk7/snapraid.content in 0 seconds
Verified /mnt/data/disk8/snapraid.content in 0 seconds
Using 80 MiB of memory for 128 cached blocks.
Selecting...
Syncing...
Nothing to do
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[rocky@localhost ~]$ ll /mnt/data/disk1
total 56
-rw-------. 1 root root 3 Jan 3 10:01 file14.txt
-rw-------. 1 root root 3 Jan 3 10:01 file22.txt
-rw-------. 1 root root 3 Jan 3 10:01 file30.txt
-rw-------. 1 root root 3 Jan 3 10:01 file38.txt
-rw-------. 1 root root 3 Jan 3 10:01 file46.txt
-rw-------. 1 root root 3 Jan 3 10:01 file54.txt
-rw-------. 1 root root 3 Jan 3 10:01 file62.txt
-rw-------. 1 root root 3 Jan 3 10:01 file70.txt
-rw-------. 1 root root 3 Jan 3 10:01 file78.txt
-rw-------. 1 root root 3 Jan 3 10:01 file86.txt
-rw-------. 1 root root 3 Jan 3 10:01 file94.txt
-rw-------. 1 root root 7 Jan 3 09:48 gomi.txt
-rw-------. 1 root root 5134 Jan 3 10:17 snapraid.content

SnapRAID はファイルの権限を保持しません。
そのため復旧直後は全てのファイルの所有者が root になります。

Parity Disk 復旧

Parity が失われた場合はどうでしょうか。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid sync -F
Self test...
Loading state from /var/snapraid.content...
Scanning...
Scanned d1 in 0 seconds
Scanned d2 in 0 seconds
Scanned d4 in 0 seconds
Scanned d5 in 0 seconds
Scanned d3 in 0 seconds
Scanned d6 in 0 seconds
Scanned d7 in 0 seconds
Scanned d8 in 0 seconds
Using 0 MiB of memory for the file-system.
Initializing...
Resizing...
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Saving state to /mnt/data/disk6/snapraid.content...
Saving state to /mnt/data/disk7/snapraid.content...
Saving state to /mnt/data/disk8/snapraid.content...
Verifying...
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
Verified /mnt/data/disk6/snapraid.content in 0 seconds
Verified /mnt/data/disk7/snapraid.content in 0 seconds
Verified /mnt/data/disk8/snapraid.content in 0 seconds
Using 80 MiB of memory for 128 cached blocks.
Selecting...
Syncing...
100% completed, 1 MB accessed in 0:00

d1 1% | *
d2 12% | *******
d3 3% | *
d4 3% | *
d5 11% | ******
d6 4% | **
d7 80% | ***********************************************
d8 7% | ****
parity 0% |
2-parity 0% |
raid 5% | ***
hash 0% |
sched 12% | *******
misc 0% |
|____________________________________________________________
wait time (total, less is better)

Error syncing parity file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
DANGER! Unexpected sync error in 2-Parity disk.
Ensure that disk '2-parity' is sane.
Stopping at block 13
Error advising parity file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '1'
Error advising parity file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '2'
Error advising parity file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '3'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '4'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '5'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '6'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '7'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '8'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '9'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '10'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '11'
Error writing file '/mnt/parity/disk2/snapraid.2-parity'. Input/output error.
Input/Output error in parity '2-parity' at position '12'
Saving state to /var/snapraid.content...
Saving state to /mnt/data/disk1/snapraid.content...
Saving state to /mnt/data/disk2/snapraid.content...
Saving state to /mnt/data/disk3/snapraid.content...
Saving state to /mnt/data/disk4/snapraid.content...
Saving state to /mnt/data/disk5/snapraid.content...
Saving state to /mnt/data/disk6/snapraid.content...
Saving state to /mnt/data/disk7/snapraid.content...
Saving state to /mnt/data/disk8/snapraid.content...
Verifying...
Verified /var/snapraid.content in 0 seconds
Verified /mnt/data/disk1/snapraid.content in 0 seconds
Verified /mnt/data/disk2/snapraid.content in 0 seconds
Verified /mnt/data/disk3/snapraid.content in 0 seconds
Verified /mnt/data/disk4/snapraid.content in 0 seconds
Verified /mnt/data/disk5/snapraid.content in 0 seconds
Verified /mnt/data/disk6/snapraid.content in 0 seconds
Verified /mnt/data/disk7/snapraid.content in 0 seconds
Verified /mnt/data/disk8/snapraid.content in 0 seconds

この場合も、Data Disk 故障時と同じ操作で復旧させられます。

1
2
3
4
5
6
7
8
9
sudo parted -a optimal -s /dev/sdm -- mklabel gpt mkpart primary xfs 1 -1 align-check optimal 1
sudo mkfs.xfs /dev/sdm1
sudo blkid -s UUID /dev/sdm1
sudo vim /etc/fstab
sudo mount /mnt/parity/disk2

sudo /usr/local/bin/snapraid -d 2-parity -l fix.log fix
sudo /usr/local/bin/snapraid -d 2-parity -a check
sudo /usr/local/bin/snapraid sync

補足

Sync だけでなく、週1回間隔で Scrub を実行してサイレントエラーが起きていないかチェックしておくとよいでしょう。

1
2
3
4
5
6
7
8
9
[rocky@localhost ~]$ sudo /usr/local/bin/snapraid scrub
Self test...
Loading state from /var/snapraid.content...
Using 0 MiB of memory for the file-system.
Initializing...
Using 96 MiB of memory for 128 cached blocks.
Selecting...
Scrubbing...
Nothing to do

まとめ

以上、RAID がつらい人のための SnapRAID 運用についてでした。

チラシの裏

Parity 計算の仕組みも見ておきたかったのですが、何やってるのかぜんぜんわかりませんでした (泣)