Raspberry Pi4とPXE netbootとiSCSI rootと。

# はじめに
　プライベートクラウド構築のため、既製品NASを利用しようとしたところ、深刻なスペック不足のために断念しました。
　そこでRaspberryを用いて、ストレージはNASへiSCSIでマウントさせて作業しようと考えて色々と調べました。

# 結論
　NASのiSCSIのスピードがおそすぎて使い物にならないので、裸族のマンションを使うことにしました。
　ですが、やり方は勉強になったので記録します。

# Howto
## 用意するもの
- RaspberryPi ４
- microSDカード
- RaspberryPi OS
- Linuxマシン
（自分はsshおよびイメージファイルの書き込みにMacを利用しています。）
- NAS（iSCSIターゲット）

## Raspberry Pi OSをiSCSI root可能にする
　標準のOSイメージではNFSマウントが可能で、たくさんの記事がありますがiSCSIをrootマウントする場合は``initramfs``の更新が必要です。まずはその作業をする準備をします。
　
### Raspberry Pi OSを通常通りmicroSDにインストール
 まずは一旦、通常通りの手順でRaspberry PiとmicroSDの組み合わせで動作できる状態まで作業します。手順については公式で案内されているので、その手順で問題ないと思います。自分の手順は以下に、参考程度にしてください。
1. [Raspberry Pi OS Lite(64bit)](https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit)をダウンロード
1. xz解凍
``$ unxz -v 2022-04-04-raspios-bullseye-arm64-lite.img.xz``
1. 解凍したイメージファイルをmicroSDカードに書き込み
`` (mac)# dd if=2022-04-04-raspios-bullseye-arm64-lite.img of=/dev/rdiskX bs=1m``
1. Raspberry PiにmicroSDカードをセットして、通常通りインストールを実施

### ついでにSerialを控えておく
　PXEbootで、Serial番号が必要になりますのでこのタイミングで確認しておきます。
```
(raspi) $ cat /proc/cpuinfo
・・・
Serial : 0000000012345678
```
ちょうど最後の出力でSerialが出力されます。先頭の0を除いた番号が必要なのでどこかにメモしておきます。

### Open-iSCSIをインストール
　詳しいことは理解していませんが、``initramfs``へのバイナリを追加するために必要な操作になります。
```
(raspi)# apt install open-iscsi
(raspi)# touch /etc/iscsi/iscsi.initramfs
```

### initramfsの作成
　起動時にiSCSIマウント可能にするため、``initramfs``の作成行います。
```
(raspi)# update-initramfs -v -k $(uname -r) -c
```
作成した``initramfs``を、sftpなどで別のマシンにコピーしておきます。

## PXEboot環境の構築
　RaspberryPi4については、DHCPリクエストに対してTFTPサーバのアドレスを渡すことで可能になります。
　TFTPサーバから``/boot``のファイルをダウンロードしたあとにカーネルの起動が始まります。

### DHCP設定
　このあたりの設定は、他の方々も手順を書いていらっしゃるので、そちらを参考になるといいと思います。よく見るのは``dnsmasq``です。参考までに、自宅のCiscoコンフィグの内容を書いておきます。
```
ip dhcp pool raspi4
 host 192.168.0.250 255.255.255.0
 hardware-address dca6.3201.2345
 default-router 192.168.0.254
 option 43 ascii "Raspberry Pi Boot"
 option 66 ascii 192.168.0.252
```

### TFTPサーバの構築
　TFTPサーバの構築についても、特に特筆すべき内容はありませんので自分の手順を書いておきます。
```
(NAS)# mkdir -p /noraid/share/pxeboot
(NAS)# chown -R nobody:nogrpup /noraid/share

(NAS)# apt install tftpd-hpa
(NAS)# cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_USERNAME="nobody"
TFTP_DIRECTORY="/noraid/share/pxeboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

(NAS)# systemctl restart tftpd-hpa
```

