LinuxでルートファイルシステムをRAID+bcache化する

# 背景と概要

Linuxで高速大容量ストレージを安価に組みたい！

現状のストレージのコスト感

|デバイスタイプ|価格帯|
|:----|:---:|
| HDD 4TB | 0.8〜2万 |
| SSD(SATA) 4TB | 6〜9万 |
| SSD(NVMe) 2TB | 2〜4万 |

という時代に、Linuxで4〜8TB程度のRAID1ストレージのマシンをこさえるというシチュエーションです。

SSDが安くなってきたとは言え、容量単価はまだHDDの5倍くらいの感覚です。4TBでSSDでRAID1を組もうと思ったら10万円コースですし、その容量だとNVMeはまだ出てこないのでSATAのSSDになってしまいます。

そこで、容量と速度のいいトコ取りをお値打ち価格で、を目指してファイルシステム全体を HDDのRAID1 + SSDキャッシュ の構成で組むことにしました。SSDキャッシュ化はbcacheを使います。
RAIDはいらないけどファイルシステム全体をbcacheで高速にしたい、というケースでも似たような手順になるかと思います。

### 実際に使用するマテリアル、その他

HDD 6TB x 2 + NVMe SSD 1TB (合計3.5万円）
Ubuntu 18.04 LTS をインストールメディアで入れます。（Ubuntu 20.04, 22.04 でも以下手順で確認済み）
UEFIブートします。


# インストール手順

### 全体の流れ

インストーラはbcacheに対応していませんので、通常のパーティションに仮インストールをしたのち、ファイルシステムを構成してから本環境にデータを移動する流れになります。

### 最終形イメージ

|デバイス|マウントポイント|
|:----|:---|
|HDD | /boot/efi |
|HDD | /boot |
|bcache (backing=HDDのRAID1, cache=SSD) | / |

通常のデスクトップ用であればESPや/bootはSSDに置く方が起動が高速になって良いかもしれません。今回は立ち上げっぱなしのサーバ用途なので、起動の速さはどうでもよくってキャッシュ容量を最大化するためSSDを丸ごとキャッシュにしました。

### パーティショニング

**<font color="DarkRed">起動: live media</font>**
最初にUSBメモリ等のlive mediaを使って（インストールせずに）起動し、gdiskでパーティションを以下のように切っておきます。
(2019-09-18 修正: 障害時にHDD交換しやすいように、左右対象にしました。交換時には /etc/fstab を調整するなりして残すHDDだけで起動できるようにする手順になります)

sda, sdb: HDD (6TiB)
nvme0n1: NVMe SSD (1TiB)

として認識されています。

```
sda:
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          499711   243.0 MiB   EF00  EFI System Partition
   2          499712         4196351   1.8 GiB     8300  Linux filesystem
   3         4196352     11721045134   5.5 TiB     8300  Linux filesystem

sdb:
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          499711   243.0 MiB   EF00  EFI System Partition
   2          499712         4196351   1.8 GiB     8300  Linux filesystem
   3         4196352     11721045134   5.5 TiB     8300  Linux filesystem

nvme0n1:
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048      2000409230   953.9 GiB   8300  Linux filesystem
```

### 仮インストール

このうち、以下のパーティションを使用してUbuntuを仮インストールします。

|デバイス|ファイルシステム|マウントポイント|
|:----|:---|:---|
|/dev/sda1 | EFI | /boot/efi |
|/dev/sda2 | ext4 | /boot |
|/dev/nvme0n1p1 | ext4 | / |

sda3とsdb3はのちほどRAIDを組むパーティションで、ここでは使いません。
またsdb1とsdb2は、sdb単体でもブートできるように後ほどsda1とsda2と等価になるようにします。

/ がSSDなので速いですね。すぐに終わります。

### パッケージ更新と追加

**<font color="DarkRed">再起動: 仮インストールしたシステム</font>**
インストールしたシステムを起動し、パッケージの更新およびRAIDとbcacheに必要なパッケージの追加をします。

```
sudo apt-get udpate
sudo apt-get install mdadm
sudo apt-get install bcache-tools
```

### RAID + bcache でファイルシステム作成

RAID1を組み、それを使用してbcacheデバイスを作成します。ここではまずバッキングデバイス(HDD RAID)だけを登録し、あとでSSDが空いたらキャッシュデバイスとして追加します。

```
sudo mdadm --create /dev/md0 --level=raid1 --raid-device=2 /dev/sda3 /dev/sdb3
(※再起動するとIDが変わって /dev/md127 になったりするので注意)
sudo modprobe bcache
sudo /usr/sbin/make-bcache -B /dev/md0
sudo mkfs.ext4 /dev/bcache0
```

### 仮インストールしたルートファイルシステムをbcacheに移動

2021.02.08: 以下、ルートファイルシステムの移動のためlive mediaから起動しなおしてコピー操作していましたが、そのままの状態で次のコマンドでコピーできるので記述修正しました。GNU tarはextended attributeをサポートしないなどファイルシステムのコピーには問題がある可能性がありそうなので、bsdtarを使います。--one-file-system は別ファイルシステムがマウントされている先をアーカイブの対象にしないオプションで、この場合 /boot /sys /proc /dev などをコピーしないためのものです。

```
sudo mount /dev/bcache0 /mnt
sudo apt-get install bsdtar  #　(ubuntu-20.04 以降は sudo apt-get install libarchive-tools)
sudo bsdtar cvpf - --one-file-system / | sudo bsdtar xvpf - -C /mnt
```

### fstabを修正してgrub更新、再起動

bcache0のUUIDを調べます。

```
ls -l /dev/disk/by-uuid
合計 0
lrwxrwxrwx 1 root root  10  9月 12 16:31 12732d48-4b0d-464f-938a-66fceca525f7 -> ../../sdb1
lrwxrwxrwx 1 root root  10  9月 12 16:31 1EE3-33F7 -> ../../sda1
lrwxrwxrwx 1 root root  11  9月 12 16:31 935f09e9-1aaf-4243-b433-974d83427b7d -> ../../md127
lrwxrwxrwx 1 root root  10  9月 17 11:09 b131c455-8bdb-4486-bf50-2f12140c9b37 -> ../../sda2
lrwxrwxrwx 1 root root  10  9月 17 11:09 a5a262cb-5016-44b6-9b62-bd139ed7784d -> ../../sdb2
lrwxrwxrwx 1 root root  13  9月 12 16:31 ab55455c-c7ea-4e36-9101-ac82e4571e33 -> ../../bcache0
lrwxrwxrwx 1 root root  15  9月 12 16:31 f7952b71-49b7-41a4-9e5c-54b33ab166b3 -> ../../nvme0n1p1
```

/mnt/etc/fstabの / のエントリを、bcache0に変更します。

```
# / was on /dev/nvme0n1p1 during installation
# UUID=f7952b71-49b7-41a4-9e5c-54b33ab166b3  /  ext4  errors=remount-ro  0  1
# / on /dev/bcache0
UUID=ab55455c-c7ea-4e36-9101-ac82e4571e33  /  ext4  errors=remount-ro  0  1
```

本番ファイルシステム構成を再現した状態でgrubを更新

```
sudo mount -B /dev /mnt/dev
sudo mount -B /proc /mnt/proc
sudo mount -B /sys /mnt/sys
sudo mount /dev/sda2 /mnt/boot
sudo mount /dev/sda1 /mnt/boot/efi
sudo chroot /mnt
grub-install --recheck /dev/sda
update-grub
```

今動いているSSDのシステムも検出してしまいますが、後でもう一度更新します。

**<font color="DarkRed">再起動: 本システム</font>**
再起動すると bcache0 が / になってブートします。

```
df
 (必要なものだけ抜粋)
/dev/bcache0         5811055832   48770652 5469354184   1% /
/dev/sda2               1998672     120548    1756884   7% /boot
/dev/sda1                244988       6201     238787   3% /boot/efi
```

### bcacheにキャッシュデバイスを追加

これでSSDを使わずにブートができたということになるので、仮インストールをしたSSDを潰してキャッシュにすることができます。

```
sudo wipefs -a /dev/nvme0n1p1
sudo make-bcache -C /dev/nvme0n1p1
UUID:           f7952b71-49b7-41a4-9e5c-54b33ab166b3
Set UUID:       333480d8-9c84-4cda-8c4c-82029e85eed6
 :

echo 333480d8-9c84-4cda-8c4c-82029e85eed6 | sudo tee /sys/block/bcache0/bcache/attach
```

bcacheの状態確認

```
ls /sys/block/bcache0/slaves
md127  nvme0n1p1
```
```
sudo bcache-super-show /dev/md127
sb.magic        ok
sb.first_sector     8 [match]
sb.csum         4DC6313D63E40B61 [match]
sb.version      1 [backing device]

dev.label       (empty)
dev.uuid        935f09e9-1aaf-4243-b433-974d83427b7d
dev.sectors_per_block   1
dev.sectors_per_bucket  1024
dev.data.first_sector   16
dev.data.cache_mode 0 [writethrough]
dev.data.cache_state    1 [clean]

cset.uuid       333480d8-9c84-4cda-8c4c-82029e85eed6
```

cset.uuidが先ほどセットしたUUIDになり、 cache_stateが [clean] になっています。（attachする前は [detached]）

### 最後に両方のHDDにgrubをインストール

sdbの方は今やらなくてもよいのですが、一応両方のHDDにgrubをインストールしておきます。

```
sudo grub-install --recheck /dev/sda
sudo update-grub

# copy sda1, sda2 -> sdb1, sdb2
sudo mkfs.vfat -F32 /dev/sdb1
sudo mkfs.ext4 /dev/sdb2
sudo mount /dev/sdb2 /mnt
sudo mkdir /mnt/efi
sudo mount /dev/sdb1 /mnt/efi
sudo cp -a /boot/* /mnt
sudo umount /mnt/efi
sudo umount /mnt

sudo mount /dev/sdb2 /boot
sudo mount /dev/sdb1 /boot/efi
sudo grub-install --recheck /dev/sdb
sudo update-grub
sudo umount /boot/efi
sudo umount /boot
```

これで完成です。
6TB RAID1でSSDキャッシュの効いたルートファイルシステムが完成しました。

---

# 補足

一旦作ってしまったbcacheデバイスを止めたいときは以下のようにします。

```
echo 1 | sudo tee /sys/block/bcache0/bcache/stop
sudo wipefs -a /dev/md127
```