Einführung
Nomad von HashiCorp ist ein leichtgewichtiges Orchestrierungs- und Scheduling-Tool für Container, virtuelle Maschinen und einfache Binärdateien. Im Gegensatz zu Kubernetes benötigt Nomad keine umfangreiche Infrastruktur. Es läuft problemlos auf einem einzelnen Node und überzeugt durch seine Einfachheit – besonders in kleineren Setups, Homelabs oder Edge-Umgebungen.
Nomad besteht im Kern aus einem einzigen Binary, das sowohl als Server als auch als Client fungieren kann. Damit eignet es sich hervorragend für kompakte Systeme – etwa für einen Alpine-Linux-Server.
- Sprache: Go
- Aufgabe: Orchestrierung und Scheduling
- Architektur: Einfache, binäre Bereitstellung
- Alternativen: Kubernetes, Docker Swarm
Alpine Linux & MUSL – Minimalismus mit Tücken
Alpine Linux steht für Minimalismus. Die Distribution basiert auf:
- musl libc statt glibc
- busybox statt der GNU Coreutils
Das Resultat: ein extrem schlankes, schnelles und sicheres System – ideal für Container-Umgebungen und ressourcenschwache Hardware.
Doch dieser Minimalismus hat Nebenwirkungen. Viele Anwendungen, die gegen glibc gebaut sind, funktionieren nicht ohne Weiteres mit musl. Besonders problematisch wird es, wenn man auf einem glibc-basierten System kompiliert und das Binary anschließend auf Alpine ausführt.
Das Problem: gebaut auf Xubuntu, gescheitert auf Alpine
Genau in diese Falle bin ich geraten.
Auf meiner Xubuntu-Workstation (glibc) wollte ich Nomad aus den Quellen bauen – um es anschließend auf meinem Alpine-basierten Heimserver auszuführen.
Der erste Versuch
git clone https://github.com/hashicorp/nomad.git
cd nomad
make release TARGETS=linux_amd64
Der Build lief fehlerfrei durch. Im Verzeichnis pkg/linux_amd64/ lag das fertige Binary:
pkg/linux_amd64/nomad
Doch nach dem Kopieren auf den Alpine-Server folgte die Ernüchterung:
./nomad
-sh: ./nomad: not found
Die Datei war vorhanden, aber nicht ausführbar. Ein klassischer Hinweis auf ein Linking-Problem.
Diagnose mit ldd
Ein kurzer Test brachte Gewissheit:
ldd nomad
Ergebnis:
/lib64/ld-linux-x86-64.so.2 (0x7fa9060cf000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fa9060cf000)
Error relocating nomad: __snprintf_chk: symbol not found
Error relocating nomad: __vfprintf_chk: symbol not found
Error relocating nomad: __pthread_unregister_cancel: symbol not found
Error relocating nomad: __register_atfork: symbol not found
Error relocating nomad: __asprintf_chk: symbol not found
Error relocating nomad: __vdprintf_chk: symbol not found
Error relocating nomad: __isoc23_strtol: symbol not found
Error relocating nomad: __vasprintf_chk: symbol not found
Error relocating nomad: __vsnprintf_chk: symbol not found
Error relocating nomad: __strncpy_chk: symbol not found
Error relocating nomad: __longjmp_chk: symbol not found
Error relocating nomad: __dprintf_chk: symbol not found
Error relocating nomad: __fprintf_chk: symbol not found
Error relocating nomad: __pthread_register_cancel: symbol not found
Die Ausgabe zeigte deutlich: Das Binary war gegen glibc gelinkt – und musl konnte die benötigten Symbole schlicht nicht bereitstellen.
Die Lösung: Build direkt unter Alpine
Der eleganteste Ansatz: Nomad nativ mit musl bauen – also direkt in einer Alpine-Umgebung.
Dafür genügt ein kompakter Container, der alle notwendigen Werkzeuge enthält. Das folgende Containerfile legt die Grundlage:
# syntax=docker/dockerfile:1
FROM alpine:3.22 AS builder
# --- Build dependencies ---
RUN apk add --no-cache \
bash \
build-base \
git \
curl \
ca-certificates \
make \
unzip \
tar \
linux-headers \
zip
# --- Install Go ---
ENV GOLANG_VERSION=1.25.3
RUN curl -fsSL https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz | tar -C /usr/local -xz
ENV PATH="/usr/local/go/bin:${PATH}"
# --- Build environment ---
WORKDIR /src
COPY . .
ENV CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
GOPATH=/root/go \
PATH="/root/go/bin:/usr/local/go/bin:${PATH}"
# --- Build Nomad ---
RUN make release TARGETS=linux_amd64
# --- Post-build export step ---
# Wenn /out gemountet ist, wird das Ergebnis direkt dort abgelegt
RUN mkdir -p /out && \
cp pkg/linux_amd64/nomad /out/nomad && \
cp pkg/linux_amd64.zip /out/linux_amd64.zip
# --- Runtime behavior: export results and exit ---
CMD ["/bin/sh", "-c", "echo '✅ Build complete. Files are in /out'; ls -lh /out"]
Kompilieren und Exportieren
mkdir dist
podman build -v "$(pwd)/dist:/out" -t nomad-musl-builder -f Containerfile
Nach erfolgreichem Build liegen die Dateien bereit:
ls dist
linux_amd64.zip nomad
Ein kurzer Check auf dem Zielsystem bestätigt den Erfolg:
ldd nomad
Ausgabe:
/lib/ld-musl-x86_64.so.1 (0x7fc7a84ff000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc7a84ff000)
Das Binary ist nun musl-native – und läuft problemlos.
Testlauf
./nomad agent -dev -bind=0.0.0.0
Im Browser unter
http://<ip-des-servers>:4646
erscheint die gewohnte Nomad-Oberfläche – diesmal ohne Fehlermeldungen.
- Kein Cross-Compiling notwendig
- Reproduzierbare Builds
- Korrektes Target für libc garantiert
- Saubere Trennung von Build- und Laufzeitumgebung
Fazit
Alpine Linux ist ein hervorragendes Fundament für schlanke Systeme – doch seine musl-basierte Architektur verlangt Aufmerksamkeit beim Bauen von Software. Nomad selbst ist in Go geschrieben und lässt sich daher mit geringem Aufwand nativ für musl kompilieren.
Ein dedizierter Container-Build sorgt für reproduzierbare Ergebnisse und vermeidet typische Stolperfallen.
Erkenntnis: Wer auf Alpine deployen will, sollte dort auch bauen.
„Minimalismus funktioniert nur, wenn man versteht, was fehlt.“
Bildnachweis: Das Nomad-Logo ist eine eingetragene Marke von HashiCorp, Inc. Verwendung im Rahmen redaktioneller Berichterstattung gemäß den Trademark-Richtlinien von HashiCorp. Das Alpine-Linux-Logo ist Eigentum des Alpine Linux Development Teams. Verwendung im Rahmen redaktioneller Berichterstattung. Tux, das Linux-Maskottchen, wurde von Larry Ewing erstellt (unter Verwendung von GIMP, 1996). Verwendung im Rahmen redaktioneller Berichterstattung.