## iSCSI環境の用意
　PXEbootのあとに読み込まれる、rootファイルシステム用のiSCSI環境を構築します。
### iSCSI targetの構築
　この手順も、特に特筆すべきことはありませんので、やったことを書いておきます。
- NASのソフトウェアRAIDデバイスをそのままブロックデバイスとして利用しています
- イニシエータとして、NAS本体とRaspberryPiを登録しています。
```
(nas)# apt install targetcli-fb

(nas)# targetcli
targetcli shell version 2.1.53
Copyright 2011-2013 by Datera, Inc and others.
For help on commands, type 'help'.

/> ls
o- / ..................................................................... [...]
  o- backstores .......................................................... [...]
  | o- block .............................................. [Storage Objects: 1]
  | | o- raspi ........................ [/dev/md1 (5.4TiB) write-thru activated]
  | |   o- alua ............................................... [ALUA Groups: 1]
  | |     o- default_tg_pt_gp ................... [ALUA state: Active/optimized]
  | o- fileio ............................................. [Storage Objects: 0]
  | o- pscsi .............................................. [Storage Objects: 0]
  | o- ramdisk ............................................ [Storage Objects: 0]
  o- iscsi ........................................................ [Targets: 1]
  | o- iqn.2022-04.san.laygan.net:raspi .............................. [TPGs: 1]
  |   o- tpg1 ........................................... [no-gen-acls, no-auth]
  |     o- acls ...................................................... [ACLs: 2]
  |     | o- iqn.1993-08.org.debian:01:0123456789ab ........... [Mapped LUNs: 1]
  |     | | o- mapped_lun0 ............................. [lun0 block/raspi (rw)]
  |     | o- iqn.2022-04.raspi4.laygan.net:dca632012345 ....... [Mapped LUNs: 1]
  |     |   o- mapped_lun0 ............................. [lun0 block/raspi (rw)]
  |     o- luns ...................................................... [LUNs: 1]
  |     | o- lun0 .................. [block/raspi (/dev/md1) (default_tg_pt_gp)]
  |     o- portals ................................................ [Portals: 1]
  |       o- 0.0.0.0:3260 ................................................. [OK]
  o- loopback ..................................................... [Targets: 0]
  o- vhost ........................................................ [Targets: 0]
  o- xen-pvscsi ................................................... [Targets: 0]
/>
```

### iSCSI initiatorをインストール
　作成したiSCSIデバイスにOSイメージを展開するため、NAS本体に``open-iscsi``をインストールしておきます。
```
(NAS)# apt install open-iscsi
```
　前述のターゲットでNASのiqnをもう登録してありますが、この内容は以下で確認できます。
```
(NAS)# cat /etc/iscsi/initiatorname.iscsi
```

## RaspiOSのファイルを展開
　``initramfs``を作成する段階でOSのイメージファイルをダウンロード、展開していますので、そのまま利用しても大丈夫です。

:::note info
GUI環境がほしい方
以降の手順で用いるイメージファイルは、Lite版ではないものを予めダウンロード、解凍しておいてください。
:::

### PXE用bootファイル配置
　ダウンロードしたイメージファイルのうち、パーティション1の内容が``/boot``の内容になります。
　それをTFTPのフォルダにコピーします。
　``kpartx``を利用しますので、インストールされていない場合は、``# apt install kpartx``でインストールしてください。
```
(NAS)# kpartx -av 2022-04-04-raspios-bullseye-arm64-lite.img
add map loop0p1 (253:0): 0 524288 linear 7:0 8192
add map loop0p2 (253:1): 0 7766016 linear 7:0 532480
```

今回欲しいファイルは、``/dev/mapper/loop0p1``にありますので、マウントします。
```
(NAS)# mkdir -p /mnt/img/boot
(NAS)# mount /dev/mapper/loop0p1 /mnt/img/boot
```
　マウントしたら、すべての内容をTFTPのディレクトリへコピーしますが、ここで最初にメモしていたRasPiのSerialが必要になります。
　TFTPのディレクトリに、新しくSerialと同じ名前のディレクトリを作ってそこへコピーしてください。
```
(NAS)# mkdir /noraid/share/pxeboot/[RASPBERRYPI-SERIAL]
(NAS)# rsync -av /mnt/img/boot/ /noraid/share/pxeboot/[RASPBERRYPI-SERIAL]
```
　そのあと、コピーされたファイルに「**``bootcode.bin``**」というファイルがあります。このファイルは、TFTPの一番上のディレクトリに配置する必要がありますので、コピーします。
```
(NAS)# cd /noraid/share/pxeboot/[RASPBERRYPI-SERIAL]
(NAS) /noraid/share/pxeboot/[RASPBERRYPI-SERIAL] # cp bootcode.bin ./../.
```

　最後に、先程作成した``initramfs``ファイルを``/noraid/share/pxeboot/[RASPBERRYPI-SERIAL]``のディレクトリにコピーして、PXE用のファイルは展開できました。

