Blog

FFS auf Fedora CoreOS bereitstellen — Von Null zur Produktion

Veröffentlicht am 2026-04-21

Eine Webanwendung in Produktion zu betreiben bedeutet mehr, als sie zum Kompilieren zu bringen. Es bedeutet verschlüsselte Zugangsdaten, die niemals im Klartext auf die Festplatte gelangen, einen Prozess-Supervisor, der Hänger erkennt — nicht nur Abstürze — und einen Update-Pfad, der weder SSH noch Hoffnung erfordert. Dieser Beitrag beschreibt die Bereitstellung von FFS auf Fedora CoreOS: warum CoreOS, wie die Deployment-Pipeline funktioniert und welche Kompromisse damit verbunden sind.

Warum Fedora CoreOS

CoreOS ist ein Betriebssystem, das für eine einzige Aufgabe konzipiert wurde: Container auszuführen. Es gibt keinen Paketmanager im herkömmlichen Sinne, kein /usr/local/bin voller manuell kompilierter Tools, keine Konfigurationsdrift. Der gesamte Systemzustand wird bei der Installation in einer einzigen Ignition-Datei deklariert. Wenn ein Knoten ausfällt, provisioniert man einen neuen aus derselben Datei und erhält eine identische Maschine.

Diese Philosophie passt gut zu einer Plattform wie FFS. Die Anwendung läuft in einem Container-Image, das seine eigene Wt-Laufzeitumgebung, Shared Libraries und kompilierte Komponenten mitbringt. Der Host stellt den Kernel, die Container-Laufzeit und systemd bereit — sonst nichts.

Vorteile:

Unveränderliche Infrastruktur beseitigt „funktioniert auf meinem Rechner"-Überraschungen. Jeder CoreOS-Knoten, der aus derselben Ignition-Datei provisioniert wird, ist identisch: dieselben Firewall-Regeln, dieselbe Verzeichnisstruktur, dieselben systemd-Einheiten, dieselben SSH-Schlüssel. Es gibt keinen angesammelten Zustand aus monatelangem dnf install und vergessenen Konfigurationsänderungen.

Automatische Updates halten das Betriebssystem gepatcht, ohne Bedienereingriff. CoreOS verwendet ein A/B-Partitionsschema — es lädt das Update auf die inaktive Partition herunter, startet in diese hinein und führt bei fehlgeschlagenem Boot automatisch ein Rollback durch. Der Anwendungscontainer bleibt unberührt, da er seine eigenen Abhängigkeiten mitbringt.

Minimale Angriffsfläche ergibt sich aus dem schreibgeschützten Root-Dateisystem und dem Fehlen eines Paketmanagers. Es gibt keinen Compiler, kein curl | bash-Risiko, keine vergessenen Entwicklungs-Header. Die einzigen beschreibbaren Pfade sind explizit gemountete Datenverzeichnisse.

Kompromisse:

Debugging auf einem CoreOS-Knoten ist schwieriger als auf einer vollständigen Linux-Workstation. Es gibt kein gdb, kein strace (ohne toolbox), keinen Texteditor außer vi. Wenn um 2 Uhr nachts etwas schiefgeht, liest man journalctl-Ausgaben und denkt sorgfältig nach, statt einen Debugger anzuschließen.

Das Ignition-basierte Provisionierungsmodell bedeutet, dass Konfigurationsänderungen entweder eine Neuprovisionierung des Knotens oder das manuelle Bearbeiten von systemd-Einheiten erfordern — es gibt kein apt-get install oder dnf update für Änderungen auf Host-Ebene. Das ist ein Feature, kein Bug, erfordert aber Disziplin.

SELinux ist standardmäßig im erzwingenden Modus. Container-Volume-Mounts interagieren mit SELinux-Labels, und die Kombination von Rootless-Podman mit bind-gemounteten Host-Verzeichnissen kann Berechtigungsprobleme erzeugen, die erst zur Laufzeit sichtbar werden. Das :Z-Volume-Flag übernimmt die Neulabelung, ändert aber die Dateieigentümerschaft auf eine Weise, die überraschen kann, wenn man Verzeichnisse zwischen Containern mit unterschiedlichen UID-Namespaces teilt.

Die Deployment-Pipeline

Die FFS-Bereitstellung auf CoreOS folgt einer dreistufigen Pipeline: Vorbereitung, Provisionierung, Deployment. Jede Stufe hat ein eigenes Skript, das die Komplexität kapselt.

