Raspberry Pi を使った講習みたいな仕事が舞い込んできました. 各人に Raspberry Pi を配布して, SD カードを渡して, Imager を使ったインストーラを体験させ, Linux の一通りのオペレーションを教えた後, apt-get やら pip でほーらこんな感じでカスタマイズできるんだよと伝えます.
講習の目的は, さらにその後いろいろインストールして「あいおーてぃー」みたいな機器を作ることなんですが, 講習内でいろいろインストールさせるのは時間の無駄です. そこで, こんな感じでインストールしていきます, こちらが出来上がりました SD カードです, 差し替えて動かしてみましょうという料理番組方式を採用することにしました.
SD カードの複製ですが, まず一通り動く SD カードを作った後 適当な Linux マシンを使って dd でダビングすれば出来なくはないのですが, 近年の SD は 32G とか 64G とかあり全領域をダビングするとえらい時間がかかります.
という風にすれば時間が節約できるはずで, その方法を探してみました.
作業は適当な Linux マシンで行います. その Linux マシンから Raspberry Pi の SD が何に見えるかは環境次第ですが, ここでは /dev/sdc に見えるとして話を進めます. また近年の Linux ディストリビューションは USB メモリや SD を挿すと勝手に マウントしてきます. まあ便利と言えば便利なのですが今回の作業では毎回いちいちアンマウントすることとなり手間です. こいつを司っているのは udisksd というプログラムなので, 作業を開始するにあたって
ps aux | grep udisk |
とかやって udisksd のプロセス番号を調べて kill しておくといいでしょう.
普通に Raspberry Pi OS を SD にインストールすると
の二つのパーティションが作られます. 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/sdc1 | 8192 | 532479 | 524288 | 256M | c | W95 FAT32 (LBA) |
/dev/sdc2 | 532480 | 124735487 | 124203008 | 59.2G | 83 | Linux |
といった感じで表示されます. ここで大事なのはパーテション 2 (/dev/sdc2) が始まるセクタ番号 (この例では 532480) です. 必要ならメモを取ってもいいですが, 多分必要ありません.
d コマンド (delete) で 2 を消し, n コマンド (new) で新たに作ります. primary partition の 2 番目, 最初のセクタ番号は先ほど大事と言った番号 (532480), 最後のシリンダは +4G と指定します. ここで 4G はあくまで例で, もし resize2fs で 4G に収まらなくてもう少し大きめにしたのならその値にします. 署名云々と言われたら無視しましょう. 書き換える必要はありません. もう一度 p コマンドを打つと最後の数が変わっているはずです.
/dev/sdc1 | 8192 | 532479 | 524288 | 256M | c | W95 FAT32 (LBA) |
/dev/sdc2 | 532480 | 8921087 | 8388608 | 4G | 83 | Linux |
今度は大事なのは最後のセクタ番号 (この例では 8388608) です. 必要ならメモりましょう. 無事にパーティションが書き換わったので w コマンド (write) で fdisk を終了します. なんなら /dev/sdc2 をマウントしてみて正しく見れるか確認してもいいでしょう.
ディスクイメージを吸い出しは以下のようなコマンドで実現できます. この例では Raspi.img というファイルが生成されます.
dd bs=1 if=/dev/sdc of=Raspi.img count=4097 |
ここで count の値は先ほど得た値 (8388608) を 2048 で割って 1 足した整数値にします. 8388608 は 2048 で割り切れるので多分 4096 でも大丈夫じゃないかと思うのですが保険です. bs はメディアによって 1 以外の値にした方が速くなるという話は聞きますが試していません.
SD の生産の手順は以下の通りです.
ここの手順は比較的単純ですが, 講習会のため 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 に割り当てられたイメージが生成されるようになります.