Argo CD Multi-Cluster-GitOps: Architektur für viele Cluster
- Argo CD
- GitOps
- Multi-Cluster
- Kubernetes
- Platform Engineering
Sobald mehr als eine Handvoll Kubernetes-Cluster und mehrere Teams im Spiel sind, wird GitOps mit Argo CD zur Architekturfrage. Zu klären ist, wo Argo CD läuft, wer in welche Cluster deployen darf und wie das Repository geschnitten ist. Vor allem muss die Architektur verhindern, dass ein fehlerhafter Commit zwanzig Cluster gleichzeitig trifft.
Dieser Artikel richtet sich an Plattform-Teams, die Argo CD für viele Cluster und Fachteams aufbauen oder ein gewachsenes Setup aufräumen wollen. Die Beispiele beziehen sich auf Argo CD 3.5 (aktuell v3.5.3, v3.6 ist als Release Candidate verfügbar; Stand: Oktober 2026).
Topologien: zentral, pro Cluster oder Hub-and-Spoke
Zentrales Argo CD
Eine Argo-CD-Instanz auf einem Management-Cluster verwaltet alle Workload-Cluster. Die Cluster werden als Secrets mit dem Label argocd.argoproj.io/secret-type: cluster registriert, der Application Controller spricht direkt mit deren Kubernetes-APIs.
Der Vorteil ist eine zentrale Oberfläche mit einem RBAC-Modell und einer Stelle für ApplicationSets. Der Nachteil ist ebenso klar: Die Zentrale braucht Netzzugriff auf jede Cluster-API und hält Credentials für alle Cluster. Fällt der Management-Cluster aus, laufen die Workloads weiter, aber es gibt keine Syncs und keine Drift-Korrektur.
Argo CD pro Cluster
Jeder Cluster bekommt eine eigene Instanz, die nur sich selbst verwaltet. Das ist die einfachste Variante für strikt getrennte Netze und reduziert den Blast Radius auf einen Cluster. Mit wachsender Clusterzahl wird der Overhead spürbar: Upgrades, SSO-Anbindung, RBAC und Monitoring müssen pro Instanz gepflegt werden, und es gibt keinen Gesamtüberblick.
Hub-and-Spoke mit argocd-agent
Das Projekt argocd-agent unter argoproj-labs dreht das Modell um. Application Controller, Repo-Server und Redis laufen auf den Workload-Clustern, ein Agent baut von dort eine ausgehende gRPC-Verbindung zum Principal auf dem Control-Plane-Cluster auf. Dort laufen UI, API und SSO. Die Verbindung ist bidirektional, wird aber ausschließlich vom Agent initiiert.
Es gibt zwei Modi. Im Managed-Modus ist der Control-Plane-Cluster die Quelle der Wahrheit für Applications, im Autonomous-Modus definiert der Workload-Cluster seine Applications selbst und meldet nur Status zurück. Beide lassen sich mischen. Bricht die Verbindung ab, reconcilen die Workload-Cluster lokal weiter.
Stand Oktober 2026 ist v0.10.0 aktuell (August 2026), unter anderem mit SPIFFE/SPIRE-Integration für mTLS. Die Dokumentation beschreibt das Projekt als aktiv entwickelt und noch nicht feature-complete. Ich würde es dort einsetzen, wo die Netzanforderungen ein Pull-Modell erzwingen, und dann mit einem klaren Upgrade-Plan.
Meine Empfehlung
Für die meisten Organisationen ist ein zentrales Argo CD pro Sicherheitszone der richtige Schnitt: eine Instanz für Entwicklung und Test, eine eigene für Produktion, gegebenenfalls getrennt nach Standort oder Netzsegment. So bleibt die Zahl der Instanzen klein, und Produktions-Credentials liegen nicht im selben System wie die Dev-Cluster.
Cluster als Daten: Labels statt Listen
Die wichtigste Entscheidung für Skalierung ist, Cluster über Labels zu beschreiben. Das Cluster-Secret trägt Stage, Standort und Fähigkeiten, ApplicationSets wählen darüber aus.
apiVersion: v1
kind: Secret
metadata:
name: prod-site-a
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
stage: prod
site: site-a
gpu: "true"
type: Opaque
stringData:
name: prod-site-a
server: https://prod-site-a.k8s.example.com:6443
config: |
{
"bearerToken": "<token>",
"tlsClientConfig": {
"caData": "<base64-ca>"
}
}
Ein neuer Cluster bekommt seine Add-ons dann allein dadurch, dass sein Secret mit den richtigen Labels angelegt wird. Das Secret selbst sollte nicht im Klartext in Git liegen, sondern über External Secrets aus Vault kommen (siehe unten).
ApplicationSets für Plattform-Add-ons
Für Add-ons wie cert-manager, Ingress-Controller oder Monitoring-Agents ist der Cluster-Generator das passende Werkzeug. Das folgende ApplicationSet rollt cert-manager auf alle Cluster aus und wählt das Kustomize-Overlay anhand des Stage-Labels.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: addon-cert-manager
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- clusters:
selector:
matchLabels:
argocd.argoproj.io/secret-type: cluster
matchExpressions:
- key: stage
operator: In
values: ["dev", "test", "prod"]
syncPolicy:
applicationsSync: create-update
preserveResourcesOnDeletion: true
template:
metadata:
name: 'cert-manager-{{.nameNormalized}}'
labels:
stage: '{{index .metadata.labels "stage"}}'
spec:
project: platform
source:
repoURL: https://git.example.com/platform/gitops.git
targetRevision: main
path: 'addons/cert-manager/overlays/{{index .metadata.labels "stage"}}'
destination:
server: '{{.server}}'
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
Zwei Details sind hier bewusst gesetzt. Der Selektor auf argocd.argoproj.io/secret-type schließt den lokalen In-Cluster-Eintrag aus, der kein solches Secret hat. Und applicationsSync: create-update zusammen mit preserveResourcesOnDeletion: true verhindert, dass ein versehentlich entferntes Label oder ein gelöschtes ApplicationSet Applications samt Ressourcen auf allen Clustern abräumt. Wer bewusst löschen will, tut das gezielt.
Repository-Struktur
Ob Mono-Repo oder Multi-Repo ist weniger eine Glaubensfrage als eine Frage der Zuständigkeit. Bewährt hat sich ein Plattform-Repository, das dem Plattformteam gehört, und je ein Deploy-Repository pro Fachteam.
platform-gitops/
├── bootstrap/ # Root-Application, AppProjects, ApplicationSets
├── clusters/ # ExternalSecrets für Cluster-Secrets
└── addons/
└── cert-manager/
├── base/
└── overlays/
├── dev/
├── test/
└── prod/
team-a-deploy/
└── apps/
└── orders/
├── base/
└── stages/
├── dev/ # config.yaml + kustomization.yaml
├── test/
└── prod/
Stages werden als Verzeichnisse modelliert, nicht als Branches. Branch-pro-Stage führt zu Merge-Konflikten, unklaren Diffs und Cherry-Picks, die niemand mehr nachvollziehen kann. Mit Verzeichnissen ist der Unterschied zwischen Test und Produktion ein normaler Diff im selben Commit.
Kustomize eignet sich für eigene Anwendungen mit kleinen Stage-Unterschieden. Helm ist sinnvoll für Third-Party-Charts. Beides lässt sich kombinieren: Ein Chart in einer festen Version, die Values pro Stage im Repo, eingebunden über sources mit ref. Argo CD 3.5 rendert Helm-Charts mit Helm 4. Laut Upgrade-Notes müssen OCI-Repositories ohne TLS deshalb explizit als Plain-HTTP konfiguriert werden.
Self-Service für Fachteams
AppProjects als Leitplanke
Jedes Fachteam bekommt ein AppProject. Es legt fest, aus welchen Repositories das Team deployen darf, in welche Cluster und Namespaces, und welche Ressourcentypen erlaubt sind.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: team-a
namespace: argocd
spec:
description: Deployments von Team A
sourceRepos:
- https://git.example.com/team-a/*
destinations:
- name: 'dev-*'
namespace: 'team-a-*'
- name: 'test-*'
namespace: 'team-a-*'
- name: 'prod-*'
namespace: 'team-a-*'
clusterResourceWhitelist: []
namespaceResourceBlacklist:
- group: ''
kind: ResourceQuota
- group: ''
kind: LimitRange
- group: networking.k8s.io
kind: NetworkPolicy
destinationServiceAccounts:
- server: https://prod-site-a.k8s.example.com:6443
namespace: 'team-a-*'
defaultServiceAccount: argocd-deployer
- server: https://dev-site-a.k8s.example.com:6443
namespace: 'team-a-*'
defaultServiceAccount: argocd-deployer
roles:
- name: developer
groups:
- team-a-devs
policies:
- p, proj:team-a:developer, applications, get, team-a/*, allow
- p, proj:team-a:developer, applications, sync, team-a/*, allow
syncWindows:
- kind: deny
schedule: '0 18 * * 5'
duration: 62h
clusters:
- 'prod-*'
manualSync: true
timeZone: Europe/Berlin
description: Kein automatisches Deployment in Produktion am Wochenende
Die leere clusterResourceWhitelist verbietet cluster-weite Ressourcen. Namespaces, Quotas und NetworkPolicies legt das Plattformteam an, nicht das Fachteam. Das Sync-Window blockiert automatische Syncs in Produktion übers Wochenende, erlaubt aber manuelle Syncs für Hotfixes.
Impersonation
destinationServiceAccounts greift nur, wenn Impersonation in argocd-cm mit application.sync.impersonation.enabled: "true" aktiviert ist. Das Feature ist seit Argo CD 3.5 Beta und gilt seitdem nicht nur für Syncs, sondern auch für Löschungen, Logs und Resource Actions. Argo CD agiert dann im Ziel-Namespace mit den Rechten des Service Accounts argocd-deployer, den das Plattformteam mit einer namespace-gebundenen Role ausstattet. Damit begrenzt Kubernetes-RBAC zusätzlich zum AppProject, was ein Team anrichten kann.
ApplicationSets für Teams
Fachteams sollten keine ApplicationSets selbst anlegen. Die Argo-CD-Doku ist da deutlich: ApplicationSets können Applications in beliebigen Projekten erzeugen, deshalb gehört das Anlegen in Admin-Hand, und ein templatiertes project-Feld ist ein Sicherheitsrisiko. Das Plattformteam legt pro Team ein ApplicationSet mit Git-Files-Generator an und trägt das Projekt fest ein.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: team-a-apps
namespace: argocd
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- git:
repoURL: https://git.example.com/team-a/team-a-deploy.git
revision: main
files:
- path: "apps/*/stages/*/config.yaml"
template:
metadata:
name: 'team-a-{{index .path.segments 1}}-{{.path.basename}}'
labels:
team: team-a
stage: '{{.path.basename}}'
spec:
project: team-a
source:
repoURL: https://git.example.com/team-a/team-a-deploy.git
targetRevision: main
path: '{{.path.path}}'
destination:
name: '{{.cluster}}'
namespace: '{{.namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
Die config.yaml im Stage-Verzeichnis enthält nur cluster und namespace. Gibt ein Team dort einen fremden Namespace an, verweigert Argo CD den Sync, weil das Ziel im AppProject nicht erlaubt ist. So entsteht Self-Service, ohne dass das Team Argo-CD-Objekte schreibt. Nach diesem Prinzip habe ich Git-Self-Service für mehrere Teams umgesetzt: Eine neue Anwendung ist dann ein Pull Request im Team-Repository, kein Ticket beim Plattformteam.
Promotion zwischen Stages
Promotion heißt in diesem Modell: Die Änderung, die in Test funktioniert, wird per Pull Request in das Prod-Verzeichnis übernommen, typischerweise ein neuer Image-Tag in der kustomization.yaml. CI-Pipelines können diesen PR automatisch öffnen, die Freigabe bleibt ein Review. Für viele Teams reicht das völlig.
Für Rollouts über viele Cluster einer Stage bietet Argo CD Progressive Syncs. Mit strategy.type: RollingSync synchronisiert das ApplicationSet seine Applications in Schritten nach Labels, etwa erst Standort A, dann Standort B. Das Feature ist Beta, wird über applicationsetcontroller.enable.progressive.syncs aktiviert und schaltet Auto-Sync für die erzeugten Applications ab.
spec:
strategy:
type: RollingSync
rollingSync:
steps:
- matchExpressions:
- key: site
operator: In
values: ["site-a"]
- matchExpressions:
- key: site
operator: In
values: ["site-b"]
maxUpdate: 50%
Wer komplexere Promotion-Ketten mit Freigaben und Verifikation braucht, sollte sich Kargo ansehen, das auf Argo CD aufsetzt. Ich würde es erst einführen, wenn der PR-basierte Weg nachweislich nicht mehr reicht.
Secrets
Die Argo-CD-Doku rät ausdrücklich davon ab, Secrets bei der Manifest-Generierung per Plugin einzuspielen, unter anderem weil Argo CD gerenderte Manifeste im Klartext im Redis-Cache hält. Empfohlen wird, Secrets im Ziel-Cluster durch einen Operator zu erzeugen.
In der Praxis ist das meist External Secrets Operator mit Vault. In Git liegt nur die Referenz:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: orders-db
namespace: team-a-orders
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-team-a
kind: SecretStore
target:
name: orders-db
data:
- secretKey: password
remoteRef:
key: team-a/orders/db
property: password
Pro Cluster sollte es in Vault einen eigenen Kubernetes-Auth-Mount geben, pro Team eine eigene Rolle und Policy. Dann kann ein kompromittierter Dev-Cluster keine Prod-Secrets lesen. Sealed Secrets ist eine Alternative für kleinere Umgebungen ohne Vault. Bei vielen Clustern wird die Verwaltung der Schlüsselpaare pro Cluster und deren Sicherung aber schnell zur eigenen Aufgabe.
Skalierung
Ab einigen hundert Applications ist der Application Controller der Engpass. Er lässt sich als StatefulSet mit mehreren Replicas betreiben, ARGOCD_CONTROLLER_REPLICAS muss der Replica-Zahl entsprechen. Verteilt wird pro Cluster, nicht pro Application. Ein einzelner sehr großer Cluster bleibt also auf einem Shard.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
namespace: argocd
data:
controller.sharding.algorithm: consistent-hashing
controller.status.processors: "50"
controller.operation.processors: "25"
reposerver.parallelism.limit: "10"
Die Algorithmen round-robin und consistent-hashing sind laut Feature-Maturity-Seite Alpha, legacy verteilt nach UID und oft ungleichmäßig. Einzelne Cluster lassen sich über das Feld shard im Cluster-Secret fest zuordnen. Die Processor-Werte 50 und 25 entsprechen der Doku-Empfehlung für etwa 1.000 Applications. Das Parallelism-Limit am Repo-Server verhindert OOM-Kills bei vielen gleichzeitigen Helm-Renderings. Git-Webhooks statt Polling entlasten Repo-Server und Git-Server gleichermaßen.
Sicherheit der Cluster-Credentials
argocd cluster add legt im Ziel-Cluster einen Service Account mit cluster-weiten Vollrechten an. Für Produktion ist das zu viel. Besser ist ein eigener Service Account mit einer ClusterRole, die genau die Ressourcen erlaubt, die Argo CD dort verwalten soll, und eine Rotation des Tokens über Vault und External Secrets. Wo möglich, sind kurzlebige Credentials über execProviderConfig oder Cloud-IAM-Anbindungen langlebigen Bearer-Tokens vorzuziehen.
Der Management-Cluster selbst ist das wertvollste Ziel der Plattform. Er gehört in ein eigenes Netzsegment, Admin-Zugriff nur über SSO-Gruppen, die lokale Admin-Rolle deaktiviert. Argo CD 3.5 bringt zudem optionales mTLS zwischen den internen Komponenten und Signaturprüfung von Git-Commits über das neue Source-Integrity-Subsystem.
Betrieb in abgeschotteten Netzen
In abgeschotteten Umgebungen, die ich betreut habe, galt immer: Jede externe Abhängigkeit muss intern gespiegelt werden. Git-Server, Helm-Charts als OCI-Artefakte in einer internen Registry, Container-Images und die Argo-CD-Images selbst. Der Repo-Server darf keine Charts aus dem Internet nachladen, Helm-Dependencies müssen vorher vendored oder gespiegelt sein.
Eine Trennung nach Standorten oder Netzsegmenten ist ein Argument für eine Instanz pro Standort oder für das Agent-Modell, bei dem nur ausgehende Verbindungen von den Workload-Clustern nötig sind. Mehr zu den Grundlagen steht im Artikel zu Kubernetes im Air-Gap, zur Kombination mit Crossplane in Crossplane als Plattform-API.
Typische Fallstricke
Sync-Waves und App-of-Apps
Sync-Waves über argocd.argoproj.io/sync-wave ordnen Ressourcen innerhalb einer Application. Bei App-of-Apps wartet die Root-Application aber standardmäßig nicht auf die Health der Kind-Applications, weil Argo CD seit Version 1.8 keinen eingebauten Health-Check für Application-Ressourcen mehr hat. Wer Reihenfolgen über Applications hinweg braucht, etwa CRDs vor Operatoren, muss eine Health-Customization für argoproj.io/Application in argocd-cm hinterlegen. Für CRs, deren CRD erst in derselben Sync-Operation entsteht, hilft SkipDryRunOnMissingResource=true.
Ressourcen-Drift
HPAs, Mutating Webhooks und Operatoren ändern Felder, die auch im Git stehen. Ergebnis sind dauerhaft “OutOfSync”-Applications oder Self-Heal-Schleifen. Abhilfe schaffen gezielte ignoreDifferences mit RespectIgnoreDifferences=true und Server-Side Apply. Pauschale Ignore-Regeln auf ganze Kinds verdecken dagegen echte Drift.
App-of-Apps-Wildwuchs
Verschachtelte App-of-Apps über drei oder vier Ebenen sind kaum noch nachvollziehbar. Ich halte es bei einer Root-Application, die AppProjects und ApplicationSets ausrollt. Alles darunter erzeugen ApplicationSets aus Daten, nicht handgeschriebene Application-Manifeste.
Git-Generator als Lastquelle
Ein Team mit Schreibrechten auf ein Generator-Repo kann versehentlich hunderte Applications erzeugen. Die ApplicationSet-Doku nennt das als Risiko. Code-Owner-Regeln auf config.yaml und Limits im Review helfen mehr als nachträgliches Aufräumen.
Einordnung
Multi-Cluster-GitOps mit Argo CD lohnt sich ab etwa fünf Clustern oder sobald mehr als ein Team deployt. Darunter ist eine Instanz pro Cluster mit einfachen Applications oft pragmatischer.
Meine klare Empfehlung für Organisationen mit vielen Clustern: ein zentrales Argo CD pro Sicherheitszone, Cluster über Labels beschrieben, ApplicationSets ausschließlich in der Hand des Plattformteams, ein AppProject mit Impersonation pro Fachteam, Stages als Verzeichnisse und Secrets über External Secrets aus Vault. Sharding erst aktivieren, wenn Metriken es zeigen. argocd-agent im Blick behalten und dort pilotieren, wo Netztrennung das Pull-Modell erzwingt, für das gesamte Produktionssetup aber erst nach einem stabilen Release.
Nicht sinnvoll ist Argo CD als Ersatz für Cluster-Provisionierung. Dafür sind Werkzeuge wie Crossplane oder Terraform gedacht, siehe Terraform oder Crossplane. Wer eine solche Plattform aufbauen oder ein bestehendes Setup prüfen lassen will, findet meine Leistungen und den Kontakt auf der Startseite.
Häufige Fragen
Sollte man ein zentrales Argo CD oder eine Instanz pro Cluster betreiben?
Ein zentrales Argo CD pro Sicherheitszone oder Netzsegment ist für die meisten Organisationen der beste Kompromiss: eine Oberfläche, ein RBAC-Modell, eine Stelle für ApplicationSets. Eine Instanz pro Cluster lohnt sich, wenn Cluster netztechnisch strikt getrennt sind oder unabhängig vom Management-Cluster weiterarbeiten müssen. Der Preis sind mehr Instanzen, die man upgraden und überwachen muss.
Was ist der Argo CD Agent und ist er produktionsreif?
argocd-agent ist ein Projekt unter argoproj-labs, bei dem Application Controller und Repo-Server auf den Workload-Clustern laufen und ein Agent eine ausgehende gRPC-Verbindung zum zentralen Principal aufbaut. Dadurch braucht die Zentrale keinen Netzzugriff auf die Kubernetes-APIs der Workload-Cluster. Stand Oktober 2026 ist v0.10.0 aktuell; das Projekt ist aktiv, aber noch nicht als stabil gekennzeichnet, ein Einsatz in Produktion sollte entsprechend abgesichert sein.
Wie bekommen Fachteams Self-Service in Argo CD, ohne Admin-Rechte zu erhalten?
Jedes Team erhält ein eigenes AppProject, das erlaubte Quell-Repositories, Ziel-Cluster und Namespaces festlegt und cluster-weite Ressourcen sperrt. Die ApplicationSets mit fest eingetragenem project-Feld verwaltet das Plattformteam, die Teams pflegen nur Konfigurationsdateien in ihrem Repository. Mit Service Account Impersonation (Beta seit Argo CD 3.5) synchronisiert Argo CD zusätzlich mit den Rechten eines Service Accounts im Ziel-Namespace statt mit eigenen Admin-Rechten.
Wie skaliert Argo CD auf viele Cluster und tausende Applications?
Der Application Controller lässt sich als StatefulSet mit mehreren Replicas betreiben, die Cluster werden auf Shards verteilt. Neben dem Standardalgorithmus legacy gibt es round-robin und consistent-hashing, beide laut Feature-Maturity-Seite noch Alpha. Zusätzlich helfen höhere Status- und Operation-Processor-Werte, ein begrenztes Parallelism-Limit am Repo-Server und Git-Webhooks statt Polling.
Wie verwaltet man Secrets in einem Multi-Cluster-GitOps-Setup mit Argo CD?
Die Argo-CD-Dokumentation empfiehlt ausdrücklich, Secrets im Ziel-Cluster durch einen Operator zu erzeugen, statt sie bei der Manifest-Generierung einzuspielen. In der Praxis heißt das meist External Secrets Operator mit Vault als Backend, wobei in Git nur die ExternalSecret-Referenz liegt. Sealed Secrets funktioniert ebenfalls, erfordert aber pro Cluster ein eigenes Schlüsselpaar und eine saubere Sicherung des Controller-Schlüssels.