🚀 Platform Engineer Lektion 25/50 ~10 Min. Fortgeschritten

GitOps als Plattform-Betriebsmodell

Pull vs. Push, Repo-Struktur, App-of-Apps und Fleet-Management.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du GitOps als Betriebsmodell einer ganzen Plattform erklären — nicht nur als Deployment-Methode einzelner Apps. Du kennst den Unterschied zwischen Pull- und Push-Modell, kannst Repo-Strukturen für viele Teams und viele Cluster begründen, verstehst das App-of-Apps-Muster bzw. ApplicationSets für Fleet-Management und kennst die offenen Flanken: Secrets, Drift und die Frage, was bei einem kaputten Sync passiert.

Das Problem: Wer hat den Cluster zuletzt angefasst?

Eine Plattform mit zehn Clustern und fünfzig Teams ändert sich hundertfach am Tag. Wenn diese Änderungen per kubectl apply aus Pipelines, Laptops und Notfall-Sessions in die Cluster gelangen, gibt es keine verlässliche Antwort auf die drei wichtigsten Betriebsfragen: Was läuft gerade? (Der Cluster weiß es, aber niemand sonst.) Wer hat das geändert und warum? (Das Audit-Log zeigt ein Service-Account-Token.) Wie kommen wir zum Stand von gestern zurück? (Niemand weiß, was gestern galt.)

GitOps beantwortet alle drei mit demselben Mechanismus: Der gewünschte Zustand jedes Clusters liegt deklarativ in Git; ein Controller im Cluster gleicht kontinuierlich ab und stellt ihn her. Damit ist Git die Single Source of Truth, jede Änderung ein reviewbarer Commit, und Rollback ein git revert. Für eine Plattform ist das mehr als Deployment-Komfort — es ist das Betriebsmodell: Auch Addons, Policies, Quotas und die Tenant-Namespaces selbst (alles aus den Lektionen 16–22) werden so verwaltet.

Pull schlägt Push — und Reconciliation schlägt Apply

Der entscheidende Architektur-Unterschied liegt im Auslöser:

Push-Modell: Die CI-Pipeline führt nach dem Build kubectl apply oder helm upgrade aus. Funktioniert — hat aber zwei strukturelle Schwächen. Erstens Security: Die Pipeline braucht weitreichende Cluster-Credentials, und CI-Systeme mit Admin-Zugriff auf alle Cluster sind ein erstklassiges Angriffsziel (Supply-Chain-Perspektive). Zweitens Einmaligkeit: Der Apply passiert einmal; was danach im Cluster verändert wird, sieht niemand.

Pull-Modell: Ein Controller im Cluster — die beiden CNCF-Graduated-Vertreter sind Argo CD und Flux — beobachtet Git und zieht Änderungen selbst. Die Cluster-Credentials verlassen den Cluster nie; CI endet beim Artefakt (Image bauen, Manifest-Repo aktualisieren). Vor allem aber: Der Controller gleicht kontinuierlich ab. Ändert jemand ein Deployment per kubectl edit am Repo vorbei, meldet der Controller Drift — und stellt je nach Konfiguration (Self-Heal) den Git-Stand automatisch wieder her. Diese permanente Reconciliation ist genau das, was Helm allein nicht bietet (Lektion 1 des Helm-Pfads lässt grüßen: Helm rendert, GitOps wacht).

Der Ablauf im Kern: Desired State in Git (YAML, Helm-Charts mit Values, Kustomize-Overlays) → Controller rendert und difft gegen den Live-Zustand → Sync wendet die Differenz an → Health Checks bewerten das Ergebnis. Argo CD bringt dafür eine UI mit Diff-Ansicht und Sync-Status mit; Flux ist stärker auf Kubernetes-native Bausteine (CRDs, Kustomize/Helm-Controller) reduziert. Beide tragen Plattformen in Produktion — die Wahl ist sekundär gegenüber der Struktur dahinter.

Repo-Struktur: die eigentliche Designentscheidung

Bei fünfzig Teams entscheidet die Repository-Architektur über Ownership, Blast Radius und Review-Qualität. Die bewährte Grundform trennt drei Belange:

platform-repo/          # Ownership: Platform-Team
├── clusters/
│   ├── prod-eu-1/      # was läuft auf welchem Cluster: Addons,
│   └── staging-eu-1/   # Policies, Tenant-Onboarding, Versionen
└── tenants/
    └── team-orders.yaml   # Namespace, Quota, RBAC, NetworkPolicies pro Tenant

