自分好みのラズパイイメージを作成する

# 1 はじめに
　Raspberry Piで色々実験を行っていると、それまでの作業をなかったことにして、最初からやり直したいときがある。最初からといっても、決まりきったライブラリのインストールやビルドなどは、やり直したくない。そこで、公式のOSイメージをカスタマイズし、aptやpipを使ったインストールやライブラリのビルドを事前に行った状態のOSイメージを作成しておくことにする。

　カスタムイメージを作成する方法としては、一連のインストールを終えたのちに、SDカードイメージをダンプするという手もとれる。しかし、この方法のデメリットとして、書き込み先SDカードのサイズがダンプサイズより大きくないと手間がかかることにある。同じサイズのSDカード、例えば32GBカードであっても、メーカーやロットによって微妙なサイズが異なる。同時期にかった同じメーカーとの同じ型番のSDカードにダンプイメージをコピーできなかったときのショックは極めて大きい。

また、公式のOSイメージは、起動時にパーティションサイズを自動で最大まで拡張するので、使用するSDカードのサイズに影響されないという特徴がある。

# 2 準備するもの
* Raspberry Pi（今回は4Bを使うが他のモデルでもいけるはず）
* Raspberry Pi OSファイル
https://www.raspberrypi.com/software/operating-systems/
* マイクロSDカード2枚
    * OSイメージをカスタムするためのOSを動かすSDカード
    * カスタムしたOSイメージを動かすためのSDカード

# 3 手順
# 3.1 環境の準備
まずは、カスタムイメージを操作する環境として、通常通りのOSイメージを用意する。Raspberry Pi ImagerなどでOSをSDカードに書き込む。トラブル防止のため、カスタムするOSと作業するOSは同じとする。今回はBullseye Lite 64bitを使う。起動するまでは、きわめて普通。

# 3.2 カスタム対象の準備
とりあえず、ユーザー名はuser、作業ディレクトリ名はホームのworkとする。
起動したら、カスタムしたいOSイメージのファイルをRPiの中に書き込む。wgetやcurlで取得してもいいし、sshなどでローカル転送してもよい。

今回は個々のファイルを使った。
https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64-lite.img.xz

以下の状態にする。
```
$ ls -al /home/user/work/
-rw-r--r-- 1 xacti xacti 322504480 Sep 17 18:04 2023-05-03-raspios-bullseye-arm64-lite.img.xz
```

次に、パーティションテーブルからデバイスマップを作成するアプリケーションをインストールする。
```
$ sudo apt install kpartx
```

圧縮されたOSイメージを伸長する。オプション付けていないので元ファイルはなくなる。
「.xz」がとれた「2023-05-03-raspios-bullseye-arm64-lite.img」が生成される
```
$ unxz 2023-05-03-raspios-bullseye-arm64-lite.img.xz
```

## 3.3 パーティションのサイズ拡張
事前にルートパーティションのサイズを拡張する。というのも後述のfdiskの結果の通り、全体で1.96GiB、デフォルトのルートパーティションには1.7GBしか割り当てがない。多くのソフトウェアをインストールすると、領域不足になる。ちょっと入れるだけなら不要な作業なのでスキップしてもよい。

```
$ fdisk -l 2023-05-03-raspios-bullseye-arm64-lite.img
Disk 2023-05-03-raspios-bullseye-arm64-lite.img: 1.96 GiB, 2101346304 bytes, 4104192 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x544c6228

Device                                      Boot  Start     End Sectors  Size Id Type
2023-05-03-raspios-bullseye-arm64-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2023-05-03-raspios-bullseye-arm64-lite.img2      532480 4104191 3571712  1.7G 83 Linux
```

### 3.3.1 ファイルを指定したサイズに変更
全体で4GBとしておく。必要に応じてもっと大きくしてもよい。
```
$ truncate -s 4GiB 2023-05-03-raspios-bullseye-arm64-lite.img
```