Stufe 1 — Vorbereitung (Entwicklerrechner)

ffs-prepare.sh läuft auf der Workstation des Entwicklers, nicht auf dem Server. Es liest die Projektkonfiguration, sammelt SSH-Schlüssel, SMTP-Zugangsdaten und TLS-Zertifikatspasswörter und erzeugt zwei Dateien: eine Butane-Konfiguration (ffs.bu) und ihr kompiliertes Ignition-Pendant (ffs.ign).

$ bash deploy/coreos/ffs-prepare.sh --project serbest --name www

Die Butane-Vorlage verwendet Platzhalter (@PROJECT@, @INSTANCE_NAME@, @HTTPS_PORT@), die aus der .env-Datei des Projekts und interaktiven Eingabeaufforderungen befüllt werden. Das Ergebnis ist eine vollständige Maschinenspezifikation: systemd-Einheiten, Firewall-Regeln, Verzeichnisberechtigungen, Benutzerkonten und autorisierte SSH-Schlüssel.

Das TLS-Zertifikatspasswort wird mit systemd-creds verschlüsselt — es wird verschlüsselt auf der Festplatte gespeichert und erst beim Dienststart in ein temporäres RAM-gestütztes Verzeichnis entschlüsselt.

Stufe 2 — Provisionierung (CoreOS-Installation)

Die generierte ffs.ign wird dem CoreOS-Installer übergeben. Dies ist eine einmalige Operation pro Maschine (oder pro Neuprovisionierung):

$ coreos-installer install /dev/sda --ignition-file ffs.ign

Nach dem Neustart ist die Maschine vollständig eingerichtet: der ffs-Systembenutzer, die Projektverzeichnisstruktur, die systemd-Diensteinheiten und die Firewall-Konfiguration. Für die Ersteinrichtung ist kein SSH-Login erforderlich.

Stufe 3 — Deployment (Image-Transfer)

ffs-deploy.sh überträgt das Container-Image und die Projektdateien auf den CoreOS-Knoten. Bei der Erstbereitstellung führt es auch das Server-Setup-Skript aus und verschlüsselt die TLS-Zugangsdaten:

$ bash deploy/coreos/ffs-deploy.sh 192.168.1.10 \
    --name www --user core --first-deploy

Nachfolgende Updates sind einfacher — nur das Image und geänderte Projektdateien:

$ bash deploy/coreos/ffs-deploy.sh 192.168.1.10 --name www

Das Deploy-Skript übernimmt den Image-Transfer über podman save / podman load, die Synchronisation der Projektdateien über rsync und den Dienstneustart über systemctl restart.

TLS-Zertifikatsverwaltung

FFS verwendet verschlüsselte private TLS-Schlüssel. Der private Schlüssel wird während des Build-Prozesses mit AES-256-CBC verschlüsselt und als server.key.enc gespeichert. Beim Containerstart entschlüsselt das Entrypoint-Skript ihn nach /tmp/secrets/ (ein tmpfs-Mount — nur RAM, berührt niemals die Festplatte) unter Verwendung der CERT_PASS-Umgebungsvariable, die ihrerseits aus einer verschlüsselten systemd-Zugangsdaten stammt.

Zertifikatsrotation

ffs-cert.sh bietet Zertifikats-Lifecycle-Management:

# Aktuellen Zertifikatsstatus anzeigen (lokal, Server, Container)
$ bash deploy/coreos/ffs-cert.sh status 192.168.1.10 --name www

# HTTPS-Zertifikat erneuern
$ bash deploy/coreos/ffs-cert.sh rotate-https 192.168.1.10 --name www

# SSH-Zugangsschlüssel erneuern
$ bash deploy/coreos/ffs-cert.sh rotate-ssh 192.168.1.10 --name www

Der status-Befehl zeigt Ablaufdaten der Zertifikate, Fingerabdrücke und warnt, wenn Zertifikate innerhalb von 30 Tagen ablaufen. rotate-https sichert automatisch das bestehende Zertifikat, stellt das neue bereit, verschlüsselt die Zugangsdaten erneut und startet den Dienst neu.

Prozessüberwachung und Gesundheitsmonitoring

Eine der schwierigsten Lektionen aus den frühen CoreOS-Deployments war die Erkenntnis, dass ein laufender Prozess nicht unbedingt ein gesunder Prozess ist. FFS verließ sich ursprünglich auf einen einfachen fork-basierten Watchdog: Parent forkt Child, wartet, startet bei Absturz neu. Das fängt Segfaults und unbehandelte Ausnahmen ab, aber keine Hänger — eine blockierte SMTP-Verbindung oder eine verklemmte Ereignisschleife hält den Prozess am Leben, während er nichts ausliefert.

