GitOps auf Vanilla-Kubernetes ist schon eine Disziplin. GitOps auf OpenShift legt eine weitere Schicht darüber: Der Cluster ist nicht nur Kubernetes. Er ist Red-Hat-Verpackung, Operatoren, Security Context Constraints, Routes und oft ein Plattformteam, das Teile der Control Plane besitzt, die man nie anfasst.

Ich nutze Argo CD auf OpenShift wie anderswo — gewünschter Zustand in Git, Reconciliation im Cluster, Drift sichtbar in einer UI. Der Unterschied zeigt sich, wenn Sync auf Plattform-Leitplanken trifft. Dieser Beitrag ist ein praktischer Leitfaden für Anwendungsteams und Plattformingeneure, die einen Cluster teilen und GitOps brauchen, das den Kontakt mit dem Day-two-Alltag übersteht.

Was OpenShift zum GitOps-Bild hinzufügt

Upstream-GitOps setzt voraus, dass man das meiste deklarieren kann und der Controller konvergiert. OpenShift setzt dasselbe voraus und ergänzt Meinungen als Defaults.

Routes statt nur Ingress für viele externe Einstiegspunkte.

SCCs, die Pod-Security-Einstellungen mutieren oder ablehnen, die das Manifest deklariert.

Cluster Operators, die Plattformkomponenten installieren und upgraden, außerhalb des Application-Scopes.

Managed-Cluster-Policies auf ROSA oder ARO, wo manche Objekte read-only sind oder dem Cloud-Provider gehören.

Namespace-Quotas und Limits, die greifen, bevor das Deployment überhaupt schedult.

Das macht GitOps auf OpenShift nicht falsch. Es bedeutet, dass die Reconciliation-Schleife Nachbarn hat. Die Application synchronisiert das Deployment; die Plattform mutiert den Pod; der Quota-Controller lehnt ein Scale-up ab; Argo zeigt OutOfSync — und man muss wissen, welcher Diff zählt.

Der OpenShift-GitOps-Operator

Red Hat liefert OpenShift GitOps als Cluster-Operator. Er installiert und verwaltet Argo-CD-Instanzen — typischerweise eine Default-Instanz im Namespace openshift-gitops und optional weitere Instanzen für Team-Isolation.

Argo CD installiert man nicht wie in einem Lab-Cluster aus rohem YAML. Man installiert den Operator, konfiguriert eine ArgoCD-Custom Resource und überlässt Upgrades OLM. Das ist ein Vorteil: unterstützter Lifecycle, integrierte SSO-Muster, dokumentierte Upgrade-Pfade. Es ist auch eine Grenze: Das GitOps-Tooling des Teams hängt an Cluster-Version und Operator-Gesundheit.

Eine minimale ArgoCD-Instanz-Deklaration sieht so aus:

apiVersion: argoproj.io/v1beta1
kind: ArgoCD
metadata:
  name: openshift-gitops
  namespace: openshift-gitops
spec:
  applicationSet: {}
  resourceTrackingMethod: annotation
  server:
    route:
      enabled: true

Der Operator erzeugt Deployments, Services, Routes und RBAC für die Argo-CD-Control-Plane. Anwendungsteams arbeiten meist mit Application- und AppProject-CRs, nicht mit dem Operator selbst — aber jemand auf der Plattformseite sollte Operator-Upgrades und Instanz-Sizing besitzen.

Praktischer Hinweis: Wissen, welche Instanz das Team nutzt. Multi-Instanz-Setups trennen Plattform-Applications von Mandanten-Applications. An die falsche Argo-CD-URL zu gehen, ist ein häufiger Onboarding-Fehler.

Argo-CD-Applications auf OCP

Eine Application verbindet eine Git-Quelle mit einem Cluster-Ziel. Auf OpenShift bleibt das Ziel https://kubernetes.default.svc innerhalb des Clusters, oder eine externe API-URL bei zentralisiertem Argo CD.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: shop-api
  namespace: openshift-gitops
spec:
  project: team-payments
  source:
    repoURL: "https://github.com/example-org/shop-api.git"
    targetRevision: main
    path: deploy/overlays/production
  destination:
    server: "https://kubernetes.default.svc"
    namespace: payments-prod
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
    syncOptions:
      - CreateNamespace=true
      - ApplyOutOfSyncOnly=true
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas

