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:
- OpenShift GitOps installieren oder erben — Instanz-URL, SSO und Upgrade-Owner klären.
- Ein Non-Prod-Namespace, eine Application, manueller Sync — Diff, Hooks und SCC-Mutationen ohne Kunden-Blast-Radius lernen.
- AppProject auf diesen Namespace gesperrt — Destinations erst erweitern, wenn Quota und Network Policy existieren.
- CI rendert Manifeste —
kustomize buildoderhelm templatein der Pipeline; gerendertes YAML an PRs hängen. - Auto-Sync auf Staging — Produktion manuell oder semi-auto, bis Prune-Verhalten verstanden ist.
- App-of-Apps erst, wenn Application-Anzahl weh tut — nicht am ersten Tag.
- Drift-Report wöchentlich — OutOfSync mit Ownern;
ignoreDifferencesplanmäß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.