### root用ファイルの展開
　次に、メインであるrootファイルを展開します。様々な手法があるのですが、ここから自己流になりますのですこし詳しめに書きます。
#### RaspiOSのイメージファイルをそのままコピー
　一旦、イメージファイルをiSCSIのドライブにコピーします。
　まずはiSCSIドライブに接続します。
```
(NAS)# iscsiadm -m node --login
Logging in to [iface: default, target: iqn.2022-04.san.laygan.net:raspi, portal: 127.0.0.1,3260]
Login to [iface: default, target: iqn.2022-04.san.laygan.net:raspi, portal: 127.0.0.1,3260] successful.
```
　これでiSCSIドライブに接続しました。どのドライブとして認識しているかは、``lsblk``で確認します。
```
(NAS)# lsblk
NAME      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
loop0       7:0    0    4G  0 loop
├─loop0p1 253:0    0  256M  0 part  /mnt/img/boot
└─loop0p2 253:1    0  3.7G  0 part
・
・
・
sdc         8:32   0  5.4T  0 disk
```
　私の出力では、``/dev/sdc``に接続されているようです。
　このまま、``dd``コマンドを利用してイメージをディスクにコピーします。
```
(NAS)# dd id=2022-04-04-raspios-bullseye-arm64-lite.img of=/dev/sdc bs=1m
```
　完了すると、パーティションが2つiSCSIディスクに作成されています。

#### ファイルシステムの変更
　イメージファイルはMBRで管理されており、パーティションの作成等で制限が出てきますので、GPTに変換しておきます。
```
# gdisk /dev/sdc
GPT fdisk (gdisk) version 1.0.6

Warning: Partition table header claims that the size of partition table
entries is 0 bytes, but this program  supports only 128-byte entries.
Adjusting accordingly, but partition table may be garbage.
Warning: Partition table header claims that the size of partition table
entries is 0 bytes, but this program  supports only 128-byte entries.
Adjusting accordingly, but partition table may be garbage.
Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present


***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by
typing 'q' if you don't want to convert your MBR partitions
to GPT format!
***************************************************************


Warning! Secondary partition table overlaps the last partition by
33 blocks!
You will need to delete this partition or resize it in another utility.

Command (? for help):
```
　当時の出力が用意できずに申し訳ありませんが、MBRでしかHDDが管理されていない旨が表示されます。
 ``Command``を要求されますので、
1. ``r	recovery and transformation options (experts only)``
を選択します。

1. MBRの情報を元に、GPTを作成するメニュー、
``f	load MBR and build fresh GPT from it``
を選択します。パーティションが消去されてしまうかもしれないと注意が出ますが、``Y``で続行します。

1. 最後に変更をディスクに書き込みます。
``w	write table to disk and exit``
最終確認が出ますが、よければ続行してください。

### rootファイルパーティションの拡張
　RasPiOSでは初回起動時に``resize2fs``でrootパーティションが拡張されますが、この際に手動で拡張させておきます。
　こちらも、当時の出力がありませんが、
1. ``p``でパーティションを確認
1. 該当パーティション（今回は2）を確認
1. ``resizepart 2``を入力し、ディスクの最後の領域まで拡張


```
# parted /dev/sdc
(parted) p
Number  Start   End     Size    File system  Name                  Flags
 1      4194kB  273MB   268MB   fat32        Microsoft basic data  msftdata
 2      273MB   3865GB  3865GB  ext4         Linux filesystem

(parted) resizepart 2
End [xxxxMB]? 100%
```

となります。EndのサイズはそのままEnterでリサイズされないので、100%と指定して空き領域をすべて使うように指定します。

以上で、PXEboot用環境の構築は完了です。

## TFTP取得ファイルの編集
　序盤の方で、iSCSI rootを利用する際には、``initramfs``の更新が必要になるということを書きましたが、ここでその関連作業を行います。

### cmdline.txtの編集
 このファイルは、Kernelを読み込む前に呼び出されるファイルで、rootファイルシステムの場所等がワンライナーで記載されています。
　以下、デフォルト状態の``cmdline.txt``になります。
```
console=serial0,115200 console=tty1 root=PARTUUID=0ee3e8a8-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh
```
これから、手を加えて
```
ip=::::[HOSTNAME]:eth0:dhcp \
ISCSI_INITIATOR=iqn.2022-04.raspi4.laygan.net:dca632012345 \
ISCSI_TARGET_NAME=iqn.2022-04.san.laygan.net:raspi \
ISCSI_TARGET_IP=[ISCSI_SERVER_IP] \
console=serial0,115200 console=tty1 root=PARTUUID=[rootUUID] \
rootfstype=ext4 \ rootwait 
```