Mehrere Felder verdienen Aufmerksamkeit auf OpenShift:

CreateNamespace=true — praktisch für Self-Service-Namespaces; vor breiter Aktivierung mit Plattform-Namens- und Quota-Policies abstimmen.

ApplyOutOfSyncOnly=true — reduziert Full-Replace-Churn; hilfreich, wenn Webhooks und SCC-Mutationen viele Felder berühren.

ignoreDifferences bei Replicas — üblich, wenn Horizontal Pod Autoscaler die Replica-Anzahl besitzt. Ohne das OutOfSync-Rauschen oder Sync-Kampf mit HPA.

automated prune: false — mein Default in Produktion, bis das Team geprüft hat, was Prune in einem geteilten Namespace löschen würde.

Mit oc prüfen, was Argo anfasst, bevor man der UI vertraut:

oc get application shop-api -n openshift-gitops -o yaml
oc argocd app diff shop-api --local deploy/overlays/production

Die genaue CLI-Anbindung hängt von Argo-CD-CLI-Login und RBAC ab. Die Gewohnheit zählt mehr als der Befehl: Diff in Git und in Argo lesen, bevor in Produktion synchronisiert wird.

AppProject-Grenzen

AppProject ist der Ort, an dem Multi-Tenant-GitOps gelingt oder undicht wird. Auf OpenShift AppProject-Destinations an Namespaces binden, die das Plattformteam wirklich bereitgestellt hat.

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-payments
  namespace: openshift-gitops
spec:
  description: "Payments squad production and staging"
  sourceRepos:
    - "https://github.com/example-org/shop-api.git"
    - "https://github.com/example-org/payments-platform.git"
  destinations:
    - namespace: payments-prod
      server: "https://kubernetes.default.svc"
    - namespace: payments-staging
      server: "https://kubernetes.default.svc"
  clusterResourceWhitelist: []
  namespaceResourceBlacklist:
    - group: ""
      kind: ResourceQuota
    - group: ""
      kind: LimitRange

ResourceQuota und LimitRange aus Anwendungs-Repos auszuschließen, ist ein Muster, das funktioniert: Plattform besitzt Quota-Objekte; Anwendungen besitzen Deployments, Services, Routes und ConfigMaps innerhalb des Rahmens.

AppProject mit derselben Ernsthaftigkeit prüfen wie Cluster-RBAC. Ein falsch konfiguriertes Project kann cluster-scoped Ressourcen synchronisieren oder in einen Nachbar-Namespace schreiben.

App-of-Apps und wo Vorsicht beginnt

Das App-of-Apps-Muster bootstrappt einen Baum von Applications aus einer Root-Application. Plattformteams lieben es für Cluster-Baseline-Add-ons. Anwendungsteams übernehmen es, um viele Microservices aus einem Repo zu verwalten.

Strukturbeispiel:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payments-root
  namespace: openshift-gitops
spec:
  project: team-payments
  source:
    repoURL: "https://github.com/example-org/payments-gitops.git"
    targetRevision: main
    path: apps
  destination:
    server: "https://kubernetes.default.svc"
    namespace: openshift-gitops
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Der Ordner apps enthält weitere Application-Manifeste — eines pro Service. Sauber in Diagrammen. Fragil in Produktion, wenn man es als Fire-and-Forget behandelt.

Vorsicht, die ich aus echten Clustern mitnehme:

Blast Radius. Ein schlechter Merge im Root-Repo betrifft jede Child-Application. CODEOWNERS auf dem Root-Pfad verlangen und bei Bedarf separate Repos für experimentelle Services.

Prune an der Root. Auto-Sync mit Prune an der App-of-Apps-Root hat Child-Applications gelöscht, als jemand einen Ordner umbenannt hat. Ich bevorzuge manuellen Sync an Roots, bis das Team mindestens einen Incident-Drill zu versehentlichem Prune durchlaufen hat.

Sync-Reihenfolge. App-of-Apps ersetzt keine Sync Waves. CRDs, Namespaces, Operatoren, dann Deployments — Reihenfolge zählt weiter. OpenShift ergänzt Routes und Zertifikate, die laut scheitern, wenn der Backend-Service noch nicht existiert.

