EWI-USB用の音源をRaspberry Pi 3A+で作ろうと思って、Raspberry Pi OS Liteをできるだけ軽く動くようにヘッドレスで設定をした記録です。以前はbullseyeでやってたんですが、使いたいシンセサイザーエンジンがbookwormを必要とし始めたので、やり直しました。bullseyeまでとずいぶん勝手が違いますね。
OSイメージの書き込み
2022年春以降、Raspberry Pi OSのイメージファイルからデフォルトユーザが削除されました。その分、Raspberry Pi Imager(https://www.raspberrypi.com/software/ から入手、以下RPiImager)の機能が拡充され、ユーザの設定だけでなくSSHやWi-Fiおよび使用国、ロケールを詳細設定(右下の歯車マーク)からあらかじめ指定してmicroSDに書き込めるようになっているので、これを利用するのが一番良い選択だと思います。
RPiImagerのバージョンには注意してください。Windows環境ではダウンロードしたインストーラを素直に起動すれば最新版がインストールされますが、筆者の母艦(Ubuntu 22.04.4 LTS)では、公式ページからダウンロードしたdebファイルを開いても、ページで案内されているsudo apt install rpi-imager
を実行しても、インストールされたのはリポジトリに登録されていた1.7.2に留まっていました。
1.7.2で提供されるfirstrun.sh
ではWi-Fiの設定がdhcpd前提のままで、bookwormで標準化されているNetworkManagerの設定ができません。「bookwormではヘッドレス設定はできないらしい」と解説しているページもあったのは、このせいかも。
あれこれ調べて結局、最新版(執筆時点でバージョン1.8.5)をインストールする方法は
sudo snap install rpi-imager
でした。困っている方はお試しを。
RPiImagerではOS が選べますが、今回は「Raspberry Pi OS(other)」を選んで出てくる64ビットLiteをRPi3A+で利用しました。Zeroや2以前は32ビットOSしか使えませんが、手順としては同様です。
ユーザー設定、Wi-fiの設定、使用国と言語、SSHの有効化を設定して、書き込みへ進みましょう。
起動前に設定できることはやっておきたい
Wi-FiのIPアドレスを固定したい
DHCPでIPアドレスをもらってくる時間を端折りたかったので、固定しました。
RPiImagerは詳細設定をbootfs
にあるfirstrun.sh
に書き込み、初回起動時に実行することで各種設定を進めていくので、事前にこのファイルへ埋め込んでしまいます。
下から3行目に
rm -f /boot/firstrun.sh
とあるので、この前に追加しました。たぶん最終行exit 0
の前ならいいんだと思いますが、実行される前にスクリプトが削除されるのが気持ち悪いのでここにしています。
アドレス固定、DNSはGoogleパブリック、使う気がないIPv6は無効化、の設定を追記します。
systemctl start NetworkManager.service
nmcli connection reload
nmcli connection modify preconfigured ipv4.method "manual" ipv4.addresses "192.168.xx.yy/24" ipv4.gateway "192.168.xx.zz"
nmcli connection modify preconfigured ipv4.dns 8.8.8.8
nmcli connection modify preconfigured ipv6.method disabled
nmcli connection down preconfigured
nmcli connection up preconfigured
preconfigured
は初回起動時の処理の中で作られるWi-Fiコネクションの識別子です。NetworkManagerがどのタイミングで起動しているか判別できなかったので、サービスを起動→コネクションの読み直し→アドレス固定、DNS設定、IPv6無効化→コネクション落として上げなおし、という手順にしています。
念のため記しておくと、192.168.xx.yy
が固定したいアドレス、192.168.xx.zz
がゲートウェイのアドレスです。お手許の環境に合わせて書き換えてください。8.8.8.8
はGoogleパブリックDNSのアドレスです。
DNSをGoogleパブリックにしていると、せっかくRPiImagerでホスト名を付けてもDNSに登録されないので、後の作業でホスト名でのアクセスができません。ルータがDNSを提供しているなら、DNSのアドレスはルータのアドレスにします。
https://netlog.jpn.org/r271-635/2024/04/raspberrypi_install_ver_bookworm.html
こちらのページではfirstrun.sh
が何をやっているかを詳細に見ておられていて大変興味深いのですが、このバージョンではfirstrun.sh
が途中で呼び出すimager_custom
で一発モノのデーモンを作って、再起動後にそれが動いてpreconfigured
を作っています。先述のRPiImager1.7.2ではこのような動作がなくwpa_supplicant.conf
を作るのみでした。今回使った1.8.5では動作がまた変更されていて、imager_custom
はpreconfigured.nmconnection
を直接作っています。そのため本稿ではこのようにしていますが、RPiImagerのバージョンが変わったらまたやり方を変えなければいけないかもしれません。
config.txtの設定
bootfs
にあるconfig.txt
は、初回起動時に/boot/firmware/cmdline.txt
にコピーされます。ここで編集しておくと便利です。
[all]
の下に追加していきます。以下は筆者の好みです。
# メモリの割り当て変更:デフォルトではGPUに64MB振られているのを最低の16MBに
gpu_mem=16
# 動作クロックをターボモードに
force_turbo=1
# Bluetooth無効
dtoverlay=disable-bt
# 電源LEDはデフォルトではパワーLEDは稼働時もシャットダウン後も点灯したままなので
# 点滅させて稼働中なのがわかるように
dtparam=pwr_led_trigger=heartbeat
bootfs
にあるcmdline.txt
も、config.txt
同様に初回起動時に/boot/firmware/cmdline.txt
にコピーされます。
以前のOSでは「cmdline.txt
にquiet
を追加して起動時のメッセージを抑制、起動時間を稼ぐ」というのをやっていましたが、今回のOSイメージではcmdline.txt
にもともとquiet
って書かれてたので編集していません。他に指定するような設定があるなら、ここで先に設定しておくのは有用だと思います。
起動後に行う設定
bootfs
に配置されている設定ファイルは先にいじることができますが、rootfs
にあるファイルは権限が設定されていたりシステムで書き換えられたりするので、起動後に設定することになります。
準備したmicroSDをRPiにセット、起動したら、RPiImagerの詳細設定でつけたホスト名もしくは固定したIPアドレスにSSHでアクセスし、最初の設定を進めていきます。
IPアドレスをDHCPから得ている場合、DHCPの払い出しログなどでRPiのMACアドレスB8:27:EB:〜
、DC:A6:32:〜
、E4:5F:01:〜
のいずれかに対応するIPアドレスを探します。
パッケージの更新
この後でテンポラリフォルダのRAMディスク化をしてしまうとうまくいかなくなることがあるので、先にやってしまいましょう。
apt
はupgrade
でいいかdist-upgrade
までやるかは状況によると思います。
sudo apt update
sudo apt -y upgrade
sudo apt -y dist-upgrade
sudo rpi-update
起動スピードを稼ぐ
ちょっとだけでもと思ってやっています。
必要なさそうなサービスの停止
firstrun.sh
に書き込んで先にやってしまうのを試してみたんですが、うまくいかなかったので起動後に。
sudo systemctl disable avahi-daemon
sudo systemctl disable systemd-timesyncd
sudo systemctl disable bluetooth
sudo systemctl disable hciuart
sudo systemctl disable ModemManager
sudo systemctl disable rsync
sudo systemctl disable triggerhappy
検索したり雑誌を読んだりしていると、ofono
、psplash-start
、psplash-systemd
、plymouth
も止めましょうという話もありますが、今回のインストールではもともと入っていませんでした。
aptの日次更新を止める
AutoAptEnable
を0
に。エディタで開いて該当部分を書き換えればいいのですが、たった1箇所を探すのが面倒なのでsed
でやっちゃいます。
sudo sed -i 's/^AutoAptEnable=1/AutoAptEnable=0/' /usr/lib/apt/apt.systemd.daily
ntp問い合わせ先の設定
上記でsystemd-timesyncd
を止めてしまっているので起動スピードには直接関係がないですが、起動後に時刻合わせをしたくなった時、ntpが聞きに行くサーバが遠いとレスポンスも遅くなるので、日本用のを使うようにします。
こちらを参考に、timesyncdの設定を変更しました。
sudo nano /etc/systemd/timesyncd.conf
[Time]
NTP=ntp.nict.jp
FallbackNTP=ntp1.jst.mfeed.ad.jp ntp2.jst.mfeed.ad.jp ntp3.jst.mfeed.ad.jp
時刻が合っていないと、SSL通信をする際の証明書照合でエラーになります。aptやpipなどでパッケージのインストールや更新をするときにSSL通信が必要なことがあるので、その前には時刻合わせをしておく必要があるでしょう。
cat >~/timeadjust.sh <<EOF
#!/bin/bash
sudo systemctl enable systemd-timesyncd.service
sudo systemctl restart systemd-timesyncd.service
sudo systemctl disable systemd-timesyncd.service
EOF
chmod +x ~/timeadjust.sh
として、ホームディレクトリにtimeadjust.sh
を作っておけば
~/timeadjust.sh
で時刻合わせができます。
SDカードの延命処置
参考にしたのはこちらです。
当時はbullseyeだったのでありがたくこの通りやらせていただいていましたが、bookwormで少し事情が変わっています。
なお、IoTシステムなんかで長期間手許を離れるようなものだとF2FSのようなフラッシュメモリ向けファイルシステムへの変更までしておいたほうがいいのでしょうが、手間がかかるので今回はやっていません。
ログの出力を停止
ログはbookwormではjournaldに集約されています。
ログの保存場所はデフォルトで自動的に決定され、あれば/var/log/journal
、なければ/run/log/journal
です。df -T
で確認すると、/run
はファイルシステムがtmpfsですが、これは後述の設定でRAMディスク化してしまうので、/var/log/journal
が存在しなければSDカードには書き込まれないことになります。
ls -r /var/log/journal
で確認しておきます。
ls: cannot access '/var/log/journal': No such file or directory
と出なかった(つまり存在している)場合は、
sudo rm -r /var/log/journal
で削除しておきましょう。
ログの更新頻度とバックアップを抑制
/var/log/
には他のログがあり、それがローテーションでバックアップされていくのがSDカードを傷めていくと言われているので、止めてしまいます。
logrotate
の設定ファイルをsed
で書き換えます。頻度をmonthly
に、ローテーションをrotate 0
にします。
sudo sed -i 's/daily$/monthly/' /etc/logrotate.conf
sudo sed -i 's/weekly$/monthly/' /etc/logrotate.conf
sudo sed -i 's/rotate .*$/rotate 0/' /etc/logrotate.conf
また、/etc/logrotate.d/
には個別のログ更新設定ファイルが保存されているので、全部書き換えます。
この中にはrotate
の指定が2か所あるものもあるので、g
をつけて全て対象にします。
sudo sed -i 's/daily$/monthly/g' /etc/logrotate.d/*
sudo sed -i 's/weekly$/monthly/g' /etc/logrotate.d/*
sudo sed -i 's/rotate .*$/rotate 0/g' /etc/logrotate.d/*
スワップファイルのRAMディスク化
まず、スワップファイルを止め、dphysを削除します。
sudo swapoff --all
sudo /etc/init.d/dphys-swapfile stop
sudo apt purge -y dphys-swapfile
Zramの導入は一発でできるようになってます。ありがたいことです。
aptのアップデートはやっておきましょう。
sudo apt update
sudo apt install -y zram-tools
今回の環境では調整するほどのRAMサイズが元々ないので、デフォルトのままにしました。RAM容量の大きいモデルを利用する場合は調整を考えてください。
tmpフォルダのRAMディスク化
大変参考になった元ネタはこちら。
sudo nano /etc/fstab
次の3行を追記します。
tmpfs /tmp tmpfs defaults,size=32m,noatime,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=16m,noatime,mode=1777 0 0
tmpfs /var/log tmpfs defaults,size=16m,noatime,mode=0755 0 0
/var/tmp
はRAMディスク化するな、というのがArch wikiに書かれているそうです。筆者の環境では問題なく動いていますが、やめた方が無難かもしれません。
これを行ってしまうと、後々導入するライブラリやパッケージが領域不足でビルドできなくなることがありました。RPi3A+を使いましたが、RAMディスクの容量を64MB以上にするとビルド中にハングアップしてしまいます(たぶんメインメモリが足りない)。ビルド中に画面が止まるようなら失敗とみて、このビルドの間だけRAMディスク化を解除したほうが良さそうです。
sudo sed -i 's/^tmpfs /#tmpfs /g' /etc/fstab
行の先頭にtmpfs
があれば#をつけてコメントアウト、というコマンド。実行したら再起動して、ビルドし直してください。終わったら逆の
sudo sed -i 's/^#tmpfs /tmpfs /g' /etc/fstab
を行って再起動すればOKです。
クリーンアップ
先述の元ネタではRAM環境で起動を確認してからにされてますが、横着してリブートせずに進めます。真面目な方は元ネタの通りに進めたほうがいいでしょう。
sudo apt autoremove
sudo apt autoclean
sudo rm -rf /var/lib/apt/lists/*
sudo rm -rf /var/cache/apt/archives/*
sudo rm -rf /tmp/*
sudo rm -rf /var/tmp/*
sudo rm -rf /var/log/*
sudo rm /var/swap
再起動後にもろもろ有効になります。
その他、注意点
- bookwormではオーディオサブシステムがPulseAudioからPipeWireへ変更された、という話ですが、RPiOS LiteではPipeWireどころかPulseAudioすら入っていませんでした。
- pythonで利用していたRPi.GPIOが廃止されています。gpiozeroなどに移行する必要があります。
- pythonの環境は仮想化が推奨されるようになりました。とはいえ、RPiの環境なんて仮想化せずとも、プロジェクトごとに安いSDカードを差し替えれば良い話と思っているので、ライブラリの導入時にはpipの
--break-system-packages
オプションで強引にやってしまっています。sudo
でやるとインストール後「venvとか使えや」って言われますが、筆者は知らん顔しています。ちゃんとvenvで対応したい方はこちらの素晴らしい記事を参考にしてください。
なにか気づけば追記していきます。
余談
このあたりのコマンドは動作確認に役に立ちました。
# インストールされているパッケージをすべて表示
sudo apt list --installed
# 現在実行されているサービスの一覧
sudo systemctl | grep running
# システム起動時のパフォーマンス
systemd-analyze time
# systemd各ユニットの初期化時間
systemd-analyze blame
# CPUの温度
vcgencmd measure_temp
# CPUの現在のクロック数
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq