Ein Raspberry Pi eignet sich ideal für Server oder Desktop Anwendungen. Das Betriebssystem ist schnell und einfach installiert und diverse Dienste oder Anwendungen lassen sich einfach nach installieren. Ist die Anwendung jedoch komplexer oder müssen mehrere Instanzen erstellt werden, kann es schnell vorbei sein mit der Benutzerfreundlichkeit. Um dieses Problem zu lösen, gibt es mehrere Ansätze. Einer davon ist selber ein Raspberry Pi Image erstellen mithilfe von Yocto.
Dieser Ansatz verspricht ein kleinstes mögliches Image sowie eine einfache Wartbarkeit der Konfiguration. Als Bonuspunkt kann das Projekt einfach auf eine benutzerspezifische Hardware portiert werden.
Buildumgebung Docker
Das Yocto Projekt, welches benötigt wird, um ein eigenes Raspberry Pi Image erstellen zu können benötigt eine Linux Umgebung, in unserem Fall Docker mit WSL. Hier ein Dockerfile welches ein entsprechendes Dockerimage erstellen kann das alle nötigen Anwendungen enthält:
FROM ubuntu:22.04 # Relax retries and timeout to prevent errors RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries; \ echo 'Acquire::http::Timeout "20";' > /etc/apt/apt.conf.d/99-timeout-http; \ echo 'Acquire::ftp::Timeout "20";' > /etc/apt/apt.conf.d/99-timeout-ftp; # Update and upgrade existing RUN apt-get update; \ apt-get upgrade -y; # Setup locales RUN apt-get update; \ apt-get -y install locales apt-utils sudo && dpkg-reconfigure locales && locale-gen en_US.UTF-8 && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 ENV LANG=en_US.UTF-8 # Setup timezone ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Europe/Zurich RUN apt-get update; \ apt-get install -y tzdata; \ ln -fs /usr/share/zoneinfo/Europe/Zurich /etc/localtime; \ dpkg-reconfigure --frontend noninteractive tzdata; # Setup user ENV USERNAME=yoctouser ENV USERGROUP=users RUN groupadd users;\ useradd -ms /bin/bash -G sudo -d /home/${USERNAME} -g ${USERGROUP} -r ${USERNAME}; \ passwd -d ${USERNAME} RUN passwd -d root USER ${USERNAME} # Install packages needed to build yocto (from https://docs.yoctoproject.org/ref-manual/system-requirements.html), dos2unix to simplify maintaining packages.txt COPY packages.txt / RUN sudo apt-get update; \ sudo apt-get install -y dos2unix; \ sudo dos2unix /packages.txt; \ sudo apt-get install -y $(grep -vE "^\s*#" /packages.txt | tr "\n" " "); # Setup manifest repo ENV MANIFEST_DIR=/manifest RUN sudo install -o ${USERNAME} -g ${USERGROUP} -d ${MANIFEST_DIR}; WORKDIR ${MANIFEST_DIR} RUN git init; \ git config --global user.email "you@example.com"; \ git config --global user.name "you"; COPY default.xml ${MANIFEST_DIR}/ RUN git add *; \ git commit -m "Added reopsitory"; WORKDIR / # Setup project dir ENV PROJECTDIR=/home/${USERNAME}/yoctodir RUN sudo install -o ${USERNAME} -g ${USERGROUP} -d ${PROJECTDIR}; WORKDIR ${PROJECTDIR} RUN repo init -u file://${MANIFEST_DIR}/ -b master < /dev/null; \ repo sync -j8; # Setup build dir ENV BUILDDIR=/home/${USERNAME}/builddir RUN sudo install -o ${USERNAME} -g ${USERGROUP} -d ${BUILDDIR}; WORKDIR ${BUILDDIR} COPY layers.conf /layers.conf COPY localconf_append.conf /localconf_append.conf RUN sudo dos2unix /layers.conf; \ sudo dos2unix /localconf_append.conf; \ sudo sed -i -e "s,##PROJECTDIR##,${PROJECTDIR},g" /layers.conf; COPY setup_env_script.sh setup_env_script.sh RUN sudo chmod +x setup_env_script.sh; \ sed -i -e "s,##PROJECTDIR##,${PROJECTDIR},g" setup_env_script.sh; \ sed -i -e "s,##LOCALCONFFILE##,/localconf_append.conf,g" setup_env_script.sh; \ sed -i -e "s,##BBLAYERFILE##,/layers.conf,g" setup_env_script.sh; WORKDIR ${BUILDDIR} ENTRYPOINT ["/bin/bash"]
Damit die nötigen Anwendungen einfach gepflegt und erweitert werden können sind diese in einem separaten File aufgelistet:
# Additional tools (may conatains dublicates) git nano curl repo # Official yocto packages for yocto (https://docs.yoctoproject.org/ref-manual/system-requirements.html) gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev python3-subunit mesa-common-dev zstd liblz4-tool file locales libacl1 curl python-is-python3 nano
Projekt Setup
Um ein Raspberry Pi Image zu erstellen, muss der entsprechende Raspberrypi-Yocto-Layer inklusive seiner Abhängigkeiten in das Projekt eingebunden werden. Um diese Schritte zu vereinfachen, werden alle benötigten Repositories mithilfe des repo-tools beim Erstellen des Images geholt. Folgende Konfigurationsdatei kann hierzu verwendet werden:
<?xml version="1.0" encoding="UTF-8"?> <manifest> <default sync-j="8" revision="kirkstone"/> <remote name="oe" fetch="https://git.openembedded.org"/> <remote name="yocto" fetch="https://git.yoctoproject.org"/> <project name="meta-security.git" path="meta-security" remote="yocto" revision="kirkstone"/> <project name="meta-raspberrypi" path="meta-raspberrypi" remote="yocto" revision="kirkstone"/> <project name="meta-openembedded" path="meta-openembedded" remote="oe" revision="kirkstone"/> <project name="poky.git" path="poky" remote="yocto" revision="kirkstone"/> </manifest>
Weiter wird direkt mit dem Image ein Build Verzeichnis erstellt, welches ein Setupskript enthält.
#!/bin/bash if [ "${BASH_SOURCE}" = "${0}" ]; then printf "\\n[ERROR]: Use this script with source\\n\\n" else PROJECTDIR="##PROJECTDIR##" BBLAYERFILE="##BBLAYERFILE##" LOCALCONFFILE="##LOCALCONFFILE##" BBLAYERSEXIST="1" CONFEXIST="1" if [ ! -f "conf/bblayers.conf" ]; then LAYERSEXIST="0" fi if [ ! -f "conf/local.conf" ]; then CONFEXIST="0" fi cd $(dirname ${BASH_SOURCE}) . ${PROJECTDIR}/poky/oe-init-build-env . if [ "$LAYERSEXIST" == "0" ]; then bitbake-layers add-layer $(grep -vE "^\s*#" ${BBLAYERFILE} | tr "\n" " "); fi if [ "$CONFEXIST" == "0" ]; then cat $LOCALCONFFILE >> conf/local.conf fi unset LAYERSEXIST unset CONFEXIST unset LAYERFILE unset LOCALCONFFILE unset PROJECTDIR fi
Dieses kann verwendet werden, um den Yocto Build zu initialisieren sowie nach jedem Neustart des Containers die Umgebung zu laden. Mithilfe des layers.conf sind die zu verwendenden Layer einfach zu definieren.
##PROJECTDIR##/meta-openembedded/meta-oe/ ##PROJECTDIR##/meta-openembedded/meta-python/ ##PROJECTDIR##/meta-openembedded/meta-multimedia/ ##PROJECTDIR##/meta-openembedded/meta-networking/ ##PROJECTDIR##/meta-raspberrypi/
Weiter kann mithilfe des localconf_append.conf das local.conf einfach erweitert werden.
# Extension of locale.conf file to configure project MACHINE = "raspberrypi4"
Container erstellen
Mithilfe des oben beschriebenen Aufbaus kann aus der Kommandozeile mit
docker build --tag ‚yoctoimage‘ .
ein entsprechendes Image erstellt werden. Ein interaktiver Container daraus wird mit
docker run -it yoctoimage
erzeugt.
Image erstellen und auf die SD-Karte laden
Läuft der erstellte Container kann das Build Projekt erstellt werden, indem das bereitgestellte setup Skript gesourced wird:
cd ${BUILDDIR}
source setup_env_script.sh
Nun steht das Build Projekt bereit und es kann zum Beispiel mit
bitbake core-image-minimal
ein minimal Image erstellt werden. Dies kann je nach Leistung des Rechners einige Zeit in Anspruch nehmen. Ebenfalls benötig der Build mindestens 50GB an Festplattenspeicher. Das erstellte Image muss nun extrahiert werden.
bzip2 -d -f ${BUILDDIR}/tmp/deploy/images/raspberrypi4/core-image-minimal-raspberrypi4.wic.bz2
Die Windows PowerShell eignet sich um das extrahierte Image aus dem Container auf das Hostsystem zu kopieren um es danach mithilfe von rufus auf eine SD-Karte zu flashen.
docker cp <Docker Container Id>:/home/yoctouser/builddir/tmp/deploy/images/raspberrypi4/core-image-minimal-raspberrypi4.wic .
Nun kann der Raspberry Pi mit dem selbst erstellten Image gestartet werden.
Abschliessender Kommentar
Die beschriebenen Konfigurationen und Schritte ermöglichen es ein minimales Image für den Raspberry Pi zu erstellen. Nun liegt es am Benutzer das Image zu erweitern und eventuell zusätzliche oder eigene Layer im Yoctoprojekt einzufügen. Diese können auch direkt im default.xml sowie layers.conf erweitert werden damit diese automatisch mit erstellt werden beim Generieren des Images. Weiter kann es sich lohnen das download Verzeichnis als Volume in den Container zu mounten damit man die Daten bei mehreren Projekten nur einmal herunterladen muss. Zum Schluss wurde festgestellt, dass der Download des Rezeptes linux-raspberrypi fehlschlagen kann. Dies kann umgangen werden indem das Rezept einzeln mit
bitbake -c fetch linux-raspberrypi
heruntergeladen wird und anschliessend nochmals das Image erstellt wird mit
bitbake core-image-minimal
Dass ganze Projekt kann als ZIP-File hier herunter geladen werden.