Das Lab wird multi-cluster — wiederholbar föderiert, ehrlich evaluiert

Aus einem handverdrahteten Cluster plus Wegwerf-Test wurde ein wiederholbares Muster: ein Cluster pro Befehl, Apps im cluster-scope, Flux-Tenants, eine aufgeräumte Headscale-Domain — und Föderation statt Node-Mesh. Inklusive der ehrlichen Vorgeschichte (warum ClusterMesh rausflog), einer nüchternen Stabilitäts-Bewertung und dem, was als Nächstes fällig ist: Monitoring, Alerting, Backup und HA für die Föderations-Wurzel.

Die letzten Tage im Lab waren ein Strukturschnitt. Lange gab es genau einen ernsthaften Cluster — hydra, bare-metal — und daneben einen Wegwerf-Test, von dem die ClusterMesh-Episode erzählte. Inzwischen ist daraus ein wiederholbares Muster geworden: ein Cluster pro Befehl, Anwendungen im cluster-scope, Flux-Tenants, eine aufgeräumte Tailnet-Domain — und eine Föderation, die bewusst kein Pod-Mesh ist. Dieser Beitrag fasst die Änderungen zusammen, spricht ehrlich über die Probleme dahinter und bewertet, wie produktionstauglich das Ganze wirklich ist.

fr0st — mein NixOS-Flake von der Wurzel bis zum Wallpaper

Sechs Maschinen, zwei Betriebssysteme, ein Repository: fr0st ist mein persönliches flake-parts-Flake, das ThinkPads, einen Desktop, ein MacBook und VMs aus derselben Quelle deklariert. Eine ausführliche Tour durch die Architektur — die lib.system-Fabrik, die rekursive Modul-Discovery mit eigenem Options-Namespace, die Profile-Komposition, das Single-User-Identitätsmodell, Secure Boot via Lanzaboote, verschlüsselte Roots via disko, Remote-Installs mit nixos-anywhere, das SOPS-Schlüsselregister, eigene Pakete und Overlays, und am Ende der Desktop, den das alles ergibt.

Mein Lab ist ein GitOps-Monorepo, aus dem Flux Kubernetes-Cluster reconciled. Aber die Maschinen, von denen aus ich auf dieses Lab schaue — die ThinkPads, der Desktop, das MacBook, ein paar VMs —, haben ihre eigene deklarative Wurzel. Sie heißt fr0st, ist ein flake-parts -Flake, und dieser Beitrag nimmt sie von der Wurzel bis zum Wallpaper auseinander.

Frost auf dem Asphalt, der graue Himmel vor dir

Das Repo ist öffentlich über meinen Radicle -Seed-Node erreichbar:

# klassisch:
git clone https://seed.this-is-fine.io/zVi9VheaDwbEgCUQUQ9sLwpHuaMo.git ~/.config/nix
# oder via Radicle:
rad clone rad:zVi9VheaDwbEgCUQUQ9sLwpHuaMo

Stöbern lässt es sich im Web unter radicle.network/nodes/seed.this-is-fine.io .

Eine Postgres-Plattform statt zehn Datenbanken — CloudNativePG im Lab

Synapse, Mastodon, Harbor, Pocket-ID, Headscale: fast jeder zustandsbehaftete Dienst im Lab will Postgres. Früher hieß das zehn handgepflegte Datenbanken. Heute ist es ein Operator und pro Dienst ein dreizeiliges Cluster-Manifest — mit synchroner Replikation über drei Instanzen, VolumeSnapshot-Backups, automatischem Failover und Prometheus-Metriken out of the box. Wie CloudNativePG aus einer Bürde eine Plattform macht.

Wenn ich die zustandsbehafteten Dienste in meinem Lab durchgehe, fällt ein Muster auf: Synapse, Mastodon, Harbor, Pocket-ID, Headscale — sie alle wollen Postgres. Früher bedeutete das eine Handvoll handgepflegter Datenbanken, jede mit eigener Backup-Logik, eigenem Failover-Plan (also keinem) und eigener Überwachung (auch keiner). Heute ist Postgres in meinem Lab eine Plattform: ein Operator, und pro Dienst ein winziges deklaratives Cluster-Manifest. Das ist CloudNativePG .

Vault als PKI rausgeworfen — eine Offline-CA-Hierarchie mit cert-manager

Vault als interne Zertifizierungsstelle war bequem und ein Single Point of Failure zugleich: Läuft Vault nicht, wird kein Cert mehr ausgestellt. Ich habe die PKI auf eine klassische Offline-Hierarchie umgestellt — Root und Intermediate-Key liegen air-gapped, eine kurzlebige Sub-CA lebt als cert-manager-ClusterIssuer im Cluster, und Name Constraints erzwingen, dass kein Blatt je außerhalb meiner internen Domains signiert wird. Ein staged Cutover ohne Trust-Lücke inklusive.

Lange hat Vault in meinem Lab die internen Zertifikate ausgestellt — eine PKI-Engine, ein cert-manager-ClusterIssuer namens vault, fertig. Bequem. Und ein Single Point of Failure mit Ansage: Ist Vault sealed, abgelaufen oder beim Bootstrap, stellt niemand mehr ein Cert aus. Genau dieses Henne-Ei hat mich schon einmal in eine Break-Glass-Recovery gezwungen. Also habe ich die PKI von Vault gelöst und auf das gestellt, wofür X.509 eigentlich gedacht ist: eine Offline-Hierarchie, deren teuerste Schlüssel nie online sind.

Drei Schichten für ein Secret — von pass über SOPS und Vault in den Pod

In einem öffentlichen GitOps-Repo darf nie ein Klartext-Secret liegen — und trotzdem muss jeder Workload an seine Passwörter kommen. Mein Lab löst das in drei klar getrennten Schichten: SOPS+PGP/age für die paar Bootstrap-Secrets, die vor Vault existieren müssen; Vault KV als Langzeitspeicher für alles Anwendungsnahe; und der External Secrets Operator, der daraus zur Laufzeit Kubernetes-Secrets materialisiert. Eine Tour durch den Lebensweg eines Geheimnisses.

Mein Lab ist ein öffentliches GitOps-Repo — über meinen Radicle -Seed-Node seed.this-is-fine.io kann es jeder klonen. Daraus folgt eine harte Regel: kein Klartext-Secret, niemals, nirgends in Git. Und trotzdem braucht jeder Workload im Cluster seine Passwörter, API-Tokens und Signing-Keys. Diesen Widerspruch löse ich in drei Schichten, von denen jede genau eine Aufgabe hat.

Im Beitrag über die pre-commit-Hooks ging es darum, wie ich verhindere, dass ein Secret versehentlich in Git landet. Hier geht es um den geplanten Weg: wie ein Secret absichtlich von meiner Maschine bis in einen Pod fließt, ohne je unverschlüsselt das Repo zu berühren.