Im folgenden Artikel wird beschrieben wie man Raspberries per netboot über das lokale Netz booten und betreiben kann. Sowohl die Bootpartition als auch die Rootpartition liegen dann nicht mehr auf einer SD karte sondern im Netz auf einem nfs Server. Beschrieben wird wie man dazu entweder eine Raspberry als DHCP Server, der als DHCP proxy agiert, nutzen kann oder auch wie man eine Synology auf der DSM7 aktiv ist, den dort enthaltenen dnsmasq DHCP Server konfigurieren muss, damit er als DHCP Proxy agiert. Ein DHCP Proxy ist immer dann notwendig wenn man einen Router betreibt, der IP Addressen im lokalen Netz verteilt aber kein netboot unterstützt. Das trifft z.B. beim mir mit meiner Fritzbox 7590 zu. Allerdings kann man auch die DHCP Funktion der Fritzbox deaktivieren und eine Raspberry oder die Synology die Funktion übernehmen lassen. Ich möchte aber dass ich in meiner Fritzbox alle Geräte über das Webfrontend sehen kann. Speziell auch an welchen Repeatern sich die Geräte verbunden haben (Fritzbox Mesh Funktion).
Einsatz einer Raspberry als DHCP-Proxy Server und tftp Server
Ab der Raspberry 3 kann die Raspberry auch ohne SD Karte starten und laufen. Ihr Bootcode sucht sie im lokalen Netz von einem netboot Server und lädt dann davon das Rootfilesystem. Das ist sehr praktisch wenn man z.B. eine größere Menge von Raspberries alle mit demselben Image starten lassen will wie z.B. Raspberry Schulungen. Weiterhin kann man aber auch jeder Raspberry im lokalen Netz unterschiedliche Images geben. Der Vorteil ist dass es keine SD Karte mehr gibt die sich abnutzt und man zentral Backups der Images erstellen kann. Nachteil ist dass saemtliche Aenderungen der Daten der Rootpartition ueber das Netz gehen muessen und dadurch die Performance etwas leidet.
Den DHCP Server sowie den tftp Server den man dazu benötigt kann man auch gut auf eine Raspberry legen. Ich habe so einen Testsetup bei mir lokal mal interessehalber aufgesetzt und primaer dazu diese Anleitung von Raspberry Pi Geek benutzt.
In der Anleitung wird als Unterordner in /nfs und /tftp die IP Adresse genommen. Ich habe dazu aber die CPU ID genommen, die man mit grep -i Serial /proc/cpuinfo erhält. Dann davon die letzten 8 Stellen nehmen. Alternativ kann man natuerlich auch die IP Adressen nehmen. Nur muss man dann darauf achten dass die Clients von der FB immer dieselbe IP Adresse bekommen. Wenn man eine groessere Anzahl von PXE Clients in einem kleinen Netz hat (/24 = 254 IPs) ist das aber ungeschickt da dann alle IPs immer reserviert sind und von anderen Clients nicht mehr benutzt werden koennen.
Wichtig ist die Option tftp-unique-root in /etc/dnsmasq.conf. Dadurch werden beim PXE Boot in /tftpboot/<serialid> die Bootinformationen incl /etc/cmdline.txt genutzt wo konfiguriert ist wo der netboot Client sein Rootverzeichnis bezieht. Aus nicht verstaendlichen Gruenden muss auch ein Teil des Bootverzeichnisses auch in /tftpboot stehen.
Das /tftpboot Verzeichnis sieht dann wie folgt aus (87f728f5 ist die CPU ID meiner Raspberry)
├── 87f728f5
├── bootcode.bin
Per nfs ist das root Verzeichnis der Raspberry exposed. Letztendlich kann das jedes Verzeichnis sein denn dessen Pfad wird in der /tftpboot/87f728f5/etc/cmdline.txt definiert. Aus Konsistenzgründen sollte es aber /nfs/87f728f5 sein.
Etwas problematisch ist es mit der Bootpartition die ja in einem tftp Verzeichnis auf der Raspi liegt. Damit ein Update der Bootpartition des netboot Client Raspberries auf dem tftp Server stattfindet sollte man als Bootverzeichnis in der /etc/fstab der netboot Clients das tftp Bootverzeichnis definieren und das tftp Verzeichnis per nfs exportieren. Damit erfolgt dann ein Update des tftp Bootverzeichnisses transparent beim Update den Clients.
Inhalte der /etc/fstab eines netboot Clients mit der serverid 87f728f5 in /volume1/pxe_nfs/87f728f5/etc/fstab:
proc /proc proc defaults 0 0
<tftpserverIP>:/tftpboot/87f728f5 /boot nfs defaults,vers=3 0 0
Inhalt der /etc/cmdline.txt eines netboot Clients mit der serverid 87f728f5 auf dem tftp Server in /tftpboot/87f728f5
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=<synologyIP>:/volume1/pxe_nfs/87f728f5,vers=3 rw ip=dhcp elevator=deadline fsck.repair=yes rootwait
Einsatz einer Synology als DHCP und tftp Server
Leider ist der tftp Server der Synology unzuverlässig und mit DSM 7.3.1 habe ich es nicht mehr hinbekommen den DNSMASQ Server der Synology zu nutzen um die Raspberries über Netz zu booten. Mit einer Raspberry als dhcp Proxy Server - beschrieben weiter unten - funktioniert es aber zuverlässig.
Auf einer Synology kann man mit DSM Standardmitteln keinen dhcp-proxy konfigurieren. D.h. man muss dann einen DHCP Server auf der Syno konfigurieren und den DHCP Server auf der FB deaktivieren. Aber man kann dem tftp Server den Servicenamen Raspberry Pi Bootkonfigurieren der von der Raspberry verlangt wird. Man muss dazu im DHCP Server der Syno eine Vendordefinition wie folgt vornehmen:

