Raspberry Pi 3 Model B に 64bit NBD Root な環境を構築する

## 1. 概要

巷ではoverlayfsなrootを利用した環境構築が流行っているものの、
NBD Rootを利用した構成がほとんど見つからないことからやってみました。

## 2. 構成

### ハードウエア

- Raspberry Pi 3 Model B
  - microSD 2GB
  - 有線ネットワーク環境
- [ReadyNAS DUOv2](https://www.netgear.jp/products/details/RND2000-200AJS.html)
  - そろそろNASを更改しなければ…
  - 母艦(Mac)

### ソフトウエア

#### Raspberry Pi

* HypriotOS aarch64
  *  [ここ](https://github.com/DieterReuter/image-builder-rpi64/releases/)のを使う。

#### NAS

* NBD Server

## 2. ディスク構成

* SDカード /boot
  - FAT32
* NBD Root /
  - 8GB
  - btrfs
    - lzo圧縮を使いたいのでbtrfsにした。
* NBD Swap
  - 2GB
    - 実際こんなにいらない。
* Zram
  - 2.5GB
  - memlimit 800MB

## 3. 実施作業

### Macで作業

SD書き込みツールのダウンロード&必要なパッケージのインストール

```shell-session
$ brew install pv
$ brew install awscli
$ curl -O https://raw.githubusercontent.com/hypriot/flash/master/flash
$ chmod a+x ./flash
```

イメージの書き込み
ここを確認して最新のイメージを書き込む
[Releases · DieterReuter/image-builder-rpi64 · GitHub](https://
github.com/DieterReuter/image-builder-rpi64/releases/)

```shell-session
$ ./flash --hostname raspi -d /dev/disk2 https://github.com/DieterReuter/image-builder-rpi64/releases/download/v20180429-184538/hypriotos-rpi64-v20180429-184538.img.zip
```

不要ファイルの削除

```shell-session
$ rm flash
```

### NAS作業

#### Rootの作成

NBDの設定ファイルを作成

```config-file:/etc/nbd-server/conf.d/raspi-root.conf
[raspi-root]
    exportname = /c/nbd/raspi-root.img
    copyonwrite = false
```

空ファイルの作成

```shell-session
# truncate -s 8G /c/nbd/raspi-root.img
# chown nbd: /c/nbd/raspi-root.img
```

ここでフォーマットしてもよいが、btrfsを使うため、実機作業とする。


#### Swapスペースの作成

```config-file:/etc/nbd-server/conf.d/swap.conf
[swap]
    exportname = /c/nbd/swap/%s.img
    copyonwrite = false
    virtstyle = ipliteral
    prerun = /etc/nbd-server/create_sparse_swap.sh %s 2G
    postrun = rm -f %s
```

```shell:/etc/nbd-server/create_sparse.sh
#!/bin/sh

if [ ! -e $1 ]; then
   logger -t nbd "create sparse file: $1"
   truncate -s $2 $1
fi
```

```shell:/etc/nbd-server/create_sparse_swap.sh
#!/bin/sh

/etc/nbd-server/create_sparse.sh $1 $2
mkswap -f $1
```

ディレクトリ作成

```shell-session
# mkdir /c/nbd/swap
# chown nbd: /c/nbd/swap
```

#### 再起動

NBDサーバの再起動

```shell-session
# /etc/init.d/nbd-server force-reload
```

### 実機作業

#### SSH接続

実機にSDカードを差し込みブートする
母艦よりSSHで接続

```shell-session
$ ssh pirate@raspi.local
$ sudo -s
```

パスワードは"hypriot"

#### NBD接続

```shell-session
# apt-get update
# apt-get upgrade
# apt-get install nbd-client
# modprobe nbd
# nbd-client -N raspi-root 192.168.102.21 /dev/nbd0 -p
```

3.15以前のnbd-serverに3.16または3.17のnbd-clientは接続できないので要注意
3.18ではオプション指定でできるようになるらしい。

[nbd-server before commit 2ab3a2d fails to communicate with nbd-client after commit e6b56c1 · Issue #66 · NetworkBlockDevice/nbd · GitHub](https://github.com/NetworkBlockDevice/nbd/issues/66)


#### NBDディスクのフォーマット

```shell-session
# apt-get install btrfs-progs
# mkfs.btrfs /dev/nbd0
# mount -o rw,noatime,discard,compress=lzo /dev/nbd0 /mnt
```

#### ディスクイメージからrootfsの作成

debootstrapでやってもいいが、面倒なのでディスクイメージをダウンロードして、
ローカルでマウントし、コピーすることにする。

```shell-session
# cd /mnt
# apt-get install unzip
# wget https://github.com/DieterReuter/image-builder-rpi64/releases/download/v20180429-184538/hypriotos-rpi64-v20180429-184538.img.zip
# unzip hypriotos-rpi64-v20180429-184538.img.zip
# rm hypriotos-rpi64-v20180429-184538.img.zip
# losetup -P --show -f ./hypriotos-rpi64-v20180429-184538.img
# mount /dev/loop0p2 /media
# tar cf - -C /media . | tar xfp -
# umount /media
# losetup -d /dev/loop0
# rm hypriotos-rpi64-v20180429-184538.img
```

#### chroot

```shell-session
# mount --bind /dev /mnt/dev
# mount -t tmpfs tmpfs /mnt/dev/shm
# mount -t tmpfs tmpfs /mnt/run
# mount -t proc proc /mnt/proc
# mount -t sysfs sysfs /mnt/sys
# mount -t devpts devpts /mnt/dev/pts
# mount --bind /boot /mnt/boot
# cp /etc/hostname /mnt/etc/
# cp /etc/hosts /mnt/etc/
# mkdir -p /mnt/run/systemd/resolve/
# cp /run/systemd/resolve/resolv.conf /mnt/run/systemd/resolve/
# chroot /mnt
```

#### パッケージのインストール

```
# apt-get update
# apt-get upgrade
# apt-get install nbd-client btrfs-progs initramfs-tools
```

nbd-clientはPinningしておくことにする。

```:/etc/apt/preferences
Package: nbd-client
Pin: version 3.15.*
Pin-Priority: 1001
```

#### 初期設定

##### タイムゾーン変更

```shell-session
# dpkg-reconfigure tzdata
```

Asia/Tokyoを選択

##### ユーザの作成

```shell-session
# adduser [ユーザ名]
```

パスワードなど答えてユーザを作成

sudoグループにいま作成したユーザを追加

```shell-session
# addgroup [ユーザ名] sudo
```

SSHキーの追加

```shell-session
# su [ユーザ名]
$ cd
$ mkdir .ssh
$ chmod 700 .ssh
$ vi .ssh/authorized_keys                                         
$ chmod 600 .ssh/authorized_keys   
$ exit
```

#### fstabの修正

```:/etc/fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat ro,relatime,fmask=0022,dmask=0022 0 0
/dev/nbd0 / btrfs rw,relatime,discard,compress=lzo 0 0
```

#### cloud-initの削除

```shell-session
# apt-get purge cloud-init
# apt-get autoremove --purge
# systemctl mask hypriot-firstboot
```

#### initramfsの作成

```shell-session
# mkinitramfs -o /boot/initrd.img
# echo initramfs initrd.img >> /boot/config.txt
```

#### カーネル起動パラメータの修正

```:/boot/cmdline.txt
dwc_otg.lpm_enable=0 console=tty1 nbdroot=192.168.102.21,raspi-root,nbd0 root=/dev/nbd0 ip=dhcp rootfstype=btrfs cgroup_enable=cpuset cgroup_enable=memory swapaccount=1 elevator=deadline fsck.repair=yes rootwait console=ttyAMA0,115200 net.ifnames=0
```

#### 再起動

```
# exit
# reboot
```

再起動したらもう一度sshで接続

##### ロケールの変更

```shell-session
# dpkg-reconfigure locales
```

ja_JP.UTF-8を選択

#### 再起動時フリーズの対策

```shell-session
# vi /lib/systemd/system/ifup@.service
# vi /lib/systemd/system/systemd-networkd.service
# vi /lib/systemd/system/networking.service
```

\[Unit\]のBefore,Conflictsからshutdown.targetを消す

```shell-session
# systemctl daemon-reload
```

書くのは楽だけれどもものすごく嵌った

#### watchdogの変更

ここと同じ
[Raspberry Pi 3/ Zero / Zero W等で watchdogを有効化する - Qiita](https://qiita.com/hidenorly/items/a09486c4359e85d7583e)

```:/boot/config.txt
dtparam=watchdog=on
```

```:/etc/modprobe.d/bcm2835-wdt.conf
options bcm2835_wdt heartbeat=10 nowayout=0
```

```:/etc/systemd/system.conf
RuntimeWatchdogSec=5
```

#### zramの有効化
パッケージがなくなった？ので自作

```:/etc/systemd/system/zram.service
[Unit]
Description=Zram swap

[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-zram-start
ExecStop=/usr/local/bin/systemd-zram-stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
```

```shell:/usr/local/bin/systemd-zram-start
#!/bin/sh
modprobe zram
echo 2560M > /sys/block/zram0/disksize
mkswap /dev/zram0
swapon /dev/zram0 -p 16383 
echo 800M > /sys/block/zram0/mem_limit
```

```shell:/usr/local/bin/systemd-zram-stop
#!/bin/sh
swapoff /dev/zram0
modprobe -r zram
```

```shell-session
# chmod a+x /usr/local/bin/systemd-zram-st*
# systemctl daemon-reload
# systemctl enable zram
```

#### NBD Swapの有効化

```shell-session
# echo "nbd1 192.168.102.21 swap persist,swap" >> /etc/nbdtab
```

NBD Rootによってnbdモジュールは有効化されていることから、
systemdに制御が移った時にはnbdデバイスが存在するため、
Before=dev-%i.deviceおよびRequiredBy=dev-%i.deviceは動かないので、
nbd@.serviceを編集する。

```shell-session
# vi /lib/systemd/system/nbd\@.service
```

\[Unit\] の```Before=dev-%i.device```を```Before=swap.target```に変更、
\[Install\] の```RequiredBy=dev-%i.device```を```WantedBy=sysinit.target```に変更

```shell-session
# systemctl daemon-reload
# systemctl enable nbd@nbd1
```

fstabの修正

```:/etc/fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat ro,relatime,fmask=0022,dmask=0022 0 0
/dev/nbd0 / btrfs rw,relatime,discard,compress=lzo 0 0
/dev/nbd1 swap swap defaults 0 0
```

#### sshd.serviceの修正

/etc/init/ssh.confには```mkdir -p -m0755 /run/sshd```があるのに、
/lib/systemd/system/ssh.serviceには存在しないので、
```ExecStartPre=/bin/mkdir -p -m0755 /run/sshd```を既存のExecStartPreの前に追加する。

## 4.最後に

NBD Rootはあまりないのか、ハマりポイントが多い。
うまくいかない場合は、ネットワークとディスクの起動順序を見直すと良いようだ。