Raspberry Pi 4のUbuntuで外付けUSBのSSDを / にマウントする

# 要約

- MicroSD遅い、USB外付けSSDの方が早い
- USB外付けSSDを / にマウントするには
  - SSD を MBR パーティションで組み、LABELを付ける
  - MicroSD の boot-firmware の btcmd.txt nobtcmd.txt に起動するパーティションのLABELが書かれているので、そこをLABELに書き換える
- 実測すると、2-5倍速くなった

||MicorSD|USB SSD|
|---|---|---|
|SeqRead|40 MB/s|200 MB/s|
|SeqWrite|29 MB/s|210 MB/s|
|4KQD32Read|3.2 kIOPS|11 kIOPS|
|4KQD32Write|0.70 kIOPS|1.4 kIOPS|

# やりたいこと

Ubuntu 19.10では、Raspberry Pi 4で起動できるイメージを公開している。

https://ubuntu.com/download/raspberry-pi

これに従い、MicroSD cardに dd で書き込めば起動できる状態になる。

```
xzcat ~/Downloads/<image-file.xz> | sudo dd of=<drive address> bs=32M
```

しかし、MicroSD は IO が遅く、ちょっと使い続けるのは辛かった。

fioを実行してみると、このサイトのベンチマークとほぼ同じ結果になった。

https://ibugone.com/blog/2019/09/raspberry-pi-4-review-benchmark/#3-fio-microsd-card-speed-test

それにしても、ランダムWriteが激遅だった。

外付けSSDをUSB3.0ポートに接続し、fioを実行するとMicroSDの 2-10 倍の結果になったため、こちらで / からファイルシステムを起動したいと思った。

# やりかた

fdisk を使い、MBRの単一パーティションを作る。

```
# デバイスの確認
$ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sdb           8:0    0 119.2G  0 disk

# 以下、ストレージをどーマットしてしまうため、デバイスを間違わないように注意する
$ sudo fdisk /dev/sdb
o
n
w
```

LABELにssdを付けてフォーマットする

```
sudo　mkfs.ext4 /dev/sdb1 -L ssd
```

Ubuntuイメージでは、writableというパーティションに / にマウントするリソースがあるので、それをマウントして、コピーする

```
# SSDのマウント
$ sudo mount /dev/sdb1 /mnt/ssd

# MicroSDのマウント
$ sudo mount /dev/sdc1 /mnt/system-boot
$ sudo mount /dev/sdc2 /mnt/writable

# MicroSD が /media/nnyn/writable にマウントされていたのでコピー 
$ sudo rsync -a /mnt/writable /mnt/ssd
```

ブートするパーティションを変更する。その情報は、MicroSD の system-boot パーティションの btcmd.txt、nobtcmd.txtにかかれているので、LABELをssdに書き換える。

```text:/mnt/system-boot/btcmd.txt
net.ifnames=0 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=ssd rootfstype=ext4 elevator=deadline rootwait fixrtc
```

```text:/mnt/system-boot/nobtcmd.txt
net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=LABEL=ssd rootfstype=ext4 elevator=deadline rootwait fixrtc
```

SSDのfstabも忘れずに書き換えておく。

```text:/mnt/ssd/etc/fstab
#LABEL=writable	/	 ext4	defaults	0 0
LABEL=ssd	/	 ext4	defaults	0 0
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
```

そして、MicroSD と 外付けUSB の SSD を Raspberry Pi 4に接続し、起動する。

lsblk を実行して、以下のように sda にマウントできていれば成功。

![Screenshot from 2020-01-12 08-24-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/a13ee3f2-ba0e-7a79-b083-e813e31d73dc.png)

# 計測

古いSSDを使ったので、凄くIOが出ているわけではないが、MicroSDに比べて 2-5 倍速くなっている。

||MicorSD|USB SSD|
|---|---|---|
|SeqRead|40 MB/s|200 MB/s|
|SeqWrite|29 MB/s|210 MB/s|
|4KQD32Read|3.2 kIOPS|11 kIOPS|
|4KQD32Write|0.70 kIOPS|1.4 kIOPS|