Ansonsten konfiguriert man alles genauso wie im ersten Fall mit der Raspberry konfiguriert. Nur eben dass nicht nur die Rootpartitionen per nfs von der Syno exportiert werden sondern auch die tftp Verzeichnisse.
tree -L 2 /volume1
├── pxe_nfs
│ ├── 7f308a52
│ ├── 87f728f5
│ └── @eaDir
├── pxe_tftp
│ ├── 7f308a52
│ ├── 87f728f5
│ ├── bootcode.bin
Synology DHCP Server als DHCP Proxy konfigurieren und Synology tftp Server benutzen
Man kann auch den Syno DHCP Server als DHCP Proxy konfigurieren. Dann hat der Syno DHCP Server nur die netboot Funktion aber die eigentliche DHCP Funktion bleibt weiterhin dem lokalen DHCP Server wie z.B. einer Fritzbox überlassen. Dazu muss man in /etc/dhcpd/dhcpd.proxy.conf bei DSM6 folgendes eintragen:
interface=eth0 dhcp-range=set:eth00,192.168.0.0,proxy dhcp-option=vendor:PXEClient,43,Raspberry Pi Boot dhcp-leasefile=/etc/dhcpd/dhcpd.conf.leases dhcp-script=/usr/share/dhcpd/dhcpd-script.sh
Bei DSM7 muss man folgendes in /etc/dhcpd/dhcpd.proxy.conf eintragen:
interface=eth0
dhcp-range=set:eth00,192.168.0.255,proxy
# Dont serve clients which are not listed in host list
dhcp-ignore=tag:!known
# Macs of Raspberries to serve
dhcp-host=b8:27:eb:f7:28:f5
dhcp-boot=tag:pxe,bootcode.bin
dhcp-vendorclass=set:pxe,PXEClient
dhcp-option=vendor:PXEClient,43,Raspberry Pi Boot
pxe-service=0,"Raspberry Pi Boot"
dhcp-leasefile=/etc/dhcpd/dhcpd.conf.leases
#log-dhcp
Danach ist ein Restart des DHCP Servers notwendig.
Folgendes Script hilft dabei:
#!/bin/bash
cp /etc/dhcpd/dhcpd.proxy.conf /etc/dhcpd/dhcpd.conf
OLD_PID="$(ps -e ww | grep dnsmasq | grep -v grep | head -n 1 | awk ' { print $1 }' )"
COMMAND="$(ps --no-header -p $OLD_PID -o args)"
kill $OLD_PID
$COMMAND &
Dummerweise wird die DHCP Konfiguration immer bei jeglichen DHCP Änderungen per DSM sowie bei einem Syno Restart wieder überschrieben. Ein Workaround ist auf der Syno ein triggered Task beim Booten ausfuehren lassen der das Script ausführt.
Alternativ kann man auch ohne DHCP Proxy auskommen wenn man dem DHCP Server einen IP Adressbereich gibt der nicht vom Haupt DHCP Server genutzt wird. Also z.B. die IPs 100-200 vom Haupt DHCP Server nutzen laesst und in der Syno die IPs 245-250 und nur die MAC Adressen der Raspberries auf im Syno DHCP zuläßt.
RaspberryPi DHCP Server als DHCP Proxy konfigurieren und Synology nfs Server benutzen
Leider ist der tftp Server der Synology unzuverlässig und mit DSM 7.3.1 habe ich es nicht mehr hinbekommen den DNSMASQ Server der Synology zu nutzen um die Raspberries über Netz zu booten.
Deshalb habe ich einen DHCP Proxy Server auf einer Raspberry aufgesetzt. Der erledigt das initiale DHCP Bootprotokoll für die Raspberries so dass sie Booten. Dabei nutzen sie aber die Synology für ihr Rootfilesystem. D.h. der DHCP Proxy Server enthält nur die Bootdateien. Damit sichergestellt ist, dass diese auch bei einem Systemupdate geändert werden, mounten die Raspberries /boot/firmware von dem DHCP Proxy Server.
Nach dem Start wird die Rootpartition von der Synology genutzt und die Bootparition von dem DHCP Proxy Server:
<synology>:/volume1/rpi_nfs/7f308a52 on / type nfs
<dhcpproxy>:/tftpboot/7f308a52 on /boot/firmware type nfs
Wichtig ist die richtige Bereitstellung der Boot- und Rootdateien auf dem dhcp Proy sowie der Synology:
Das tftp Verzeichnis des dhcp Proxies muss wie folgt aussehen:
├── 7f308a52
│ ├── bcm2710-rpi-2-b.dtb
│ ├── bcm2710-rpi-3-b.dtb
│ ├── bcm2710-rpi-3-b-plus.dtb
│ ├── bcm2710-rpi-cm0.dtb
│ ├── bcm2710-rpi-cm3.dtb
│ ├── bcm2710-rpi-zero-2.dtb
│ ├── bcm2710-rpi-zero-2-w.dtb
│ ├── bcm2711-rpi-400.dtb
│ ├── bcm2711-rpi-4-b.dtb
│ ├── bcm2711-rpi-cm4.dtb
│ ├── bcm2711-rpi-cm4-io.dtb
│ ├── bcm2711-rpi-cm4s.dtb
│ ├── bcm2712d0-rpi-5-b.dtb
│ ├── bcm2712-d-rpi-5-b.dtb
│ ├── bcm2712-rpi-500.dtb
│ ├── bcm2712-rpi-5-b.dtb
│ ├── bcm2712-rpi-cm5-cm4io.dtb
│ ├── bcm2712-rpi-cm5-cm5io.dtb
│ ├── bcm2712-rpi-cm5l-cm4io.dtb
│ ├── bcm2712-rpi-cm5l-cm5io.dtb
│ ├── bootcode.bin
│ ├── cmdline.txt
│ ├── config.txt
│ ├── fixup4cd.dat
│ ├── fixup4.dat
│ ├── fixup4db.dat
│ ├── fixup4x.dat
│ ├── fixup_cd.dat
│ ├── fixup.dat
│ ├── fixup_db.dat
│ ├── fixup_x.dat
│ ├── initramfs_2712
│ ├── initramfs8
│ ├── issue.txt
│ ├── kernel_2712.img
│ ├── kernel8.img
│ ├── LICENCE.broadcom
│ ├── meta-data
│ ├── network-config
│ ├── overlays
│ ├── start4cd.elf
│ ├── start4db.elf
│ ├── start4.elf
│ ├── start4x.elf
│ ├── start_cd.elf
│ ├── start_db.elf
│ ├── start.elf
│ ├── start_x.elf
│ └── user-data
└── bootcode.bin
Dabei ist der Verzeichnisname immer die Seriennummer der Raspberries aus /proc/cpuinfo. Im Rootverzeichnis muss noch einmal bootcode.bin kopiert werden. Die cmdline.txt muss wie folgt geändert werden:
console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=<synology>:/volume1/rpi_nfs/7f308a52,vers=3 rw ip=dhcp rootwait
Dabei ist rpi_nfs der per nfs exportierte Pfad auf der Synology und entsprechend anzupassen.
Das Rootverzeichnis auf der Synology muss wie folgt aussehen:
── 7f308a52
│ ├── bin -> usr/bin
│ ├── boot
│ ├── dev
│ ├── etc
│ ├── home
│ ├── lib -> usr/lib
│ ├── lost+found
│ ├── media
│ ├── mnt
│ ├── opt
│ ├── proc
│ ├── root
│ ├── run
│ ├── sbin -> usr/sbin
│ ├── srv
│ ├── sys
│ ├── tmp
│ ├── usr
│ └── var
Die Datei /etc/fstab muss noch wie folgt geändert werden:
proc /proc proc defaults 0 0
#PARTUUID=7ab614b5-01 /boot/firmware vfat defaults 0 2
#PARTUUID=7ab614b5-02 / ext4 defaults,noatime 0 1<dhcpproxy>:/tftpboot/7f308a52 /boot/firmware/ nfs defaults,vers=3 0 0
Dabei ist /tftpboot der per nfs exportierte Pfad der Bootdateien auf dem dhcp Proxy Server.
Damit sind die notwendigen und entsprechend geänderten Dateien bereitgestellt.
Zum Schluss ist noch der dhcp Proxy Server zu konfigurieren. Die /etc/dnsmasq.d/rpi-netboot.conf muss wie folgt aussehen:
port=0
# ip range used
dhcp-range=192.168.0.255,proxy
log-dhcp
# use local tftp
tftp-root=/tftpboot
enable-tftp
pxe-service=0,"Raspberry Pi Boot"
Wichtig ist dass /volume1/rpi_nfs auf der Synology sowie /tftproot auf dem dhcp Proxy Server per nfs freigegeben werden.
Einsatz einer Synology bei älteren Raspberries die kein netboot Boot können
In diesem Falle benötigt man immer noch eine SD Karte. Sie wird aber fast nur gelesen und nutzt sich deshalb so gut wie nicht ab. Sie wird nur benötigt damit die Raspberry booten kann und sich dann das /boot wie auch das Root Verzeichnis per nfs von der Synology mounted. Die /boot/cmdline.txt der SD Karte sieht exakt genauso aus wie die beim PXE Boot: Es muss die Rootpartition per nfs eingebunden werden. Die /etc/fstab von der Synology muss ebenso genauso aussehen wie die beim netboot: /boot wird von der Synology als /volume1/pxe_tftp/<systemid> eingebunden. An eine Sache muss man aber denken: Wenn es mal ein Update der /boot Partition gibt wird dieser Update auf der Synology gemacht damit dort das ganze Image gesichert werden kann. Man muss aber immer danach alles aus dem /boot Ordner noch einmal lokal auf die SD Karte kopieren da sonst beim Booten der Raspberry nicht der aktuelle /boot Code angezogen wird.
Hinweis
Irgendwann stellte ich zu meiner Verwunderung fest dass netboot meiner Raspberries nicht mehr funktioniert. Sie luden den Bootcode per tftp aber bekamen keine Rootpartition per nfs. Letztendlich stellte sich heraus dass es daran lag dass ich in der Zwischenzeit NFS 4 Support auf der Syno enabled hatte und nfs3 in den Config Dateien wie /etc/fstab stand. Nachdem ich den Support für NFS4 wieder deaktiviert hatte funktionierte wieder alles wie gewohnt.
Update: Obige Aussage trifft für DSM6 zu. Mittlerweile habe ich auf DSM7 upgraded und netboot der Raspberries funktioniert auch mit NFS 4.1. vermutlich hätte es bei DSM6 auch gereicht im Client nfs 4.1 in der /etc/fstab zu definieren.
Nützlich Befehle zum Debuggen
Auf einem System die DHCP-DISCOVER Antworten lesen und auf den netboot System ein DHCP-DICSCOVER Request losschicken.
sudo tcpdump -vvveni eth0 portrange 67-68
sudo nmap --script broadcast-dhcp-discover 255.255.255.255 -p67
Referenzen
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net_tutorial.md
https://stackoverflow.com/questions/40008276/dnsmasq-different-tftp-root-for-each-macaddress
https://robferguson.org/blog/2022/04/15/how-to-pxe-boot-your-rpi/
https://warmestrobot.com/blog/2021/6/21/raspberry-pi-network-boot-guide