app-repos (je Team)     # Ownership: Stream-Team
└── deploy/envs/{dev,staging,prod}/   # die Promotion-Struktur aus Lektion 23

Die Prinzipien dahinter: Ownership entlang der Repos — das Platform-Repo reviewt das Platform-Team, App-Repos reviewen die Teams selbst; niemand braucht Schreibrechte im fremden Bereich. Trennung von App-Code und Deployment-Config — das Manifest-Verzeichnis (oder ein separates Deployment-Repo) ändert sich durch Promotion-Commits, nicht durch jeden Code-Commit, was Reviews und Audit sauber hält. Ein Monorepo für alles funktioniert in kleinen Organisationen, skaliert aber schlecht: Review-Rauschen, Berechtigungs-Granularität und ein einziger Blast Radius für jede fehlerhafte Änderung.

Für Fleet-Management — zehn Cluster sollen konsistent dieselben Addons, Policies und Tenants bekommen — liefern die Tools Multiplikations-Muster: Argo CDs App-of-Apps (eine Application, die weitere Applications erzeugt) und mächtiger ApplicationSets mit Generatoren („für jeden Cluster im Verzeichnis X, für jeden Tenant in Liste Y: erzeuge diese Application”). Flux erreicht dasselbe mit Kustomizations pro Cluster-Verzeichnis. Damit wird ein neuer Cluster zum Bootstrap-Problem (Lektion 26): Controller installieren, auf das Repo zeigen — den Rest zieht er sich selbst.

Die offenen Flanken: Secrets, Drift-Ausnahmen, kaputte Syncs

Drei Themen entscheiden über Produktionstauglichkeit:

Secrets dürfen nicht im Klartext in Git liegen — Git vergisst nie. Zwei Muster: Verschlüsselt im Repo (Sealed Secrets, SOPS mit KMS-Schlüssel) — einfach, aber Rotation heißt Commit; oder Referenz statt Wert (External Secrets Operator: Git enthält nur den Verweis auf Vault/Cloud-Secret-Manager, der Operator synct den Wert in den Cluster). Für Plattformen ist das Referenz-Muster meist die bessere Wahl, weil Rotation und Zugriffskontrolle im Secret-System bleiben.

Gewollte Abweichungen: Manche Felder ändern Controller zur Laufzeit legitim — Replica-Zahlen unter HPA-Kontrolle sind das Standardbeispiel. Ohne Ausnahme-Konfiguration (in Argo CD ignoreDifferences) kämpfen GitOps-Controller und HPA gegeneinander. Drift-Erkennung braucht eine kuratierte Liste dessen, was abweichen darf.

Wenn der Sync bricht: Eine Application hängt auf „Degraded”, ein Upgrade des Controllers selbst geht schief, oder Git ist nicht erreichbar. Wichtigste Eigenschaft des Modells: Der Cluster läuft weiter — Reconciliation-Ausfall stoppt Änderungen, nicht Workloads. Aber das Platform-Team braucht eingeübte Antworten: Sync-Status-Alerts, ein Break-Glass-Verfahren für Notfall-Änderungen am Repo vorbei (dokumentiert, zeitlich begrenzt, mit Pflicht zur Nach-Synchronisation) und die Erkenntnis, dass der GitOps-Controller selbst ein Tier-1-Plattformdienst mit Monitoring und Upgrade-Sorgfalt ist.

Praxis: Drift-Korrektur mit eigenen Augen sehen

In einem kind-Cluster mit Argo CD:

kind create cluster --name gitops-lab
kubectl create namespace argocd
kubectl apply -n argocd -f \
  https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# CLI-Login (Port-Forward + initiales Admin-Passwort):
kubectl port-forward svc/argocd-server -n argocd 8080:443 &
argocd admin initial-password -n argocd
argocd login localhost:8080 --username admin --insecure

# Eine Application aus einem öffentlichen Beispiel-Repo anlegen:
argocd app create guestbook \
  --repo https://github.com/argoproj/argocd-example-apps.git \
  --path guestbook --dest-server https://kubernetes.default.svc \
  --dest-namespace default --sync-policy automated --self-heal
argocd app wait guestbook

Jetzt das Kernexperiment — Drift erzeugen und beobachten:

kubectl scale deployment guestbook-ui --replicas=5
kubectl get deployment guestbook-ui -w
# Binnen Sekunden setzt Argo CD die Replicas auf den Git-Stand zurück.
argocd app history guestbook   # jede Sync-Operation ist protokolliert