```
$ fio --loops=1 --size=500m --filename=fiotest.tmp --stonewall --ioengine=libaio --direct=1 \
  --name=SeqRead --bs=1m --rw=read \
  --name=SeqWrite --bs=1m --rw=write

SeqRead: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1
SeqWrite: (g=1): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1
fio-3.12
Starting 2 processes
Jobs: 1 (f=1): [_(1),W(1)][100.0%][w=198MiB/s][w=198 IOPS][eta 00m:00s]                
SeqRead: (groupid=0, jobs=1): err= 0: pid=2685: Sun Jan 12 00:12:51 2020
  read: IOPS=194, BW=194MiB/s (204MB/s)(500MiB/2576msec)
    slat (usec): min=704, max=2378, avg=723.00, stdev=75.70
    clat (usec): min=3738, max=6309, avg=4417.19, stdev=359.92
     lat (usec): min=4480, max=7950, avg=5143.24, stdev=376.87
    clat percentiles (usec):
     |  1.00th=[ 3752],  5.00th=[ 3785], 10.00th=[ 3785], 20.00th=[ 4178],
     | 30.00th=[ 4490], 40.00th=[ 4490], 50.00th=[ 4555], 60.00th=[ 4555],
     | 70.00th=[ 4555], 80.00th=[ 4621], 90.00th=[ 4621], 95.00th=[ 4686],
     | 99.00th=[ 5538], 99.50th=[ 6259], 99.90th=[ 6325], 99.95th=[ 6325],
     | 99.99th=[ 6325]
   bw (  KiB/s): min=151823, max=215153, per=84.79%, avg=168523.50, stdev=31097.26, samples=4
   iops        : min=  148, max=  210, avg=164.25, stdev=30.51, samples=4
  lat (msec)   : 4=19.80%, 10=80.20%
  cpu          : usr=0.00%, sys=15.15%, ctx=505, majf=0, minf=276
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=500,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

SeqWrite: (groupid=1, jobs=1): err= 0: pid=2686: Sun Jan 12 00:12:51 2020
  write: IOPS=200, BW=201MiB/s (211MB/s)(500MiB/2488msec); 0 zone resets
    slat (usec): min=527, max=1151, avg=1000.11, stdev=52.95
    clat (usec): min=3639, max=7132, avg=3963.10, stdev=420.59
     lat (usec): min=4376, max=8128, avg=4964.91, stdev=424.48
    clat percentiles (usec):
     |  1.00th=[ 3654],  5.00th=[ 3687], 10.00th=[ 3687], 20.00th=[ 3720],
     | 30.00th=[ 3720], 40.00th=[ 3720], 50.00th=[ 3752], 60.00th=[ 3752],
     | 70.00th=[ 3949], 80.00th=[ 4293], 90.00th=[ 4490], 95.00th=[ 4686],
     | 99.00th=[ 5407], 99.50th=[ 5932], 99.90th=[ 7111], 99.95th=[ 7111],
     | 99.99th=[ 7111]
   bw (  KiB/s): min=192512, max=215040, per=99.02%, avg=203776.00, stdev=10901.31, samples=4
   iops        : min=  188, max=  210, avg=199.00, stdev=10.65, samples=4
  lat (msec)   : 4=70.60%, 10=29.40%
  cpu          : usr=3.26%, sys=17.89%, ctx=505, majf=0, minf=22
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,500,0,0 short=0,0,0,0 dropped=0,0,0,0


$ fio --loops=5 --size=500m --filename=fiotest.tmp --stonewall --ioengine=libaio --direct=1 \
  --name=4KQD32read --bs=4k --iodepth=32 --rw=randread \
  --name=4KQD32write --bs=4k --iodepth=32 --rw=randwrite

4KQD32read: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
4KQD32write: (g=1): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
fio-3.12
Starting 2 processes
Jobs: 1 (f=1): [_(1),w(1)][99.1%][w=14.5MiB/s][w=3722 IOPS][eta 00m:05s]                  
4KQD32read: (groupid=0, jobs=1): err= 0: pid=2567: Sun Jan 12 00:10:17 2020
  read: IOPS=10.0k, BW=42.8MiB/s (44.9MB/s)(2500MiB/58422msec)
    slat (usec): min=13, max=1058, avg=27.13, stdev=21.53
    clat (usec): min=562, max=56212, avg=2889.83, stdev=1726.51
     lat (usec): min=592, max=56228, avg=2917.52, stdev=1726.51
    clat percentiles (usec):
     |  1.00th=[  971],  5.00th=[ 1106], 10.00th=[ 1336], 20.00th=[ 2114],
     | 30.00th=[ 2212], 40.00th=[ 2245], 50.00th=[ 2311], 60.00th=[ 2409],
     | 70.00th=[ 3195], 80.00th=[ 3458], 90.00th=[ 4621], 95.00th=[ 5800],
     | 99.00th=[10028], 99.50th=[11600], 99.90th=[16909], 99.95th=[19006],
     | 99.99th=[25297]
   bw (  KiB/s): min=29893, max=43978, per=89.33%, avg=39142.91, stdev=1694.44, samples=116
   iops        : min= 7473, max=10994, avg=9785.40, stdev=423.61, samples=116
  lat (usec)   : 750=0.02%, 1000=1.58%
  lat (msec)   : 2=10.82%, 4=72.98%, 10=13.59%, 20=0.97%, 50=0.04%
  lat (msec)   : 100=0.01%
  cpu          : usr=7.40%, sys=36.44%, ctx=419743, majf=0, minf=53
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=640000,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32

4KQD32write: (groupid=1, jobs=1): err= 0: pid=2568: Sun Jan 12 00:10:17 2020
  write: IOPS=1352, BW=5410KiB/s (5540kB/s)(2500MiB/473179msec); 0 zone resets
    slat (usec): min=15, max=287512, avg=47.08, stdev=437.45
    clat (usec): min=122, max=4250.8k, avg=23605.99, stdev=134391.77
     lat (usec): min=177, max=4250.9k, avg=23653.84, stdev=134396.37
    clat percentiles (usec):
     |  1.00th=[   1352],  5.00th=[   1680], 10.00th=[   1762],
     | 20.00th=[   2147], 30.00th=[   2606], 40.00th=[   2999],
     | 50.00th=[   3294], 60.00th=[   3851], 70.00th=[   5145],
     | 80.00th=[  13829], 90.00th=[  36963], 95.00th=[  61080],
     | 99.00th=[ 320865], 99.50th=[1149240], 99.90th=[1937769],
     | 99.95th=[2399142], 99.99th=[3204449]
   bw (  KiB/s): min=   16, max=59264, per=100.00%, avg=7643.73, stdev=10569.74, samples=667
   iops        : min=    4, max=14816, avg=1910.91, stdev=2642.43, samples=667
  lat (usec)   : 250=0.01%, 500=0.01%, 750=0.01%, 1000=0.01%
  lat (msec)   : 2=14.46%, 4=47.49%, 10=15.05%, 20=6.81%, 50=9.42%
  lat (msec)   : 100=4.43%, 250=1.26%, 500=0.14%, 750=0.10%, 1000=0.20%
  cpu          : usr=1.43%, sys=7.40%, ctx=392221, majf=0, minf=24
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=100.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,640000,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=32
```