Hintergrund und Motivation
HashiCorp Nomad ist ein leichtgewichtiger Orchestrator, der auf
bemerkenswert einfache Weise verschiedenste Workloads unter einer
gemeinsamen Oberfläche verwaltet – vom klassischen Prozess über
Container bis hin zu virtuellen Maschinen. Seine Stärke liegt in der
Modularität: dank unterschiedlicher Driver (exec, raw_exec,
qemu, docker u. v. m.) lassen sich sehr verschiedene Umgebungen und
Technologien unter einem einheitlichen Scheduler betreiben.
Im Kern folgt Nomad einem klaren und kompakten Architekturprinzip: Ein Agent-Programm läuft auf jeder Maschine. Im Server-Modus übernimmt es Planung, Cluster-Management und API-Zugriff, im Client-Modus führt es Aufgaben aus. Die Server-Knoten bilden gemeinsam einen Raft-basierten Konsensus-Cluster, der den Zustand repliziert und Scheduling-Entscheidungen trifft – ohne dass eine Vielzahl separater Steuerungskomponenten erforderlich ist.
Damit erfüllt Nomad zwar die wesentlichen Steuerfunktionen einer Control Plane, kommt aber ohne eine klassische, vielschichtige Control-Plane-Architektur wie Kubernetes aus. Die Clients arbeiten weitgehend autark, führen laufende Tasks auch bei kurzzeitiger Server-Nichtverfügbarkeit weiter und reduzieren so systemische Abhängigkeiten.
Dieses Design macht Nomad besonders attraktiv für kritische oder verteilte Umgebungen, in denen Stabilität, Übersicht und Determinismus wichtiger sind als maximale Feature-Dichte.
Vergleich: Kubernetes vs. Nomad (Control-Plane-Architektur)
| Aspekt | Kubernetes | HashiCorp Nomad |
|---|---|---|
| Architektur | Mehrere dedizierte Control-Plane-Komponenten (kube-apiserver, Scheduler, Controller, etcd) |
Ein einzelner Server-Agent-Typ übernimmt alle Steuerungsaufgaben (API, Scheduler, State) |
| State-Store | etcd (externer Key-Value-Store) |
Integriert (Raft-basiertes Konsensus-Cluster) |
| Kommunikationsmodell | Stark zentralisiert (API-Server als Vermittler für alle Interaktionen) | Dezentral unter Servern mit Leader-Election, Clients kommunizieren direkt mit Servern |
| Abhängigkeiten | Hoch – viele separate Prozesse und Services | Gering – ein Binary für Server & Client, keine externen Datenbanken |
| Autonomie der Worker | Eingeschränkt – Worker benötigen durchgehend Verbindung zur Control Plane | Hoch – laufende Tasks bleiben aktiv, auch wenn keine Server erreichbar sind (keine neuen Jobs ohne Server) |
| Zielbild | Hochskalierte, dynamische Cloud-Infrastruktur mit granularer Steuerung | Schlanke, robuste und kontrollierbare Clusterumgebung mit minimalem Overhead |
Warum NetBSD?
Die Idee dieses Proof of Concept entstand aus der Kombination zweier Wertewelten: Nomads Einfachheit und NetBSDs Klarheit. NetBSD steht seit Jahrzehnten für Portabilität, Konsistenz und Transparenz. Es ist ein System, das man versteht – bis in den Kernel hinein. Kein unnötiger Ballast, kein intransparenter Automatismus.
Genau das macht es zu einer spannenden Basis für Nomad:
- hohe Portabilität und sauberes Systemdesign
- stabile und vorhersehbare Laufzeitumgebung
- gut nachvollziehbarer Aufbau
Besonders interessant für den Port sind die folgenden Treiber:
exec– klassisches Prozessmodellraw_exec– direkter Prozessstart ohne zusätzliche Isolationqemu– Virtualisierung als Workload- perspektivisch: ein eigener
xen-Treiber, um native Hypervisor-Integration zu ermöglichen
Nomad und NetBSD teilen denselben architektonischen Grundgedanken: Klarheit über Komplexität.
Der Weg zum Prototypen
Nomad ist in Go geschrieben – ein Glücksfall, denn Go wird unter NetBSD hervorragend unterstützt. Das Ökosystem funktioniert reibungslos, pkgsrc liefert aktuelle Compiler und Bibliotheken, und der Cross-Platform-Gedanke von Go harmoniert mit NetBSDs Designphilosophie.
Schritt 1: Vorbereitung
Wie bei Drittsoftware auf NetBSD üblich, wäre der Weg über pkgsrc ideal. Da es dort jedoch (noch) keinen Port für Nomad gibt, beginnen wir manuell:
git clone https://github.com/hashicorp/nomad.git
pkgin install gmake go124
NetBSD bringt von Haus aus BSD Make mit, doch Nomad setzt auf ein GNU Makefile – also installieren wir gmake und starten den ersten Versuch.
Erster Build-Versuch
Ein gmake release führt zwar zum Start, aber der Build bricht mit einer Fehlermeldung ab.
Ursache: das Makefile erkennt NetBSD als Plattform (noch) nicht.
Ein kurzer Blick ins GNUmakefile zeigt, dass die Variable THIS_OS geprüft wird. Ein kleiner Patch genügt:
ifeq (NetBSD,$(THIS_OS))
ALL_TARGETS = netbsd_amd64
endif
Ein erneuter Versuch läuft etwas weiter – diesmal scheitert der Build an einer Go-Dependency: github.com/mitchellh/go-ps.
Der Fehler:
undefined: processes
undefined: findProcess
Das bedeutet: Die Bibliothek hat keine Implementierung für NetBSD.
Untersuchung der Dependency
Ein Blick auf das Repository zeigt: go-ps ist seit 2024 archiviert, der letzte Commit liegt fünf Jahre zurück. Nomad nutzt es offenbar für einfache Prozessabfragen – stabil, aber alt.
Wir forken das Projekt unter github.com/MatthiasPetermann/go-ps und beginnen die Anpassung.
Ziel:
Eine NetBSD-Implementierung, die /proc nutzt – analog zur bestehenden process_unix.go, die für Linux und Solaris aktiv ist.
Dazu genügt es zunächst, den Build-Tag anzupassen:
// +build linux solaris netbsd
Beim Kompilieren taucht der Hinweis auf, dass UnixProcess.Refresh() fehlt – die Methode, die /proc/<pid>/stat parst.
Wir kopieren also die Linux-Version (process_linux.go), legen sie als process_netbsd.go an und passen den Parser an.
Exkurs: /proc/<pid>/stat auf NetBSD
Ein Vergleich zeigt:
NetBSD:
1016 (xinit) S 856 1016 850 ...
Linux:
1042 (systemd-logind) S 1 1042 1042 ...
Die ersten Felder (pid, comm, state, ppid, pgrp, sid) sind identisch – genau die, die der Parser nutzt.
Somit kann die Funktion fast unverändert übernommen werden.
Der resultierende Code:
// +build netbsd
package ps
import (
"fmt"
"io/ioutil"
"strings"
)
func (p *UnixProcess) Refresh() error {
statPath := fmt.Sprintf("/proc/%d/stat", p.pid)
dataBytes, err := ioutil.ReadFile(statPath)
if err != nil {
return err
}
data := string(dataBytes)
binStart := strings.IndexRune(data, '(') + 1
binEnd := strings.IndexRune(data[binStart:], ')')
p.binary = data[binStart : binStart+binEnd]
data = data[binStart+binEnd+2:]
_, err = fmt.Sscanf(data, "%c %d %d %d", &p.state, &p.ppid, &p.pgrp, &p.sid)
return err
}
Damit ist go-ps um NetBSD erweitert.
Integration in Nomad
Damit Nomad unseren Fork statt der Originalbibliothek nutzt, ergänzen wir den Replace-Block in go.mod um:
replace (
...
...
github.com/mitchellh/go-ps => github.com/MatthiasPetermann/go-ps v1.1.1
)
Dann:
go mod tidy
gmake release
Und siehe da:
==> Building pkg/netbsd_amd64/nomad with tags ui codegen_generated release...
==> Packaging for netbsd_amd64...
adding: LICENSE.txt (deflated 62%)
adding: nomad (deflated 60%)
==> Results:
/h/mpeterma/Projects/hashcorp/nomad/pkg
├── netbsd_amd64
│ ├── LICENSE.txt
│ └── nomad
└── netbsd_amd64.zip
1 directory, 3 files
Ein vollständiges, komprimiertes Release-Package für NetBSD – ohne offensichtliche Fehler gebaut, und das ohne tiefgreifende Eingriffe in Nomads Kern.
Den Abschluss bildet ein Test, ob das erzeugte Binary lauffähig ist:
mkdir nomad
cd nomad
unzip ~/Projects/hashcorp/nomad/pkg/netbsd_amd64.zip
./nomad
Usage: nomad [-version] [-help] [-autocomplete-(un)install] <command> [args]
Common commands:
run Run a new job or update an existing job
stop Stop a running job
status Display the status output for a resource
alloc Interact with allocations
job Interact with jobs
node Interact with nodes
agent Runs a Nomad agent
Other commands:
acl Interact with ACL policies and tokens
action Run a pre-defined command from a given context
agent-info Display status information about the local agent
config Interact with configurations
...
Fazit
Fazit: Der Proof of Concept zeigt, dass sich Nomad mit wenigen gezielten Anpassungen erfolgreich auf NetBSD bauen lässt. Der Build-Prozess läuft stabil durch, und das resultierende Release-Package für
netbsd_amd64enthält ein startbares Binary.
Die größte Hürde war eine fehlende Plattformunterstützung in der go-ps-Bibliothek, die durch eine eigene NetBSD-Implementierung gelöst wurde. Mit dieser Ergänzung und einer kleinen Erweiterung im Makefile erkennt Nomad NetBSD korrekt als Zielplattform und kompiliert ohne weitere Eingriffe.
Damit ist die Grundlage gelegt, um Nomad unter NetBSD praktisch zu erproben. In einem kommenden Beitrag folgen erste Tests zur Laufzeit, Funktionsprüfung der Task-Driver (raw_exec, exec, qemu) und eine Einschätzung, wie sich Nomad im Betriebssystemumfeld von NetBSD verhält.
Bildnachweis: Das Nomad-Logo ist eine eingetragene Marke von HashiCorp, Inc.. Verwendung im Rahmen redaktioneller Berichterstattung gemäß den Trademark- und Markenrichtlinien von HashiCorp. Das NetBSD-Logo ist eine eingetragene Marke der NetBSD Foundation, Inc. Verwendung im Rahmen redaktioneller Berichterstattung gemäß den Richtlinien zur Logonutzung.