とします。
変更点について、列挙します。


- ``ip=::::[HOSTNAME]:eth0:dhcp``
    - IPアドレスをDHCPで取得するように記載します。``[HOSTNAME]``は好きな名前に置き換えてください。
- ``ISCSI_INITIATOR``
    - 自分自身のiSCSI initiatorのiqnを記載します。``targetcli``で設定した内容と同じ内容を記載します。
- ``ISCSI_TARGET_NAME``
    - 今度は接続先サーバのtarget iqnを記載します。
- ``ISCSI_TARGET_IP``
    - iSCSIサーバIPアドレスを記載します。
- ``root=PARTUUID``
    - ``root``パーティションのUUIDを記載します。確認は``sudo blkid``で行います。



### config.txtの編集
　このファイルには、序盤に作成した``initramfs``の読み込みについて、ファイル末尾に記載します。
```
・・・
[pi4]
# Run as fast as firmware / board allows
arm_boost=1

[all]
initramfs initrd.img-x.xx.xx-vx+
```

## あとしまつ
 NAS上でRaspiOSのイメージと、iSCSIディスクをマウントした状態ですので、それを解除しておきます。
```
(NAS)# iscsiadm -m node --logout
(NAS)# umount /mnt/img/boot
(NAS)# kpartx -dv 2022-04-04-raspios-bullseye-arm64-lite.img
```

# troubleshoot
　この手順で問題ないと思っていざ起動を試したときに、起きた事象について記載していきます。

## Cannnot open access to console, the root account is locked.
　原因ははっきり理解できていませんが、Kernel起動後にrootパーティションを読み込む際、``cmdline.txt``に記載された``PARTUUID``ではないところをリマウントしてしまうようです。
1. ``cmdline.txt``の末尾に``init=/bin/sh``を記載する
1. 再起動後、シェルが立ち上がるので、その状態で一度ルートファイルをアンマウントする。
``# umount /``

1. 現状のディスクマウント状況を``# lsblk``で確認する
1. ルートファイルのディスクをマウントする
``# mount -o remount,rw /dev/mdXXXp2 /``
1. マウントした状態で、``/etc/fstab``の内容を``PARTUUID``指定から、``lsblk``で確認したデバイス名に置き換える
1. ``exit``する。（Kernel panicになるが無視）

再起動前に、``cmdline.txt``の末尾に追加した``init=/bin/sh``を削除しておきます。

## resize2fsが動いている・・・？
　PXEboot用に用意した``cmdline.txt``は、この項目を削除しているのですが、改めてマウントする、iSCSI上の``/boot``にある``cmdline.txt``により、パーティションのリサイズが始まってしまっているような気がします。
　``iscsiadm``でマウントして作業している際、こちらの``cmdline.txt``についても、``init=・・・``を削除したほうが良いのかもしれません。（未検証）

# さいごに
　現在は全く別環境で動いているので、その時の記憶ベースで記載している内容になります。誤りがあったら申し訳ありません。もしこの記事の需要があって、どうにもうまく動かない場合は、コメントなどでお知らせいただければと思います。

# 参考文献
https://bompopo.wordpress.com/2013/02/07/raspberry-piraspbian%E3%81%A7initramfs%E3%82%92%E4%BD%BF%E3%81%86/

https://qiita.com/mofumoffy223/items/6950754078002fd3c21e

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0384?page=2

https://raspberrypi.stackexchange.com/questions/92557/how-can-i-use-an-init-ramdisk-initramfs-on-boot-up-raspberry-pi

https://int128.hatenablog.com/entry/20090407/1239124472

https://mattintosh.hatenablog.com/entry/20140926/1411686000

https://qiita.com/ch7821/items/cd760b8be44434d634ad

https://lab4ict.com/system/archives/3758

https://matt.olan.me/raspberry-pi-iscsi-root-on-ubuntu-20-04/

https://raspberrypi.akaneiro.jp/archives/413

https://www.server-world.info/query?os=Debian_10&p=iscsi&f=3

https://www.server-world.info/query?os=Debian_10&p=iscsi&f=1

https://monteecristoo.hatenablog.com/entry/2020/07/29/123741