確認すると「4 GiB」となっている。
```
$ fdisk -l 2023-05-03-raspios-bullseye-arm64-lite.img
Disk 2023-05-03-raspios-bullseye-arm64-lite.img: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x544c6228

Device                                      Boot  Start     End Sectors  Size Id Type
2023-05-03-raspios-bullseye-arm64-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2023-05-03-raspios-bullseye-arm64-lite.img2      532480 4104191 3571712  1.7G 83 Linux
```

### ルートパーティションサイズの変更
ルートパーティションを1.7GBから3.5GBに拡張する。入力は以下のようにしている。「2023-05-03-raspios-bullseye-arm64-lite.img2」の開始セクタ番号は変える必要はないので、532480はそのまま使う。
```p, d, 2, n, p, 2, 532480, +3.5G, p, w```
ログが長いけど、つまりは2番目のパーティションを削除してから作り直している。

```
$ fdisk 2023-05-03-raspios-bullseye-arm64-lite.img

Welcome to fdisk (util-linux 2.36.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): p
Disk 2023-05-03-raspios-bullseye-arm64-lite.img: 4 GiB, 4294967296 bytes, 8388608 sectorsUnits: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x544c6228

Device                                      Boot  Start     End Sectors  Size Id Type
2023-05-03-raspios-bullseye-arm64-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2023-05-03-raspios-bullseye-arm64-lite.img2      532480 4104191 3571712  1.7G 83 Linux

Command (m for help): d
Partition number (1,2, default 2): 2

Partition 2 has been deleted.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2): 2
First sector (2048-8388607, default 2048): 532480
Last sector, +/-sectors or +/-size{K,M,G,T,P} (532480-8388607, default 8388607): +3.5G

Created a new partition 2 of type 'Linux' and of size 3.5 GiB.
Partition #2 contains a ext4 signature.

Do you want to remove the signature? [Y]es/[N]o: N

Command (m for help): p

Disk 2023-05-03-raspios-bullseye-arm64-lite.img: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x544c6228

Device                                      Boot  Start     End Sectors  Size Id Type
2023-05-03-raspios-bullseye-arm64-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2023-05-03-raspios-bullseye-arm64-lite.img2      532480 7872511 7340032  3.5G 83 Linux

Command (m for help): w
The partition table has been altered.
Syncing disks.
```

確認する。3.5GBに増えてる。
```
$ fdisk -l 2023-05-03-raspios-bullseye-arm64-lite.img
Disk 2023-05-03-raspios-bullseye-arm64-lite.img: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x544c6228

Device                                      Boot  Start     End Sectors  Size Id Type
2023-05-03-raspios-bullseye-arm64-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2023-05-03-raspios-bullseye-arm64-lite.img2      532480 7872511 7340032  3.5G 83 Linux
```

# 4 カスタム先のrootになる
## 4.1 デバイスマップ作成
kpartxを使いデバイスマップを作成
```
$ sudo kpartx -av 2023-05-03-raspios-bullseye-arm64-lite.img
add map loop0p1 (254:0): 0 524288 linear 7:0 8192
add map loop0p2 (254:1): 0 3571712 linear 7:0 532480
```

確認する。1番目のパーティションが /boot で loop0p1に対応、2番目のパーティションがルートファイルシステムでloop0p2に対応。
```
$ ls /dev/mapper/
control  loop0p1  loop0p2
```

## 4.2 パーティションのリサイズ
パーティションのリサイズを実行。ここで、パーティションのリサイズを実行。これしないで、何度か容量不足のエラーをみた。

```
$ sudo fsck -n /dev/mapper/loop0p2
fsck from util-linux 2.36.1
e2fsck 1.46.2 (28-Feb-2021)
rootfs: clean, 41802/111776 files, 356622/446464 blocks

$ sudo e2fsck -f /dev/mapper/loop0p2
e2fsck 1.46.2 (28-Feb-2021)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
rootfs: 41802/111776 files (0.1% non-contiguous), 356622/446464 blocks

$ sudo resize2fs /dev/mapper/loop0p2
resize2fs 1.46.2 (28-Feb-2021)
Resizing the filesystem on /dev/mapper/loop0p2 to 917504 (4k) blocks.
The filesystem on /dev/mapper/loop0p2 is now 917504 (4k) blocks long.
```

