Voraussetzung: oc login
als cluster-admin für Schritt 2, ein öffentlich lesbares
Git-Repo (oder eines mit hinterlegten Zugangsdaten) für Schritt 3.
Schritt 6 wird nur durchgegangen, nicht ausgeführt – ein
echter sno.ps1 destroy gehört nicht in eine Lernanleitung
gegen einen laufenden Cluster.
1 Git ist das Soll, der Cluster das Ist
Argo CD vergleicht dauernd, was in Git steht, mit dem, was im Cluster läuft – und gleicht ab.
Argo CD vergleicht dauernd, was in Git steht (das Soll) mit dem, was im Cluster läuft (das Ist), zeigt die Abweichung und gleicht ab – von Hand oder automatisch. Mit selfHeal schließt es die Lücke, die Helm lässt (B12): eine manuelle Änderung am Cluster wird zurückgedreht. Eine Application zeigt auf {Repo, Pfad} und {Cluster, Namespace}.
Application verbindet {Repo, Pfad, Revision} mit {Cluster, Namespace}. Argo CD vergleicht beide dauernd, zeigt OutOfSync plus einen Diff und synct – von Hand oder automated. prune löscht, was aus Git verschwindet, selfHeal dreht manuelle Cluster-Änderungen zurück – genau die Drift-Lücke, die Helm lässt (B12). sync-wave ordnet die Reihenfolge, App-of-Apps macht eine Wurzel-App zum Einstiegspunkt für den ganzen Cluster.- eine
Application(ein CR) zeigt auf {Repo + Pfad + Revision} und {Ziel-Cluster + Namespace} - Argo CD vergleicht dauernd Git (Soll) mit dem Cluster (Ist) – Status
Synced/OutOfSyncplus ein Diff je Resource - Sync:
manual(du drückst den Knopf) oderautomated automated.prune: aus Git gelöscht → auch im Cluster wegautomated.selfHeal: eine manuelle Cluster-Änderung wird zurückgedreht – die Drift-Lücke von Helm (B12) ist damit zu- Pull-Modell: Argo läuft im Ziel-Cluster und zieht aus Git – kein Push von außen (E1), kein Deploy-Token nach draußen
- Argo rendert Helm/Kustomize selbst und macht dann
apply– keinhelmim Spiel - Argo darf nur, was seine SA erlaubt (A5) – sonst bleibt die App
OutOfSyncmit einem RBAC-Fehler sync-wave-Annotation ordnet: Namespace (-2) < CRD (-1) < Config (0) < Workload (1)- App-of-Apps: eine Wurzel-
Application, deren Pfad weitere Application-Manifeste enthält – ein Einstiegspunkt für den ganzen Cluster ApplicationSet: erzeugt Applications aus einer Vorlage – pro Cluster, pro Umgebung, pro Verzeichnis- Resource-Hooks (
PreSync/PostSync): DB-Migration, wie die Helm-Hooks (B12) - Werkzeuge:
argocd app sync,argocd app diff, die Argo-UI als Route, der OpenShift-GitOps-Operator (Argo + Dex + OpenShift-Login) - Bibliothek: eine Wurzel-App, je Dienst eine
Applicationauf das Helm-Chart – Jenkins baut nur noch das Image, den Rest zieht Argo
Anders als beim Push-Modell aus
der Build-Kette
läuft Argo CD im Ziel-Cluster und zieht – kein
Deploy-Token, das nach außen gegeben werden muss. automated.selfHeal
schließt genau die Drift-Lücke, die helm upgrade offen lässt: eine
manuelle Cluster-Änderung wird zurückgedreht.
2 Den GitOps-Operator installieren
OpenShift GitOps ist Argo CD als Operator – Argo, Dex und eine
fertige openshift-gitops-Instanz.
oc get csv -A | grep -i gitops # schon installiert? cat <<'EOF' | oc apply -f - apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: openshift-gitops-operator namespace: openshift-operators spec: channel: latest name: openshift-gitops-operator source: redhat-operators sourceNamespace: openshift-marketplace EOF oc get csv -n openshift-operators -w oc get route -n openshift-gitops # die Argo-UI, sobald bereit oc get pods -n openshift-gitops
Der Operator legt sofort den Namespace openshift-gitops mit einer
lauffähigen Argo-CD-Instanz an – kein separates Bootstrapping nötig, wie es
Community-Argo-CD bräuchte.
3 Eine Application anlegen
Eine Application zeigt auf {Repo, Pfad} und {Cluster,
Namespace} – das eine Objekt, das alles verbindet.
cat <<'EOF' | oc apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo-gitops
namespace: openshift-gitops
spec:
project: default
source:
repoURL: https://github.com/openshift/origin.git
targetRevision: HEAD
path: examples/hello-openshift
destination:
server: https://kubernetes.default.svc
namespace: library
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=false
EOF
oc get application demo-gitops -n openshift-gitops
oc get application demo-gitops -n openshift-gitops -o jsonpath='{.status.sync.status} {.status.health.status}{"\n"}'
Das Beispiel-Repo dient nur der Demonstration – für ein echtes Projekt zeigt
source.path auf den eigenen Helm-Chart-Ordner
(helm-am-bibliothek-chart.html).
syncPolicy.automated ohne weitere Angabe reicht für prune
und selfHeal – ohne sie bleibt der Sync manuell, per
argocd app sync demo-gitops oder Klick in der UI.
4 Drift erzeugen und selfHeal zusehen
Eine manuelle Cluster-Änderung, die Argo zurückdreht – live beobachtet.
# den von Argo verwalteten Deployment manuell veraendern: oc scale deployment/hello-openshift -n library --replicas=0 # sofort danach: Argo sieht die Abweichung oc get application demo-gitops -n openshift-gitops -w # Status kurz OutOfSync, dann durch selfHeal wieder Synced # der Beweis: die Replica-Zahl ist zurueckgedreht, nicht bei 0 geblieben oc get deployment/hello-openshift -n library
Das ist der PunktOhne
selfHeal hätte oc scale bis zum nächsten
manuellen Sync überlebt – genau die Lücke, die helm upgrade
(B12) offen lässt, weil Helm nach dem Deploy nicht mehr hinschaut.
Wer die andere Richtung sehen will: eine Zeile im Git-Repo ändern und pushen – Argo zieht sie innerhalb des Poll-Intervalls (Standard 3 Minuten, oder sofort per Webhook) automatisch in den Cluster.
5 Ein Secret nicht im Klartext versionieren
Ein Klartext-Secret-YAML in Git ist base64, also lesbar.
Sealed Secrets verschlüsselt mit dem Public Key eines in-cluster Controllers.
Ein Klartext-Secret in Git ist base64 – also lesbar. In GitOps (E3) braucht es einen Umweg. Drei Wege: Sealed Secrets (Key im Cluster), External Secrets Operator (Store außerhalb), SOPS (Key beim Deployer). Die Secret-Maschinerie muss vor den Workloads syncen – Sync-Wave -1.
kubeseal verschlüsselt mit dem Public Key des in-cluster Controllers → das SealedSecret ist sicher in Git → der Controller entschlüsselt es im Cluster. External Secrets Operator: ein ExternalSecret referenziert einen Key in Vault / AWS SM / Azure KV → der Operator synct ihn in ein Secret, der Wert berührt Git nie. SOPS: eine verschlüsselte Werte-Datei in Git, ein GitOps-Plugin entschlüsselt beim Rendern. Die Secret-Maschinerie muss in einer frühen Sync-Wave laufen, sonst starten die Workloads ohne ihr Secret.- ein Klartext-
Secret-YAML in Git ist base64, also lesbar – jeder mit Repo-Zugriff hat die Zugangsdaten (B10) - Sealed Secrets:
kubesealverschlüsselt mit dem Public Key des in-cluster Controllers → einSealedSecret-CR ist sicher zu committen - der Sealed-Secrets-Controller entschlüsselt im Cluster in ein echtes
Secret– der Key liegt im Cluster, der Wert nur verschlüsselt in Git - ein
SealedSecretist Namespace-gebunden (strict) – nicht in einen anderen NS kopierbar - der Sealing-Key ist clusterspezifisch: neuer Cluster → neu sealen, oder den Key sichern (die DR-Sorge aus E4)
- External Secrets Operator: ein
ExternalSecret-CR referenziert einen Key-Pfad in Vault / AWS SM / Azure KV / GCP SM - der Operator holt den Wert und schreibt ein echtes
Secret, aktualisiert in einemrefreshInterval– der Wert berührt Git nie - ESO braucht einen
SecretStore/ClusterSecretStoremit Zugang zum Backend – der Store-Zugang ist selbst ein Bootstrap-Secret - SOPS (+ age / PGP): eine Werte-Datei wird verschlüsselt (nur die Werte), liegt so in Git, ein GitOps-Plugin entschlüsselt beim Rendern
- bei SOPS liegt der Key beim Deployer (age-Key als Cluster-Secret oder eine KMS-Rolle) – passt zu jedem GitOps-Tool, kein eigener Controller
- Sync-Wave-Reihenfolge: der Secret-Controller in Wave -2, die
SealedSecret/ExternalSecret-CRs in -1, die Workloads in 0+ (E3) - Entscheidung: Sealed Secrets ist am einfachsten (Key im Cluster, DR-Risiko am Key), ESO ist eine Quelle außerhalb (mehr Teile, Cluster wegwerfbar), SOPS ist tool-unabhängig (Key-Verwaltung bei dir)
- Bibliothek: bis der GitOps-Teil steht, Secrets von Hand mit
oc create secret– danach Sealed Secrets im selben Repo wie die Charts - OCP-Unterschied: keiner – alle drei sind Community-Werkzeuge, OpenShift GitOps liefert nur Argo CD, nicht die Secret-Schicht
# Sealed-Secrets-Controller installieren (Community-Operator oder manuell): oc apply -f https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/controller.yaml # ein normales Secret NUR lokal erzeugen, nie committen: oc create secret generic demo-api-key -n library \ --dry-run=client --from-literal=key=ein-echtes-geheimnis -o yaml > secret-lokal.yaml # mit kubeseal in ein committierbares SealedSecret umwandeln: kubeseal --format=yaml < secret-lokal.yaml > sealed-secret.yaml rm secret-lokal.yaml # die Klartextdatei nie behalten cat sealed-secret.yaml # das hier ist sicher zu committen - verschluesselt oc apply -f sealed-secret.yaml # der Controller im Cluster entschluesselt automatisch: oc get secret demo-api-key -n library
sealed-secret.yaml ist nur für diesen einen Cluster
entschlüsselbar – der Public Key des Controllers ist clusterspezifisch. Deshalb
gehört der private Sealing-Key auf die Backup-Liste, sonst sind alle SealedSecrets
nach einem Rebuild (Schritt 6) unlesbar.
6 Der Rebuild-Ablauf als Checkliste
Wenn alles in Git steht, ist ein Cluster wegwerfbar – hier nur durchgegangen, nicht ausgeführt.
Wenn alles in Git steht, ist ein Cluster wegwerfbar. Die Übung: zerstören, neu installieren, Argo CD bootstrappen – und Argo zieht den ganzen Rest aus Git zurück. Was nicht in Git liegt (Klartext-Secrets, PVC-Daten, Installer-Config) muss separat gelöst sein. „Wiederhergestellt“ heißt: Workloads ja, Daten nur bei separatem Backup.
sno.ps1 destroy → sno.ps1 install → den GitOps-Operator und die Wurzel-Application anwenden → Argo zieht Namespaces, RBAC, Workloads, Operator-Subscriptions und die Argo-Apps selbst aus Git zurück. Was nicht in Git liegt, muss separat gelöst sein: Klartext-Secrets (Sealed Secrets / ESO, frühe Sync-Wave), PVC-Daten (ein echtes Backup, Velero / Snapshots), die Installer-Config. GitOps rettet die Struktur, nicht den Inhalt.- der Ablauf:
sno.ps1 destroy→sno.ps1 install→ Argo CD bootstrappen (Operator + Wurzel-Application) → Argo zieht alles aus Git - was in Git liegt und automatisch zurückkommt: Namespaces, RBAC, NetworkPolicy, Deployments/Services/Routes, ConfigMaps, HPAs
- auch in Git: Helm-Werte-Overrides je Umgebung, Operator-
SubscriptionsundOperatorGroups(D2), die Custom Resources der Operatoren (D5) - und in Git: die Argo-
Application-Objekte selbst (App-of-Apps), sowie die Cluster-Config als Manifeste (Ingress, OAuth-IdP, image-registry) - Reihenfolge per
sync-wave: die Secrets-Maschinerie in Wave -1, Config in 0, Workloads in 1 - was nicht in Git liegt – Klartext-Secrets: über Sealed Secrets in Git (Controller entschlüsselt) oder ESO aus einem externen Store, muss in Wave -1 (E5)
- bei Sealed Secrets ist der Sealing-Key clusterspezifisch – er muss separat gesichert werden, sonst sind die Secrets nach dem Rebuild unlesbar
- PVC-Daten (DB-Inhalt, Uploads) brauchen ein echtes Backup: Velero oder Volume-Snapshots (G3) – ein Rebuild stellt sie nicht her
- Installer-Config (
install-config.yaml, das echte Pull-Secret, die reservierte IP) liegt beisno.ps1, nicht im App-Repo - etcd-Backup (G7) ist die andere DR-Strategie: den Cluster-Zustand wiederherstellen statt neu bauen – für Multi-Node, nicht für den SNO-Wegwerf
- der zurückgestellte
--rebuild-Flag im cloud-host-Skript ist genau diese fünf Schritte in einem Kommando – ab Block E baubar - Time-to-green: SNO-Install ~35 min, Argo-Sync ~10 min, Operatoren-Reconcile ~10 min – grob eine Stunde
- „Wiederhergestellt“ heißt ehrlich: Workloads ja, Daten nur bei separatem Backup – GitOps rettet die Struktur, nicht den Inhalt
- OCP-Unterschied: identisch – das Muster ist Argo CD plus Infrastructure-as-Code, nur der Installer-Teil unterscheidet sich
sno.ps1 destroy– Server, Snapshot, IP, Firewall wegsno.ps1 install– frischer OKD-Single-Node, ~35 Minuten- GitOps-Operator installieren (Schritt 2) und die Wurzel-
Applicationanwenden (oc apply -f bootstrap/) - Argo zieht Namespaces, RBAC, Workloads, Operator-
Subscriptionsund die Argo-Application-Objekte selbst aus Git zurück - warten bis alles
SyncedundHealthyist – Argo-Sync grob 10 Minuten, Operatoren-Reconcile grob 10 Minuten
Was dabei NICHT zurückkommt
Klartext-Secrets (deshalb Schritt 5), PVC-Daten (DB-Inhalt, Uploads – braucht ein
echtes Backup wie Velero oder Volume-Snapshots), die Installer-Config
(install-config.yaml, das echte Pull-Secret, die reservierte
IP liegen bei sno.ps1, nicht im App-Repo).
„Wiederhergestellt“ heißt ehrlich: Workloads ja, Daten
nur bei separatem Backup – GitOps rettet die Struktur, nicht den Inhalt. Der
zurückgestellte --rebuild-Flag im cloud-host-Skript wäre genau diese
fünf Schritte in einem Kommando.
7 Typische Fallen
| Symptom | meist |
|---|---|
Application bleibt OutOfSync mit RBAC-Fehler | die Argo-SA darf das Ziel-Objekt nicht anfassen (A5) – Rolle fehlt oder falscher Namespace |
selfHeal greift nicht | syncPolicy.automated.selfHeal fehlt in der Application – ohne sie bleibt Drift stehen bis zum manuellen Sync |
| SealedSecret entschlüsselt nicht | auf einem anderen Cluster erzeugt (anderer Public Key) – neu sealen mit dem Key dieses Clusters |
| nach Rebuild sind alle SealedSecrets unlesbar | der private Sealing-Key war nicht gesichert – auf einem neuen Cluster entsteht ein neuer, alte SealedSecrets passen nicht mehr |
| Workload startet ohne sein Secret | Sync-Wave falsch – die Secret-Maschinerie muss vor den Workloads syncen (Wave -1 vs 0+) |
8 Wie das Projekt es macht
Aktuell kein Argo CD im Einsatz – die Bibliothek deployt per
helm upgrade --install aus Jenkins
(build-kette-openshift.html),
Secrets werden von Hand mit oc create secret angelegt
(secrets-in-openshift.html).
Diese Anleitung zeigt das Alternativmuster, das der Atlas als nächsten Ausbau nennt:
eine Wurzel-App, je Dienst eine Application auf das Helm-Chart –
Jenkins baut dann nur noch das Image, den Rest zieht Argo. Die SNO-API ist nur übers
Tailnet erreichbar, das gilt für Argo CD (im Cluster) nicht – nur für den
Zugriff auf den Cluster von außen.