OpenWrt + dnsmasq + raspberry pi 4B でstaging/productionの環境を分離するPXEブートを行った

OpenWrt + dnsmasq + Raspberry Pi 4B PXE ブート調査

OpenWrt 上の dnsmasq を用いて Raspberry Pi 4B を タグベースで PXE 起動させる。

このメモの 192.0.2.100 は説明用の documentation address である。homecluster-infra から OpenWrt 実機へ反映する場合は、外部 inventory の openwrt_lan_ipaddropenwrt_dhcp_ntp_serversopenwrt_gentoo_server_host を実値として明示し、documentation range が dnsmasq / TFTP / NFS endpoint に残っていれば反映前に止める。

最終的に目指した状態:

dhcp-boot=tag:rpi4-02,start4.elf,192.0.2.100,192.0.2.100

もしくはシンプルに:

dhcp-boot=start4.elf,192.0.2.100

と同等の挙動をタグ条件付きで再現する。

最初の症状

config host で set:rpi4-02 が出力されている dhcp-boot=tag:rpi4-02,… を設定 しかし PXE 起動しない タグ無しの dhcp-boot=start4.elf,192.0.2.100 なら起動する ここから調査開始。

次の内容で検証した

openwrtでは/etc/config/dhcpで設定ができる 次のコマンドでトライアンドエラーを繰り返した

vi /etc/config/dhcp && /etc/init.d/dnsmasq restart && cat /var/etc/dnsmasq.conf.*

Raspberry Pi の PXE 挙動の理解

dnsmasq ログより:

vendor class: PXEClient:Arch:00000:UNDI:002001

これは DHCP Option 60(Vendor Class)。

意味:

フィールド意味
PXEClientPXE クライアント
Arch:00000BIOS 互換
UNDI:002001PXE ドライババージョン

重要点: Raspberry Pi の PXE は BOOTP フェーズと通常 DHCP フェーズがある このとき:

  • host の set: タグが BOOTP フェーズで適用されない可能性がある
  • そのため tag:rpi4-02 が効かないケースがある

dnsmasq の -M / dhcp-match の理解

dnsmasqの公式man

-M, --dhcp-vendorclass=set:<tag>,<vendorclass>

OpenWrt UCI では:

list dhcp_match 'set:pxe,60,PXEClient'

これは:

dhcp-match=set:pxe,60,PXEClient

意味:

VendorClass に PXEClient を含むクライアントに pxe タグを付与 しかし今回は host タグでの制御を目指した。

config boot の正しい理解

最重要ポイント。 OpenWrt では:

config boot
    option filename 'start4.elf'
    option networkid 'rpi4-02'
    option serveraddress '192.0.2.100'
    option servername '192.0.2.100'

dhcp-boot=tag:rpi4-02,start4.elf,192.0.2.100,192.0.2.100

に変換される。

落とし穴 ① serveraddress / servername は両方必須

OpenWrt ドキュメント: https://openwrt.org/docs/guide-user/base-system/dhcp#booting_options

このセクションは dnsmasq の -M オプション構築について説明しているが、 config boot のパース仕様が明確に書かれていない。

実験で判明:

  • serveraddress だけでは出力されない
  • servername だけでも出力されない
  • 両方必要

⚠️ エラーは出ない(静かに無視されdnsmasq向けの設定ファイルには書き出されない)

これは OpenWrt の UCI → dnsmasq 変換の仕様。

落とし穴 ② servername にホスト名を使うと失敗する可能性

dnsmasq の構文は:

dhcp-boot=<filename>,<servername>,<serveraddress>

Raspberry Pi PXE クライアントは:

  • servername を DNS 解決する保証がない
  • 名前解決失敗時に TFTP 失敗する可能性がある

実験の結果 servername に IP を指定することで安定することがわかった。 つまり

option servername '192.0.2.100'
option serveraddress '192.0.2.100'

が最も確実。

最終的に動作した設定

config host
    option name 'rpi4-02'
    option mac 'DC:A6:32:B4:85:A8'
    option ip '192.0.2.253'
    option networkid 'rpi4-02'

config boot
    option filename 'start4.elf'
    option networkid 'rpi4-02'
    option serveraddress '192.0.2.100'
    option servername '192.0.2.100'

生成される dnsmasq 設定:

config host
    option name 'rpi4-02'
    option mac 'DC:A6:32:B4:85:A8'
    option ip '192.0.2.253'
    option networkid 'rpi4-02'

config boot
    option filename 'start4.elf'
    option networkid 'rpi4-02'
    option serveraddress '192.0.2.100'
    option servername '192.0.2.100'

生成すると

dhcp-host=...,set:rpi4-02,...
dhcp-boot=tag:rpi4-02,start4.elf,192.0.2.100,192.0.2.100

これでタグ付き起動成功。

なぜ dhcp-boot=start4.elf,192.0.2.100 は常に動いたか?

これは tag 条件が無いためBOOTP フェーズでも必ず適用された。

TFTP ログの解釈

error 0 Early terminate received
file ... not found

これは:

  • Raspberry Pi が複数回リトライする
  • オプションファイルを探しに来る
  • 必須でないファイルも探す

正常範囲。

設計上の考察

今回得られた知見:

  • OpenWrt の config boot は動作する
  • しかし両方のフィールド必須
  • servername は IP にするのが安全
  • Raspberry Pi PXE は BOOTP と DHCP が分離
  • タグ付与タイミングに注意

ベストプラクティス(今回の環境)

  • PXE ブートファイルは共通化可能
  • stage / overlay 制御は DHCP Option 224 で渡す
  • Linux 側で処理する方がデバッグしやすい

追加の知見(運用で詰まった点)

dhcp-broadcast が tag 条件付きの場合の落とし穴

OpenWrt の dnsmasq 生成で以下のような行が出ている場合:

dhcp-broadcast=tag:needs-broadcast

このとき needs-broadcast タグが付いていない端末には ユニキャスト応答が返る。 Raspberry Pi 側がユニキャストの OFFER を受理しない場合、DHCPREQUEST が出ずに止まる。 回避策は needs-broadcast タグを付与すること。

config host の bootfile が dnsmasq.conf に反映されないケース

UCI で option bootfile 'rpi4-stg/start4.elf' を設定しても、 /var/etc/dnsmasq.conf.*dhcp-host=... 行に bootfile が出力されないことがある。 この場合、dhcp-boot=tag:...config boot で明示する方が確実。

IP 再利用が効かない件

検証中に IP を使い回すと反映されないことがあった。 /tmp/dhcp.leases/tmp/hosts/dhcp.cfg* が原因の可能性があるので、以下でリセットできる。

rm -f /tmp/dhcp.leases /tmp/hosts/dhcp.cfg*
/etc/init.d/dnsmasq restart

参考