Zirkuläre Ownership. Child-Application zeigt auf Git; Parent-Application lebt in Git; jemand patcht die Parent per kubectl im Incident — Drift an der Root ist schwer sichtbar.

Plattform-Applications gemischt mit Mandanten-Applications. Cluster-Baseline (Logging-Agenten, Monitoring-Hooks) in einer plattform-eigenen App-of-Apps mit anderem RBAC als Mandanten-Repos halten.

App-of-Apps ist ein Skalierungswerkzeug, kein Ersatz für Review. Mit einer flachen Application-Liste starten, bis der Schmerz den Baum rechtfertigt.

Sync-Policy versus Plattform-Constraints

Auto-Sync und Self-Heal klingen tugendhaft, bis sie OpenShift-Defaults bekämpfen.

SCC-Admission kann runAsUser, fsGroup ändern oder Capabilities streichen, die das Manifest setzt. Argo sieht Drift. Zurück synchronisieren kann loopen oder scheitern.

Cluster Network Operator und DNS besitzen Objekte, die Anwendungs-Repos nicht anfassen sollten. Aus Application-Quellen fernhalten.

Operator-verwaltete Operands — Service Mesh, Serverless, Custom Operatoren — fügen oft Labels, Annotations oder Sidecars hinzu. ignoreDifferences oder Server-Side-Apply-Optionen brauchen periodische Review, damit sie echte Probleme nicht verstecken.

Managed OpenShift kann Änderungen an sicherheitskritischen Ressourcen verbieten oder zurücksetzen. Sync klappt in Staging; scheitert oder driftet in Produktion mit undurchsichtiger Meldung.

Beispiel-Ignore-Regel für übliches Deployment-Mutations-Rauschen:

ignoreDifferences:
  - group: apps
    kind: Deployment
    jqPathExpressions:
      - .spec.template.metadata.annotations
  - group: route.openshift.io
    kind: Route
    jsonPointers:
      - /status

Git fixen, wenn der Live-Zustand stimmt — etwa wenn das Plattformteam einen Route-Host für einen Cutover gepatcht hat. Live fixen, wenn Git falsch war — etwa bei einem gemergten Image-Tag-Tippfehler.

Sync ist eine Produktionsänderung. Plattform-Constraints sind der Grund, warum manueller Sync in Produktion für viele Teams vernünftig bleibt, auch wenn Staging frei auto-synchronisiert.

Drift auf verwalteten und geteilten Clustern

Drift ist kein moralisches Versagen. Auf managed OpenShift ist er oft erwartet.

Incident-Hotfixes — Scale, ConfigMap-Patch, Route-Gewicht — landen vielleicht, bevor Git nachzieht.

HPA und Cluster Autoscaler — Replica- und Node-Zahlen weichen by design vom Manifest ab.

Sealed Secrets oder External Secrets — Git speichert verschlüsseltes oder referenziertes Material; live Secrets unterscheiden sich.

Plattform-Wartung — Node Drain, Operator-Upgrade, Zertifikatsrotation — ändert Status-Felder und manchmal Spec-Defaults.

Read-only Managed Policies — der Live-Cluster hält Felder, die die Application nicht schreiben darf; Argo bleibt dauerhaft OutOfSync, bis man ignoriert oder den Diff akzeptiert.

Fragen vor dem Klick auf Sync:

  • Hat Plattform oder SRE im Incident gepatcht?
  • Ist der Diff nur Metadata oder Status?
  • Startet Sync Pods während der Geschäftszeiten neu?
  • Entfernt Prune eine geteilte Ressource, von der ein anderes Team abhängt?
  • Ist OutOfSync, weil Git falsch ist oder weil OpenShift recht hat?

Auf ROSA und ARO auch fragen, ob der Diff ein Cloud-Provider-managed Objekt betrifft. Gegen diese Schleife zu kämpfen, kostet On-Call-Energie.

Wöchentliche Drift-Review schlägt stilles Auto-Heal: OutOfSync-Applications listen, Owner zuweisen, dokumentieren ob Git, Ignore oder Eskalation an die Plattform.