Die aktuelle Überwachungsarchitektur verwendet pipe-basierte Gesundheitssonden:

Parent (Supervisor)
  └── Child 0  ──Pipe──→  Parent liest 'H'-Heartbeats
  └── Child N  ──Pipe──→  (zukünftige Worker)

Das Child schreibt alle 10 Sekunden ein Heartbeat-Byte in eine Pipe. Der Parent führt poll() auf allen Pipes durch — kein Heartbeat für drei aufeinanderfolgende Sondenintervalle bedeutet, dass das Child hängt, und der Parent beendet es mit SIGTERM (mit einer 5-Sekunden-Gnadenfrist vor SIGKILL) und startet neu.

Für die systemd-Integration sendet der Parent bei jedem Sondenintervall sd_notify("WATCHDOG=1"), wenn alle Children gesund sind. Wenn ein Child ungesund ist, wird der Heartbeat zurückgehalten und systemds eigener WatchdogSec-Timer beendet schließlich den gesamten Container — eine zweite Verteidigungslinie.

Die sd_notify-Implementierung ist eigenständig: ein direktes sendmsg() an den NOTIFY_SOCKET-Unix-Datagramm-Socket, ohne libsystemd-Abhängigkeit. Das hält das Container-Image minimal und funktioniert auf jedem Basis-Image.

Kommandozeilenflags

./ffs --sd-notify     # systemd-Integration (CoreOS-Produktion)
./ffs --watchdog      # eigenständige Überwachung (Docker, Dev-Container)
./ffs                 # nackter Prozess (GDB, IDE, Valgrind)

Updates bereitstellen

Nach der Erstbereitstellung folgen Updates einem vorhersehbaren Workflow:

# 1. Neues Produktions-Image bauen (Entwicklerrechner, im Dev-Container)
ffscm rebuild production --project serbest --compiler intel

# 2. Auf den CoreOS-Knoten deployen
bash deploy/coreos/ffs-deploy.sh 192.168.1.10 --name www

# 3. Verifizieren
bash deploy/coreos/ffs-cert.sh status 192.168.1.10 --name www

Das Deploy-Skript überträgt nur die geänderten Image-Schichten und Projektdateien. Der Dienst wird automatisch neu gestartet. Wenn die neue Version nicht startet (Schnellfehlererkennung: drei Abstürze innerhalb von 5 Sekunden), gibt der Watchdog auf und systemds Restart=on-failure übernimmt mit steigender Wartezeit.

Gewonnene Erkenntnisse

systemds WatchdogSec erfordert aktive Teilnahme. Das Setzen von WatchdogSec=120s ohne Senden von Heartbeats führt dazu, dass systemd den Dienst alle zwei Minuten beendet. FFS hat dies auf die harte Tour gelernt — der Server schien zufällig neu zu starten, bis wir das Neustartintervall mit dem Watchdog-Timeout korrelierten.

Shell-Expansion findet nicht in systemds ExecStart statt. Das Zugangsdaten-Muster $(cat ${CREDENTIALS_DIRECTORY}/cert_pass) erfordert einen /bin/sh -c-Wrapper. Ohne diesen wird die literale Zeichenkette $(cat ...) an den Container übergeben, und die TLS-Schlüsselentschlüsselung schlägt stillschweigend fehl.

Ausblick

Das aktuelle Deployment-Modell bewältigt die Einzelknoten-Produktion gut. Zukünftige Verbesserungen umfassen Let's-Encrypt-Integration für automatische Zertifikatserneuerung, Multi-Worker-Unterstützung (die N-Child-Watchdog-Infrastruktur ist bereits vorhanden) und einen Deployment-Health-Check, der verifiziert, dass der HTTPS-Endpunkt korrekt antwortet, bevor das Update als erfolgreich deklariert wird.

CoreOS bietet eine solide Grundlage für den Betrieb von FFS in der Produktion. Die Unveränderlichkeitsgarantien, automatischen OS-Updates und die systemd-Integration machen es zu einer natürlichen Wahl für eine Plattform, die Wert auf betriebliche Zuverlässigkeit legt. Der Kompromiss — weniger Flexibilität, steilere Debugging-Kurve — lohnt sich für das Vertrauen, dass jedes Deployment reproduzierbar und jeder Neustart überwacht ist.