#kubernetes

Das Lab wird multi-cluster — wiederholbar föderiert und 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 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.

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.

Renovate als Dependency-Bot für ein GitOps-Monorepo

Mein Lab ist ein einziges Flux-Repository — Helm-Charts, Container-Images, Talos- und Kubernetes-Versionen, CLI-Pins in Dockerfiles und shell.nix. Renovate hält das alles aktuell, automerged das Unkritische und zwingt mich bei Ceph, Cilium und Vault zum Hinschauen. Hier ist, wie ich es scharf gestellt habe.

Mein Lab ist ein einziges Repository: ein GitOps-Monorepo, aus dem Flux mehrere Talos-Cluster reconciled. Alles, was eine Version trägt, steht darin — Helm-Charts, Container-Images, die Talos- und Kubernetes-Version, dazu CLI-Pins in einem Dockerfile und in shell.nix. Manuell halte ich das nicht aktuell. Das macht Renovate , und zwar so, dass es mir die langweiligen Bumps abnimmt und mich genau bei den drei, vier Dingen stoppt, bei denen ein blinder Merge teuer wäre.