Routes, Secrets und Manifeste mit lokalem Wissen

OpenShift Routes sind für viele Teams first-class GitOps-Objekte:

apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: shop-api
  namespace: payments-prod
spec:
  host: shop-api.apps.cluster.example.com
  to:
    kind: Service
    name: shop-api
    weight: 100
  port:
    targetPort: http
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

Hosts und URLs quoten. Host-Naming mit Plattform-DNS-Mustern abstimmen, bevor gemergt wird.

Secrets gehören selten in plain Git. Sealed Secrets, External Secrets Operator oder Vault-Integration nutzen, die die Plattform dokumentiert — dann prüfen, dass das Secret im Namespace nach dem Sync existiert, nicht nur dass die Application Healthy ist.

RBAC und wer Produktion synchronisieren darf

OpenShift GitOps integriert Cluster-OAuth und Argo-CD-RBAC. Definieren, wer darf:

  • Applications in einem Namespace anlegen
  • Produktions-Projects synchronisieren
  • Sync-Optionen überschreiben
  • Applications mit Cascade Prune löschen

Wenn jeder mit Cluster-Login Produktion synchronisieren kann, ist Verify geschwächt. Plattformteams geben Entwicklern oft read-only Argo CD und beschränken Sync auf CI oder Release Engineers.

Break-Glass dokumentieren: wer darf im Ausfall oc apply nutzen, und SLA, Änderungen danach in Git nachzuziehen. Permanenter Drift ist Schulden.

Ein praktischer Rollout-Pfad

Was ich Teams auf OpenShift empfehle:

  1. OpenShift GitOps installieren oder erben — Instanz-URL, SSO und Upgrade-Owner klären.
  2. Ein Non-Prod-Namespace, eine Application, manueller Sync — Diff, Hooks und SCC-Mutationen ohne Kunden-Blast-Radius lernen.
  3. AppProject auf diesen Namespace gesperrt — Destinations erst erweitern, wenn Quota und Network Policy existieren.
  4. CI rendert Manifestekustomize build oder helm template in der Pipeline; gerendertes YAML an PRs hängen.
  5. Auto-Sync auf Staging — Produktion manuell oder semi-auto, bis Prune-Verhalten verstanden ist.
  6. App-of-Apps erst, wenn Application-Anzahl weh tut — nicht am ersten Tag.
  7. Drift-Report wöchentlich — OutOfSync mit Ownern; ignoreDifferences planmäßig tunen, nicht ewig reaktiv.

Kein Silberkugel. Iteration schlägt ein Big-Bang-„alles durch Git“-Mandat aus einer Folie.

Observability jenseits von Healthy in der UI

Argo-CD-Health ist nötig, nicht hinreichend.

Deployment-Erfolgsrate, Error-Budget-Burn und Sättigung während Sync-Fenstern beobachten. Eine Application kann Healthy sein, während die Route auf Pods zeigt, die nach einer ConfigMap-Änderung Readiness-Checks failen.

Merges im GitOps-Repo mit Incident-Zeitstempeln korrelieren. Wenn jede Page einem Plattform-Application-Sync folgt, ist Review-Tiefe — nicht Tool-Marke — das Thema.

Abschluss

GitOps auf OpenShift funktioniert, wenn Teams beide Hälften der Gleichung respektieren: Git als Absicht und die Plattform als Partner mit eigenen Operatoren, Constraints und Upgrade-Kalender.

Den OpenShift-GitOps-Operator als unterstützte Infrastruktur nutzen. App-of-Apps als Skalierungsmuster mit echten Prune- und Ordering-Risiken behandeln. Sync-Policies wählen, die Produktionsurteil widerspiegeln, nicht nur Staging-Tempo. Drift auf managed Clustern als Signal behandeln — prüfen, dann Git oder Live mit offenen Augen fixen.

Wer heute Argo CD auf OCP betreibt, eine Produktions-Application nehmen und den letzten Sync durchgehen: Was hätte Prune entfernt, wenn es aktiv wäre? Was ist gerade OutOfSync, und sagt OpenShift etwas, das Git lernen sollte? Fünf Minuten Verify schlagen eine weitere Stunde Vertrauen ins grüne Icon.