Deaktiviere danach Self-Heal (argocd app set guestbook --self-heal=false), wiederhole den Scale und sieh dir den „OutOfSync”-Status samt Diff in der UI an. Dieses Diff — Git-Stand gegen Live-Stand — ist das Artefakt, um das sich das gesamte Betriebsmodell dreht.

Typische Stolperfallen

GitOps nur für Apps, nicht für die Plattform: Wer Addons, Policies und Tenant-Setup weiter manuell pflegt, hat zwei Betriebsmodelle und damit die Audit-Lücke genau dort, wo die Rechte am höchsten sind. Das Plattform-Repo ist der wichtigere Teil.

Secrets im Klartext committen: Der Klassiker mit Langzeitschaden — einmal gepusht, für immer in der History. Pre-Commit-Scanning (z. B. gitleaks) als Pflicht-Hook, Secret-Muster von Tag eins festlegen.

Auto-Sync ohne Health-Bewertung und Staffelung: Wer alle Cluster gleichzeitig und automatisch auf jeden Commit synct, hat den Blast Radius eines fehlerhaften Commits maximiert. Progressive Ausrollung (erst Staging-Cluster, dann Prod-Wellen) und Health Checks vor der nächsten Welle gehören zum Fleet-Design.

Den Notfall-Pfad nicht definieren: Im Incident editiert jemand live — verständlich. Ohne Break-Glass-Regel (wann erlaubt, wie dokumentiert, wie zurückgeführt) wird daraus unbemerkter Dauer-Drift, und das Vertrauen ins Modell erodiert.

Interview-Vorbereitung

Auf „Erkläre GitOps als Betriebsmodell” antwortest du: Problem (hundert Änderungswege, keine verlässliche Antwort auf „was läuft, wer hat’s geändert, wie zurück?”) → Mechanik (deklarierter Zustand in Git, Pull-Controller mit kontinuierlicher Reconciliation, Drift-Erkennung/Self-Heal; Rollback = Revert) → Warum Pull (Credentials bleiben im Cluster, CI endet beim Artefakt, Abgleich ist permanent statt einmalig) → Plattform-Skala (Repo-Trennung nach Ownership, ApplicationSets/App-of-Apps für die Fleet, gestaffelte Syncs) → Flanken (Secrets per Referenz-Operator oder SOPS, ignoreDifferences für HPA & Co., Break-Glass-Prozess).

Wahrscheinliche Follow-ups:

  • „Argo CD oder Flux?” — beide CNCF-graduiert und produktionsbewährt; Argo CD punktet mit UI und ApplicationSets, Flux mit schlankem CRD-nativen Design. Wichtiger als die Wahl: Repo-Struktur, Secret-Strategie, Sync-Staffelung.
  • „Wie kommen Secrets in den Cluster?” — nie im Klartext in Git: entweder verschlüsselt (SOPS/Sealed Secrets) oder als Referenz mit External Secrets Operator gegen Vault/Cloud-KMS; Rotation spricht meist für das Referenz-Muster.
  • „Was passiert, wenn jemand direkt im Cluster ändert?” — Controller erkennt Drift; mit Self-Heal wird zurückgesetzt, ohne wird OutOfSync gemeldet. Gewollte Laufzeit-Änderungen (HPA-Replicas) brauchen deklarierte Ausnahmen.
  • „Wie rollst du eine Änderung sicher auf 20 Cluster aus?” — Wellen statt Gleichzeitigkeit: Generator-basierte Zuordnung (ApplicationSets) mit Staging-zuerst-Reihenfolge, Health-Gates zwischen den Wellen, Stopp bei Degraded.

Zusammenfassung

GitOps macht Git zur Single Source of Truth und einen Pull-Controller zur ausführenden Instanz: kontinuierliche Reconciliation statt einmaligem Apply, Drift-Erkennung statt stiller Abweichung, Revert statt Rekonstruktion. Auf Plattform-Ebene heißt das: Repos entlang der Ownership schneiden (Plattform vs. Teams), die Fleet über ApplicationSets/App-of-Apps multiplizieren und gestaffelt syncen — und die offenen Flanken Secrets, erlaubte Abweichungen und Break-Glass bewusst designen.

Ein Detail blieb offen: Wie kommt der GitOps-Controller selbst auf einen nagelneuen Cluster — und wer verwaltet den Verwalter? Das Henne-Ei-Problem des Bootstraps und alles, was nach Tag 1 kommt, behandelt die nächste Lektion.