Raspberry Pi OS の量産

[戻る]

1. はじめに

Raspberry Pi を使った講習みたいな仕事が舞い込んできました. 各人に Raspberry Pi を配布して, SD カードを渡して, Imager を使ったインストーラを体験させ, Linux の一通りのオペレーションを教えた後, apt-get やら pip でほーらこんな感じでカスタマイズできるんだよと伝えます.

講習の目的は, さらにその後いろいろインストールして「あいおーてぃー」みたいな機器を作ることなんですが, 講習内でいろいろインストールさせるのは時間の無駄です. そこで, こんな感じでインストールしていきます, こちらが出来上がりました SD カードです, 差し替えて動かしてみましょうという料理番組方式を採用することにしました.

SD カードの複製ですが, まず一通り動く SD カードを作った後 適当な Linux マシンを使って dd でダビングすれば出来なくはないのですが, 近年の SD は 32G とか 64G とかあり全領域をダビングするとえらい時間がかかります.

  1. まず普通にマスタ SD を作り
  2. ギリギリ収まるよう圧縮して
  3. その部分だけを dd でファイル化し
  4. 新しい sd に dd でコピーし
  5. 展開する

という風にすれば時間が節約できるはずで, その方法を探してみました.

2. Linux マシンの準備

作業は適当な Linux マシンで行います. その Linux マシンから Raspberry Pi の SD が何に見えるかは環境次第ですが, ここでは /dev/sdc に見えるとして話を進めます. また近年の Linux ディストリビューションは USB メモリや SD を挿すと勝手に マウントしてきます. まあ便利と言えば便利なのですが今回の作業では毎回いちいちアンマウントすることとなり手間です. こいつを司っているのは udisksd というプログラムなので, 作業を開始するにあたって

ps aux | grep udisk

とかやって udisksd のプロセス番号を調べて kill しておくといいでしょう.

3. マスタ SD の圧縮

普通に Raspberry Pi OS を SD にインストールすると

  1. fat32 の領域
  2. ext4 の領域

の二つのパーティションが作られます. Linux マシンにSD を挿すと (上述のように /dev/sdc に見えたとしたら) それぞれ/dev/sdc1, /dev/sdc2 に割り当てられます. このうち sdc1 は 256Mbyte くらいで微々たるものです. 圧縮してもしなくても大したことがありません. SD の容量のほとんどは sdc2 に割り当てられています. 64G の SD なら 63.1G 程度がこれです. しかもこれのほとんどが未使用です. ここを潰します.

潰すには resize2fs というプログラムを使います. resize2fs を実行するにあたり事前に fdisk を実行しておかなければならないので まず一発

fdisk -f /dev/sdc2
と実行した後,
resize2fs /dev/sdc2 4G

などと打ちます. 4G だと小さすぎて入らないと言われたら入るまで大きくしていきます. resize2fs が無事圧縮できたら次に

fdisk /dev/sdc

のように fdisk を立ち上げます. fdisk の中で p コマンド (print) を打つと

/dev/sdc18192532479524288256McW95 FAT32 (LBA)
/dev/sdc253248012473548712420300859.2G83Linux

といった感じで表示されます. ここで大事なのはパーテション 2 (/dev/sdc2) が始まるセクタ番号 (この例では 532480) です. 必要ならメモを取ってもいいですが, 多分必要ありません.

d コマンド (delete) で 2 を消し, n コマンド (new) で新たに作ります. primary partition の 2 番目, 最初のセクタ番号は先ほど大事と言った番号 (532480), 最後のシリンダは +4G と指定します. ここで 4G はあくまで例で, もし resize2fs で 4G に収まらなくてもう少し大きめにしたのならその値にします. 署名云々と言われたら無視しましょう. 書き換える必要はありません. もう一度 p コマンドを打つと最後の数が変わっているはずです.

/dev/sdc18192532479524288256McW95 FAT32 (LBA)
/dev/sdc2532480892108783886084G83Linux

今度は大事なのは最後のセクタ番号 (この例では 8388608) です. 必要ならメモりましょう. 無事にパーティションが書き換わったので w コマンド (write) で fdisk を終了します. なんなら /dev/sdc2 をマウントしてみて正しく見れるか確認してもいいでしょう.

4. ディスクイメージの吸出し

ディスクイメージを吸い出しは以下のようなコマンドで実現できます. この例では Raspi.img というファイルが生成されます.

dd bs=1 if=/dev/sdc of=Raspi.img count=4097

ここで count の値は先ほど得た値 (8388608) を 2048 で割って 1 足した整数値にします. 8388608 は 2048 で割り切れるので多分 4096 でも大丈夫じゃないかと思うのですが保険です. bs はメディアによって 1 以外の値にした方が速くなるという話は聞きますが試していません.

5. SD の生産

SD の生産の手順は以下の通りです.

  1. dd を使って新しい SD に先ほど得たディスクイメージ (Raspi.img) を書き込む
  2. fdisk を使ってパーティション 2 の大きさをディスクいっぱいいっぱいにする
  3. resize2fs でパーティション 2 いっぱいを有効にする
  4. 必要なら静的 IP アドレスを設定する

ここの手順は比較的単純ですが, 講習会のため 5 枚 10 枚と生産するのはなかなか手間で自動が望まれます. という訳で自動化ですが, まず以下のようなデータを fdiskdata.txt という名前で, Raspi.img と同じディレクトリに置きます. fdisk に食わせるデータです. 6 行目の 532480 はパーティション開始セクタの番号で先ほど出て来た数値です.

d
2
n
p
2
532480

w

同様に静的 IP アドレス設定を済ませた /dev/dhcpcd.conf も同ディレクトリにコピーします. 静的 IP アドレス設定を済ませた該当ファイルは

interface eth0
static ip_address=192.168.1.100/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8

などと書かれているはずです. 後で書き替えたい部分はこの例では 100 と書かれた部分です. という訳で書き替えたい部分を @@@ という文字列に書き換えておきます.

interface eth0
static ip_address=192.168.1.@@@/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8

そして次のスクリプトを sdwrite.sh という名前ででも保存しておきましょうか.

if [ $# -ne 1 ]; then
 "Usage: $0 IPAddress"
 exit 1
fi

sudo dd bs=1M if=Raspi.img of=/dev/sdc
sleep 1
sudo partprobe
sleep 1
sudo fdisk /dev/sdc < fdiskdata.txt
sleep 1
sudo e2fsck -f /dev/sdc2
sudo resize2fs /dev/sdc2
sleep 1
sudo mount /dev/sdc2 /mnt
sed "s/@@@/$1/g" dhcpcd.conf | sudo tee /mnt/etc/dhcpcd.conf > /dev/null
sudo umount /mnt

後は新しい SD を挿して (当然 /dev/sdc に見えるようにしてないといけない, そうで無い場合は適当に sdwrite.sh を書き換える),

sh sdwrite.sh 100

とでも打てば静的アドレスが 192.168.1.100 に割り当てられたイメージが生成されるようになります.


2022.11