Die Hardware ist nur das Fundament. Was mein Lab eigentlich ausmacht, ist ein einziges Git-Repository, das den kompletten Cluster beschreibt — und zwar nicht als Dokumentation, sondern als verbindliche Quelle der Wahrheit. Dieser Beitrag ist die kurze Geschichte, wie es dazu kam, und wie die Teile heute zusammenspielen.
Vom Skript zum Zustand
Wie vermutlich jedes Homelab begann auch meines mit einer Sammlung von Skripten: ein bisschen Bash hier, ein helm install dort, dazwischen handgepflegte YAML-Dateien, die man „mal eben“ mit kubectl apply eingespielt hat. Das funktioniert — bis zu dem Tag, an dem man nicht mehr sicher sagen kann, warum eine Ressource im Cluster so aussieht, wie sie aussieht. Imperative Befehle beschreiben einen Übergang, keinen Zustand. Und Übergänge vergisst man.
Der Bruch kam mit der Umstellung auf Flux CD und einem strikt deklarativen Ansatz. Seitdem gilt eine einfache Regel: Alles, was im Cluster läuft, muss aus dem Repository ableitbar sein. Der einzige manuelle Schritt ist das Bootstrap eines frischen Clusters.
Das Repository ist der Ist-Zustand
Der entscheidende Gedanke bei GitOps wird oft zu kurz gefasst: Das Repo beschreibt nicht nur den Soll-Zustand. Mit aktiviertem Pruning beschreibt es auch den Ist-Zustand — denn was nicht (mehr) im Repo steht, wird aus dem Cluster entfernt. Eine Ressource auskommentieren heißt: sie verschwindet beim nächsten Reconcile.
Das sieht man der zentralen Aggregations-Kustomization direkt an. Auskommentierte Einträge sind keine Notizen, sondern aktive Entscheidungen:
# fediverse-system
- ../../applications/fediverse-system
- ../../applications/fediverse-system/snac
# NOTE: Way too bloated - Keeping for reference only
#- ../../applications/fediverse-system/mastodon
# harbor
# FIXME: Still no ARM64 support, waiting for merge
# - ../../applications/harbor/harbor
snac läuft, Mastodon ist Geschichte (dazu in einem eigenen Beitrag mehr), Harbor wartet auf ARM64-Support. Das Repo erzählt damit ganz nebenbei die Evolution des Labs — der Git-Log ist gleichzeitig das Changelog der Infrastruktur.
Wie Flux die Ressourcen verdrahtet
Jeder Cluster hat eine GitRepository namens cluster, die auf dieses Repo zeigt. Darauf setzen pro Anwendung kleine Flux-Kustomization-Objekte auf — bei mir jeweils eine ks.yaml neben der App. Eine solche Kustomization ist bewusst schlank:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: zot
namespace: flux-system
spec:
interval: 15m
path: "./k8s/clusters/${CLUSTER_NAME}/applications/zot/zot/app"
prune: true
wait: true
sourceRef:
kind: GitRepository
name: cluster
dependsOn:
- name: rook-ceph-cluster
Drei Details tragen das ganze Modell:
prune: true— der oben beschriebene Ist-Zustand. Driftet der Cluster vom Repo ab, korrigiert Flux das.dependsOn— die Anwendungen bilden einen gerichteten Abhängigkeitsgraphen. Zot wartet auf Rook-Ceph, Workloads warten auf Vault und cert-manager, und so weiter. Flux rollt in dieser Reihenfolge aus.${CLUSTER_NAME}— die Pfade sind über einecluster-config.yamlparametrisiert. Derselbe Manifest-Baum funktioniert dadurch auf jedem Cluster; cluster-spezifisches Verhalten ist Opt-in über Overlays.
Im Überblick ergibt das die klassische Reconcile-Schleife, die nie aufhört zu laufen:
Geheimnisse bleiben draußen
Damit „alles im Repo“ nicht „alle Secrets im Repo“ bedeutet, gilt eine harte Trennung: Die wenigen Bootstrap-Secrets liegen mit
SOPS
verschlüsselt im Git (entschlüsselt wird zur Reconcile-Zeit mit dem AGE-Key des jeweiligen Clusters), alles Übrige kommt aus
Vault
und wird über den
External Secrets Operator
als Kubernetes-Secret in die Workloads gespiegelt. Im Klartext steht im Repo nie ein Geheimnis.
Mehrere Cluster, eine Form
Weil die Manifeste über ${CLUSTER_NAME} und Overlays parametrisiert sind, hat jeder Cluster dieselbe Gestalt: gleiche CNI (Cilium), gleiche Storage-Class-Namen, gleiches Tailnet-Layout. Ein neuer Cluster entsteht aus einer Vorlage (task core:cluster-create), bekommt seine eigenen Adressen in der cluster-config.yaml und ist ansonsten ein Klon der bewährten Form. Cluster werden so von Schmuckstücken zu Vieh — und genau das ist der Punkt.
Fazit
Das Lab ist kein statisches Setup, sondern ein lebendes Repository, das sich permanent im Wandel befindet. Der Unterschied zu früher ist nicht, dass sich nichts mehr ändert — sondern dass jede Änderung nachvollziehbar, reproduzierbar und rückgängig zu machen ist. Der Cluster ist nur noch die Projektion des Git-Logs in die Realität.
