Raspberry PI シリーズで分散環境構築(その4: NFSサーバの構築とクライアント用OSのインポート)

# 0. 序
1. [Raspberry PI シリーズで分散環境構築(その1:モデル別、ディスクレスクライアント化の可否まとめ)](https://qiita.com/p_cub/items/e81dd0f49b3f6fb024a5)
2. [Raspberry PI シリーズで分散環境構築(その2: PiServer の解析と代替システムの設計まで)](https://qiita.com/p_cub/items/69fa32a9a62890ca1d04)
3. [Raspberry PI シリーズで分散環境構築(その3: dnsmasq のインストールと設定)](https://qiita.com/p_cub/items/e7c889b0f92a868a6c20)
4. [Raspberry PI シリーズで分散環境構築(その4: NFSサーバの構築とクライアント用OSのインポート)](https://qiita.com/p_cub/items/dd682de7de28276129e6)
5. [Raspberry PI シリーズで分散環境構築(その5: クラスタノード向け Raspberry PI OS のカスタマイズ(1))](https://qiita.com/p_cub/items/ecf4c48b7ecb08d971b1)
6. [Raspberry PI シリーズで分散環境構築(その6: クラスタノード向け Raspberry PI OS のカスタマイズ(2))](https://qiita.com/p_cub/items/73dc833302c3f064dc39)
7. [Raspberry PI シリーズで分散環境構築(その7: 各ラズパイ向けの tftp ルート設定と起動試験)](https://qiita.com/p_cub/items/ecef742e86ab2991c1b0)
8. [Raspberry PI シリーズで分散環境構築(その8: Webクラスタ構築のケース(前半))](https://qiita.com/p_cub/items/10751e2daa2d3b74c5d5)
9. [Raspberry PI シリーズで分散環境構築(その9: Webクラスタ構築のケース(後半))](https://qiita.com/p_cub/items/c4c837a57e6487a076d4)

本稿は[その3](https://qiita.com/p_cub/items/e7c889b0f92a868a6c20)からの続きとなります。
今回の内容は、ラズパイに配布するルートディレクトリを保存するための NFS サーバの構築と、実際にラズパイ用 OS[^対象OS] をNFSサーバにインポートするまでとなります。
注意していただきたいことは、クライアント用OSをNFSサーバにインポートするだけではディスクレスクライアントは起動しない、という点です。
インポート後、複数のファイルを編集して、tftp ルートにシンボリックリンクを張らないとクライアントは起動しません。
次回はインポートした各ディストリビューションのカスタマイズ、という内容になります。

[^対象OS]: 本稿では、公式OSである Debian ベースの Raspberry PI OS,他にラズパイ用OSとして代表的なディストリビューションである Ubuntu 20.04 LTS, ArchLinux を対象とします。Fedora は2020/06/08 現在、Pi4 に未対応、と公表しているので記載しません。それから Windows IoT は無視します:-P。ついでに蛇足ですが、Gentoo の人は自力で頑張ってくださいw

# 1. NFS サーバの構築
前回構築した PXE サーバに NFS を実装しても全く構いませんが、複数 OS を切り替える、といった使用法を想定しているならば、耐障害性が高く、ディスク容量の多い別マシンに構築したほうが安心できるかと思います。

なお、ラズパイに公開する共有ディレクトリは2つとします。

<dl>
<dt>各ラズパイに配布する OS のルートディレクトリ向け共有フォルダ</dt>
<dd>本稿では NFS サーバ上の /exports/os4pi を共有するものとします。</dd>
<dd>このフォルダの下に "ディストリビューション名/バージョン/アーキテクチャ" に階層化されたサブフォルダを作成していきます。</dd>
</dl>

<dl>
<dt>全ラズパイに共通のホームディレクトリ向け共有フォルダ</dt>
<dd>本稿では NFS サーバ上の /exports/home4pi を共有するものとします。</dd>
</dl>

## 1.1. OSルート向けNFS共有設定の注意点
+ ラズパイのブートローダー(bootcode.bin) は NFSv4 に対応していないので、NFS v3 対応にしてください
+ ラズパイは読み取り専用でマウントに挑戦してくるはずですし、他の速いマシンで NFS マウントして OS をアップデートする可能性もありますので、NFSサーバ側で読み取り専用にする必要はありません。

## 1.2. 既存の NFS サーバ/ 前回構築した PXE サーバを利用する場合
NFS での共有(公開)フォルダを新たに追加します。
市販のNFS 対応 NASを利用している場合、NFSサーバも自前で管理している場合のどちらかによって、実現方法は変わってきます。

### 1.2.1. 市販の、ネットワークに接続するだけの NAS(NFS対応) を利用する場合
ご利用中の製品のマニュアルに従って共有フォルダを追加してください

### 1.2.2. Linux マシン/前回構築した PXE サーバを利用する場合

/etc/exports を編集して、exportfs コマンドを実行してください 
[参考:ArchLinux のNFS解説](https://wiki.archlinux.jp/index.php/NFS#.E3.82.B5.E3.83.BC.E3.83.90.E3.83.BC)

#### /etc/exports(追加例)
```configuration
# クライアント用各ディストリビューションを格納
/exports/os4pi     192.168.172.0/24(rw,sync,nohide,no_root_squash,subtree_check)
# 共通ホームディレクトリ
/exports/home4pi   192.168.172.0/24(rw,sync,nohide,subtree_check)
```

そして、/etc/exports で指定したディレクトリを実際に作成しておきます。

```shell-session
$ sudo mkdir -p /exports/os4pi
$ sudo mkdir -p /exports/home4pi
```

最後に、実際に NFS の共有フォルダを有効化します。

```shell-session
$ sudo exportfs -avr
```

エラーが出てしまう場合は、rpcbind 等の NFS 関連サービスが起動していないので、頑張ってください。

## 1.3. 新規に NFS サーバを構築する場合
詳細は記載しません。お好みの OS で、お好みのディスク(できればSSD)、お好みの RAID構成で構築した後、1.2. に戻ってください。[^FreeNAS]

[^FreeNAS]: 構築時間が無駄だと思ったら、[FreeNAS](https://www.freenas.org/) の利用を考慮してもいいかもしれません。FreeNAS を利用すると、市販 NAS 以上の機能を持ち、Web インターフェイスで設定可能な環境が簡単に手に入ります。

# 2. ラズパイ向け microSD イメージのダウンロードとインポート方法
ここからは、各ディストリビューションによって方法が異なるので、ディストリビューション毎に記載していきます。
なお、各ディストリビューションとも、ダウンロード対象ファイルは、データサイズが最小となっているディスクイメージとします。

## 2.1. 公式 Rasperry PI OS (旧名 Raspbian)

### 2.1.1. 概要
公式OSだけあって、カーネルのバージョンが古めである以外は最もバランスの取れたディストリビューション。
バイナリファイルを何もいじることなしでディスクレス化が可能なので、手っ取り早くディスクレス分散環境を構築したい場合の選択肢はこれしかない。
2020/06 現在ベータ版扱いとなっている 64bit 版も問題なく動いている。

### 2.1.2. ダウンロード先

+ [Raspberry PI OS(Debian の buster 相当) 32bit Lite 版](https://downloads.raspberrypi.org/raspios_lite_armhf/images/)
+ [Pi3/4 専用 64bit 版 (2020/06/08 現在ベータ扱い, Lite版がないので巨大)](https://downloads.raspberrypi.org/raspios_arm64/images/)

上記ダウンロードサイトから、通常のブラウザなり w3m なりで最新版のファイル名を確認して zip ファイルをダウンロードしてください。[^64kernel]
[^64kernel]: 32bit 版であっても、(glibc は 32bit 版になりますが)カーネルのみを 64bit 化することは可能です。

なお、旧版(Debian の stretch 相当)のイメージが欲しい場合は、下記のサイトから ファイル名に "stretch" を含む zip ファイルをダウンロードしてください。
+ [旧版 Raspbian(Debian の stretch 相当) 32bit Lite が欲しい場合](https://downloads.raspberrypi.org/raspbian_lite/images/)

### 2.1.3. 格納先NFS フォルダの作成

#### Pi2 用 32bit版をインストールする場合

```shell-session 
$ sudo mkdir -p /exports/os4pi/raspbian/buster/armhf
```

#### Pi3/4向け 32bit版(カーネルのみ 64bit化したい時)
/boot だけ切り分ける必要があるので、Pi2 との混在であれば、overlayfs の利用がいいかも

```shell-session
$ sudo mkdir -p /exports/os4pi/raspbian/buster/armhf_64
```

#### Pi3 以上専用、64bit版をインストールする場合
```shell-session
$ sudo mkdir -p /exports/os4pi/raspbian/buster/aarch64
```

#### 旧版(stretch,32bit)をインストールする場合
```shell-session
$ sudo mkdir -p /exports/os4pi/raspbian/stretch/armhf
```

### 2.1.4. 公式イメージ(zipファイル)の内容
ダウンロードしたzipファイルのチェックサムを確認して、解凍します。

```shell-session
# unzip 2020-05-27-raspios-buster-lite-armhf.zip
(出力結果省略)
# 
```

拡張子が zip から img に変わったファイルが出現するはずです(今回は 2020/06/08 現在の 32bit Lite 版を使用)。
このファイルは2パーテーションに分割された dd イメージなので、ループバックデバイスを作成、確認します。

```shell-session
# losetup -P -f 2020-05-27-raspios-buster-lite-armhf.img
# losetup -a
/dev/loop0: []: (/tmp/2020-05-27-raspios-buster-lite-armhf.img)
# ls /dev/loop0*
/dev/loop0 /dev/loop0p1 /dev/loop0p2
#
```

loop0p1 が microSD 第1パーテーション(FAT,/boot)用, loop0p2 が第2パーテーション(ext4,/) 用です。
/tmp 配下に一時的なマウントポイントを作成して、上記2パーテーションをマウントし、NFS 共有フォルダにコピーすることになります。
最初から不要なものはコピーしたくありませんが、不要なものは ext4 の lost+found だけですから、
まずは NFS 共有ディレクトリに全コピーして、終了後に lost+found を削除しています。

```shell-session
# mkdir /tmp/raspbian; mount -o ro /dev/loop0p2 /tmp/raspbian && mount -o ro /dev/loop0p1 /tmp/raspbian/boot
# rsync -avr /tmp/raspbian/* /exports/os4pi/raspbian/buster/armhf/
・・・(全ファイルコピー)
# rm -Rf /exports/os4pi/raspbian/buster/armhf/lost+found
```

コピーが終了したら、umount して、ループバックも削除してしまいましょう。

```shell-session
# umount /tmp/raspbian/boot; umount /tmp/raspbian
# losetup -d /dev/loop0
```

Raspbian に関しては、ここまでとなります。

## 2.2. Ubuntu

### 2.2.1. 注意点(2020/06/09 追加)
Ubuntu から配布されているディスクイメージに含まれているカーネルは nfs モジュールがビルトイン状態でコンパイルされて**いない**ため、そのままではネットワークブートできません。ですから、U-boot なしでディスクレス化するためにはカーネルを自前でビルドする必要があります。
それからぱっと見でしかありませんが、配布イメージに含まれている U-boot バイナリを tftp でダウンロードさせて、この U-boot でどうにかする、という処理も調査・テストなどかなり面倒そうです。つまり、今の状況ですと無駄な時間を使いたくない方はこのディストリビューションの使用を避けるべきものと判断してください。

### 2.2.2. 概要

Ubuntu Server は snapd, apparmor、iscsi、lvm2, mdadm といったラズパイディスクレスノードにはほぼ不要なサービスが多数デフォルトで立ち上がります。従って、インストール後、不要サービス/パッケージの削除が大変なことになりますので

+ OSの選択肢が Ubuntu 18.04 しかない [Jetson NANO](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit) も保有していて、OSを共通化したい

といった方以外はあえてこのディストリビューションを選ぶ必要もないかと思いますが、メジャーなので取り上げます。

 [Ubuntu の公式サイト](https://ubuntu.com/download/raspberry-pi)でダウンロードしようとすると、2用32bit / 3用64bit / 3用32bit / 4用64bit / 4用32bit と5種類もダウンロードの選択肢が存在して、どれをダウンロードすればいいのか迷うことになります。
調べてみた所、20.04(Focal Fossa) に関しては、ボタン表記が異なるだけで、実態は 32bit なのか 64bit なのかの違いしかありません。ですから、2用32bit, 3用64bit 版の2つをダウンロードすれば事足ります。
また、18.04(Bionic Beaver) に関しては、モデル別にイメージファイルが用意されていますが、これはモデルによって U-boot バイナリを変える必要があったからだと思われます。今回構築するシステムではディスクイメージ同梱の U-boot を使いませんから、こちらも 2用32bit版と 3用64bit版の2つをダウンロードすれば事足ります。


 
### 2.2.3. ダウンロード先

#### [20.04 LTS (Focal Fossa)](http://cdimage.ubuntu.com/releases/20.04/release/)

上記サイトから、

+ 32bit 版が欲しい場合: 'ubuntu-バージョン-preinstalled-server-armhf+raspi.img.xz'
+ 64bit 版が欲しい場合: 'ubuntu-バージョン-preinstalled-server-arm64+raspi.img.xz'

の名前を持つ最新のxzファイルをダウンロードしてください

#### [18.04 LTS (Bionic Beaver)](http://cdimage.ubuntu.com/releases/18.04/release/)

上記サイトから

+ 32bit版が欲しい場合: 'ubuntu-バージョン-preinstalled-server-armhf+raspi2.img.xz'
+ 64bit版が欲しい場合: 'ubuntu-バージョン-preinstalled-server-arm64+raspi3.img.xz'

の名前を持つ最新のxzファイルをダウンロードしてください。

### 2.2.4. 格納先フォルダの作成

コマンド中の'$VERSION' は 20.04/18.04 のいずれかとします。

#### Pi2 用 32bit版をインストールする場合

```shell-session
$ sudo mkdir -p /exports/os4pi/ubuntu/$VERSION/armhf
```
#### Pi3/4向け 32bit版(カーネルのみ 64bit化したい時)

/boot/firmware だけ別フォルダにする必要があるので、Pi2 用フォルダを下段に下 overlayfs を考えたほうがいいかも。

```shell-session
$ sudo mkdir -p /exports/os4pi/ubuntu/$VERSION/armhf_64
```

#### Pi3 以上専用 64bit版をインストールする場合

```shell-session
$ sudo mkdir -p /exports/os4pi/ubuntu/$VERSION/aarch64
```

### 2.2.5. 公式イメージ(xzファイル)の内容
ダウンロードしたxzファイルのチェックサムを確認した後、解凍します。

```shell-session
$ unxz ubuntu-20.04-preinstalled-server-arm64+raspi4.img.xz
(出力結果省略)
```

拡張子が img.xzから img に変わったファイルが出現するはずです(今回は 2020/06/08 現在の 20.04 64bit raspi4 版を使用)。
このファイルは2パーテーションに分割された dd イメージなので、ループバックデバイスを作成、確認します。

```shell-session
$ sudo losetup -P -f ubuntu-20.04-preinstalled-server-arm64+raspi4.img 
$ losetup -a
/dev/loop0: []: (/tmp/ubuntu-20.04-preinstalled-server-arm64+raspi4.img)
$ ls /dev/loop0*
/dev/loop0  /dev/loop0p1  /dev/loop0p2
$ 
```

loop0p1 が microSD 第1パーテーション(FAT,/boot/firmware)用, loop0p2 が第2パーテーション(ext4,/) 相当です。
/tmp 配下に一時的なマウントポイントを作成して、上記2パーテーションをマウントし、NFS 共有フォルダにコピーすることになります。
最初から不要なものはコピーしたくありませんが、不要なものは ext4 の lost+found だけですから、
まずは NFS 共有ディレクトリに全コピーして、終了後に lost+found を削除しています。

```shell-session
$ mkdir /tmp/ubuntu; sudo mount -o ro /dev/loop0p2 /tmp/ubuntu && sudo mount -o ro /dev/loop0p1 /tmp/ubuntu/boot/firmware
$ sudo rsync -avr /tmp/ubuntu/* /exports/os4pi/ubuntu/20.04/aarch64/
・・・(全ファイルコピー)
$ sudo rm -Rf /exports/os4pi/ubuntu/20.04/aarch64/lost+found
```

コピーが終了したら、umount して、ループバックも削除してしまいましょう。

```shell-session
# umount /tmp/ubuntu/boot; umount /tmp/ubuntu
# losetup -d /dev/loop0
```

Ubuntu に関しては、ここまでとなります。

## 2.3. ArchLinux ARM

### 2.3.1. 注意点
2020/06 現在、32bit 版は /boot/config.txt ファイルを書き換えるだけでディスクレスブートが可能な状態です。

しかし、64bit 版は素直にディスクレスブート化することは不可能となっています。
ArchLinux の aarch64版、 調べてみたらこのディストリビューションも u-boot で起動するものとなっていました（kernel8.img の中身が u-boot）。
そこで u-boot が読み込ませるカーネル本体と initramfs ファイルを tftp にアップして、config.txt を編集し、ディスクレス状態のラズパイを起動してみたところ、bootcode.bin のおかげで tftp からカーネル+initramfs を読み込むまでは可能でした。次に実際のカーネルに制御が移り、initramfs を仮のルートファイルシステムとして起動する途中で「ネットワークがない」と言われて止まってしまう状態に陥りました。どうやらこのカーネルはUSBイーサネットドライバ(smsc95xx)がビルトインされていない状態みたいですね。つまり、ここでもカーネルの再コンパイルが必要そうだぞ、と。そんなこんなで、

+ 「どうしても即座に ArchLinux を使いたい」という場合は 32bit 版を
+ 「即座でなくても構わないけどどうしても 64bit 版 ArchLinux を使いたい」という場合はカーネルの再構築/U-bootスクリプトの調査・テストにチャレンジしてみてください。

### 2.3.2. 概要
ArchLinux ARM の場合は、インストール用ファイルが ddイメージファイルではなく tar.xz 形式なので、NFS サーバへのインポート処理は通常のインストール方法とあまり変わりません。
ただ、個々のモデル毎に配布イメージ(というよりもデバイスツリーファイル)が違いますし、ローリングアップデートのディストリビューションゆえ、バージョン番号はありませんから、他のディストリビューションでは OS のバージョンとアーキテクチャでサブディレクトリを切っていたところを、対象ラズパイのモデル番号で切り分けることにします。

参考: 公式インストールマニュアル([2B: 32bit版](https://archlinuxarm.org/platforms/armv7/broadcom/raspberry-pi-2)　/ [3B: 32/64bit版](https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-3) / [4B: 64bit版](https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-4) )

### 2.3.3. ダウンロード先
公式マニュアルにもあるように

+ 2/3で利用可能な32bit版は http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-2-latest.tar.gz
+ 64bit版(3専用)は http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-3-latest.tar.gz
+ 64bit版(4専用)は http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-4-latest.tar.gz

から /tmp にダウンロードしてください。

### 2.3.4. 格納先フォルダの作成とインポートを一括で実行
root で以下のワンライナーを実行するだけで ArchLinux は完了です。
**注意** : GNU tar ではなく、BSD tar を使いますので、インストールされていない方は事前にインストールしておいてください。[^BSDTAR]

[^BSDTAR]: GNU tar でも展開可能なのですが、未対応のセキュリティ識別子が含まれているので展開時にエラーが発生します。

```
# mkdir -p /exports/os4pi/archlinux; cd /exports/os4pi/archlinux; for N in `seq 2 4`; do if [ -f /tmp/ArchLinuxARM-rpi-$N-latest.tar.gz ]; then mkdir pi$N; bsdtar -xpf /tmp/ArchLinuxARM-rpi-$N-latest.tar.gz -C pi$N; fi; done
```

# 3. 次回予告
[次回](https://qiita.com/p_cub/items/ecf4c48b7ecb08d971b1)は、クライアントに配布するOSのカスタマイズ(/boot 及び /etc 配下ファイルで、テキストエディタで変更可能な範囲に限定)を行っていきます。
qemu-static を利用しないと駄目な範囲までは行きません。