## 4.3 マウント
マウントポイントを作成し、マウントする
```
$ sudo mkdir tmpmnt
$ sudo mount -t ext4 -o loop /dev/mapper/loop0p2 tmpmnt/
```

中身を確認すると、ルート以下のディレクトリ構成が見える。これらがカスタム対象。
```
$ ls tmpmnt/
bin  boot  dev  etc  home  lib  lost+found  media  mnt  opt  proc  root  run  
sbin  srv  sys  tmp  usr  var
```

領域のサイズも指定したサイズ。
```
$ df -h tmpmnt/
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      3.5G  1.3G  2.0G  41% /home/xacti/work/tmpmnt
```

# 4.4 パッケージのインストール準備
chrootして、パッケージインストールできる状態になる。
```
$ sudo chroot tmpmnt/
root@raspberrypi:/#
```

ここから先はカスタム先のOSに対する操作となる。

時間を確認すると、タイムゾーンがBST（英国）になっている。
```
# date
Sun 16 Sep 10:26:14 BST 2023
```

aptなどでトラブルが起きると嫌なので、JSTにしておく。
```
# rm -f /etc/localtime 
# ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# root@raspberrypi:/# date
Sun 16 Sep 18:33:31 JST 2023
```

update & upgradeしておく。必要ないならスキップ。
```
# apt update
# apt upgrade
```

**通信ができない場合**
環境によっては、DNSの影響かもしれない。自分が当たった状況では、
```
# nano /etc/resolv.conf 
```
で nameserverをデフォルトの8.8.8.8から接続しているルーターのIPアドレスにした。

### 4.5 パッケージのインストール
ここまできたら、apt installし放題。また、/etc以下の設定ファイルをあらかじめ変更しておくことも可能。

これは自分用のメモ
```
apt -y install libssl-dev libcurl4-openssl-dev liblog4cplus-dev libcgroup-dev gir-to-d
apt -y install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-tools
apt -y install gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly
apt -y install jq　python3-pip
sudo apt -y install gir1.2-gst-rtsp-server-1.0
sudo apt -y install libcairo2-dev libxt-dev libgirepository1.0-dev
pip install pycairo PyGObject
```

# 5 後始末
コマンド履歴の削除とaptのキャッシュクリア。操作に応じて残骸ファイルの削除なども行う。
```
# history -c
# apt clean all
```
Ctrl+D でchroot状態を抜ける。
アンマウントして、デバイスマップを解除。
```
$ sudo umount tmpmnt
$ sudo kpartx -d 2023-05-03-raspios-bullseye-arm64-lite.img
```

# 6 カスタムOSイメージの圧縮
変更したファイルを以下のコマンドで元の形式に圧縮する。一応ファイル名を変えておく、実行環境はパワーのあるPC上とかの方がいい。xzは処理が遅い。
```
$ mv 2023-05-03-raspios-bullseye-arm64-lite.img 2023-05-03-raspios-bullseye-arm64-lite-custom.img
$ time xz -9 2023-05-03-raspios-bullseye-arm64-lite-custom.img
```

# 7 おまけ
ブート領域のファイルの変更もできる。
マウントを以下のようにloop0p1にすればconfig.txtなどが見えるので、変更しておけば機能の無効化やGPUメモリの変更などをあらかじめ指定しておくことができる。
```
$ sudo mount -t ext4 -o loop /dev/mapper/loop0p1 tmpmnt/
```
単なるファイル群なので、chrootなどは不要。nanoなどでファイル変更して保存すればよい。


# 今後
* 追加パーティションを作るようにしたい。/boot、/、/extみたいな状態。