Goでラズパイ向けアプリ開発する手法
Go Advent Calendar 2025の12日目が開いてたので参加しますー。
若干情報不足なのはご了承ください
メモ的に書き起こしています。第三者が実践するためには若干情報不足かと思います。
ラズパイアプリ開発のヒントになればと思い公開しておきます。順次、追記する予定ではあります。
ラズパイで動くアプリを開発したい時~
- ラズパイ上にGoやvscode-serverを入れて手元PCからつないでターゲット上でコンパイルして開発
- PC上で開発しておいてGOOS=linuxでクロスビルドしたものをターゲットに投入
- I/O周りをサーバー実装にまとめて実装してアプリを別プロセスとし、アプリ開発はPCとラズパイ間をLANでつないだ状態で開発、リリース時には双方ラズパイ上で動作させる
1.や2.はアプリケーション規模が小さいまたはラズパイのI/Oに依存しないアプリ向け
個人的には最近3.の手法を採ってることが多いです。
遭遇しがちな問題
- ラズパイのストレージ性能が不足してコンパイルが遅い
- ラズパイ上にビルドツールチェインを入れてしまうとストレージ占有量がかさむ(GoだけならまだしもCGOが絡むと大幅に必要)
- vscode-serverはメモリを大量に使うし遅い
- CGO実装が絡むと本体ビルドは遅いし、途端にPC側クロスビルドが難しくなる
- 32bit版OSを使うと意外な躓きがある(構造体アライメントなど)ので素直にもう64bit版OSを使いましょう
難しいクロスビルドにはDockerを使おう
別途qemuをインストールしてエミュレーション実行する記事をちょくちょく見ますが、Dockerにはマルチアーキテクチャをサポートする機能があり、公式イメージの多くはマルチアーキテクチャとしてイメージが公開済みです。こちらを使うと手早く指定のアーキテクチャの動作環境をDockerで構築することができます。
DockerfileにてWORKDIRを指定してそこにターゲットに配置予定のファイル構造を構築するレシピを記述
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
(マルチアーキテクチャサポートのインストール) - docker build --rm --platform=linux/arm64 -t localhost/app .
- docker run --rm --platform=linux/arm64 localhost/app tar czf - . > bin/app.tar.gz
マルチアーキテクチャサポートのインストールをすることで、任意のCPUアーキテクチャ用イメージをエミュレーション動作させることができるようになります。
この場合、ラズパイと同じアーキテクチャで動作するバイナリをGoを使ってビルドすることができ、もちろん、特有のI/Oに依存していない実装であれば実行することもできます。また、Goでないアプリもこの手法でPC上でビルドできます。(C拡張を含むPythonアプリケーションなど)
ラズパイの足回り(I/O)対応ライブラリ
ラズパイ向けGPIOやSPI、I2C、PWMは以下のライブラリが便利でした。
github.com/stianeikeland/go-rpio/v4
ただし、以下のカーネル起動パラメータiomem=relaxed
の指定が必要でした。
modules=loop,squashfs,sd-mod,usb-storage quiet console=tty1 iomem=relaxed
昨今のLinuxカーネルのデフォルト設定がよりセキュアに調整されていて、この指定が無い場合、権限エラーになります。
JSON-RPCによるI/Oとアプリの分離
JSON-RPCサーバーとラズパイのI/OをGoで構築します。
RPCライブラリは以下のいずれかの利用パターンがありました。
- 純正の"net/rpc/jsonrpc"(v1 via socket)
- https://github.com/nobonobo/jsonrpc2 (v2 via http or tcp)
- https://github.com/semrush/zenrpc (v2 via http or websocket)
I/OサーバーもアプリもGoで書く場合、純正が楽です。アプリをその他の言語処理系で書く場合はHTTP経由のものを使うと良いです。ブラウザベースアプリの場合はwebsocket経由ですね。
OSにAlpineのススメ
- alpinelinuxはラズパイ用もメンテされ続けている
- Docker内で動かすのが容易でありPCでのビルドと実行環境が同じアーキテクチャ&OSになる
- デフォルトでディスクレスモード採用なのでSDカードの寿命問題が大幅に改善する
- 唯一の欠点はMIPIカメラ関連のパッケージがメンテされていないこと
- 下記のツールでインストールも簡単
ここではAlpineとDockerでクロス開発することを推奨していますが、もちろんMIPIカメラ利用のためにRasPi-OSを採用する場合でもPC-Docker側でベースになっているDebianを使ってクロス開発することは可能です。
AlpineとDebianの違い
- システムデーモン管理がOpenRC(Alpine)かSystemd(Debian)と異なる
- 時刻同期がChrony(Alpine)かNtpd(Debian)と異なる
- libc依存がAlpineではmusl依存
Dockerデーモンのセットアップ
ラズパイ側もDockerを動かせるようにセットアップすると、よりアプリのデプロイが容易になります。
が、セットアップ時の注意点があります。
- ラズパイブートにはFAT32のプライマリパーティションが必須
- FAT32だといろいろと動かないものが多くDockerもその一つ
- セカンダリパーティションにEXT4などを置くのが良い(Debianのセットアップ時はそうなっている)
- ただ、このパーティションへの書き込み頻度はストレージ寿命に響くので注意
ストレージ寿命について
- PCのように頻繁ににストレージに書き込み続ける状態での運用では半年~1年くらいでストレージ不良を起こす(SDカードやeMMCなど)
- 高耐久型SDカード(ドラレコ用)などを使うか、書き込み量を極力少なくしないといけない
- 「Alpine+ディスクレスモード」か「RasPi-OSのリードオンリーモード」が推奨されます
- ただし、双方ともにOSに対する変更を保存するのにひと工夫が必要になります
サービスとしての登録
I/Oサーバーとアプリを以下のスクリプトを用意することで自動起動することができます。
OpenRCの場合
#!/sbin/openrc-run
name=$RC_SVCNAME
command="/root/app"
command_args=""
pidfile="/run/$RC_SVCNAME.pid"
command_background="yes"
depend() {
need net
}
chmod a+x /etc/init.d/app # 実行可能権限付与
rc-service app start # サービス開始
rc-service app stop # サービス停止
rc-update add app # サービス自動開始設定
Systemdの場合
[Unit]
Description=Application Server
After=network.target
[Service]
ExecStart=/root/app
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemctl daemon-reload # サービス設定群再読み込み
systemctl start app # サービス開始
systemctl stop app # サービス停止
systemctl enable app # サービス自動開始設定
まとめ
- I/Oサービスとアプリの分離によりアプリ開発をPC上に持ってくることができ、
- アプリ開発のターンアラウンドタイムを向上できます
- I/Oサービスのモックを作るとテスト/CIを仮想マシン上で行うこともできます
- AlpineのディスクレスモードやRasPi-OSのリードオンリーモードはストレージの寿命にやさしい
- 以上で堅牢なラズパイアプリを長く運用可能にできます
Discussion