Argo CD ApplicationSets für Multi-Cluster
Generatoren, Templates und Fleet-GitOps.
von PlatPrep
Fünf Cluster. Zwanzig Teams. Jedes Team hat mehrere Applikationen. Jede Applikation braucht eine ArgoCD Application-Ressource.
Wer das manuell pflegt, hat entweder sehr viel Zeit oder macht es falsch.
ApplicationSets sind ArgoCDs Antwort auf dieses Problem. Ein ApplicationSet ist ein Template für Application-Ressourcen, kombiniert mit einem oder mehreren Generators, die beschreiben, für welche Instanzen das Template ausgerollt wird. Statt hundert Application-Objekte zu pflegen, pflegst du ein ApplicationSet — und der ApplicationSet-Controller erzeugt, aktualisiert und löscht die Applications für dich.
Generators: woher die Parameter kommen
Der list-Generator ist der einfachste: eine explizite Liste von Parameter-Sets, volle Kontrolle, kein Automatismus. Gut für drei Cluster, mühsam für dreißig.
Interessanter ist der git-Generator. Er scannt ein Repo nach Verzeichnissen oder Dateien mit einem bestimmten Muster und erzeugt pro Match eine Application. Liegt jeder Service unter apps/<team>/<service>/, reicht ein neues Verzeichnis im Repo — und ArgoCD legt die Application automatisch an. Onboarding eines neuen Services wird ein Pull Request statt eines Plattform-Tickets.
Der clusters-Generator nutzt die in ArgoCD registrierten Cluster-Secrets als Quelle. Über Label-Selektoren steuerst du, welche Cluster betroffen sind. Richtig mächtig wird es, wenn man beides per matrix-Generator kreuzt:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: team-apps
namespace: argocd
spec:
goTemplate: true
generators:
- matrix:
generators:
- clusters:
selector:
matchLabels:
env: prod
- git:
repoURL: https://github.com/org/apps.git
revision: main
directories:
- path: "apps/*/*"
template:
metadata:
name: "{{.path.basename}}-{{.name}}"
spec:
project: tenants
source:
repoURL: https://github.com/org/apps.git
targetRevision: main
path: "{{.path.path}}"
destination:
server: "{{.server}}"
namespace: "{{.path.basename}}"
syncPolicy:
automated:
prune: true
selfHeal: true
Jeder Service aus dem Git-Repo landet auf jedem Prod-Cluster. Das Kreuzprodukt ist genau so mächtig wie gefährlich: 30 Services mal 10 Cluster sind 300 Applications, und jede Änderung am ApplicationSet betrifft alle gleichzeitig — ein Tippfehler im Template ist ein Tippfehler in 300 Applications. Für die meisten Setups ist ein einfacher Generator pro ApplicationSet die wartbarere Wahl — Matrix nur dort, wo das Fleet-Muster wirklich gebraucht wird.
Die Stellen, an denen es wehtut
Löschen kaskadiert. Der Controller besitzt die generierten Applications. Löschst du das ApplicationSet — oder ändert sich der Generator-Output so, dass eine Application herausfällt — wird sie entfernt, inklusive der Cluster-Ressourcen, wenn prune aktiv ist. Ein versehentlich gelöschtes Verzeichnis im Git-Repo räumt also Produktions-Workloads ab. Für den Übergang gibt es preserveResourcesOnDeletion in der syncPolicy des ApplicationSets; langfristig hilft nur Disziplin: Branch Protection auf dem Apps-Repo und ein Review-Pflicht-Workflow.
Polling vs. Webhooks. Der Git-Generator pollt standardmäßig alle drei Minuten. Bei vielen ApplicationSets auf einem Monorepo läufst du in API-Rate-Limits bei GitHub und wunderst dich über träge Updates. Ein Webhook vom Git-Provider an den ApplicationSet-Controller löst beides.
Konfigurationsdrift zwischen Umgebungen. Die Versuchung ist groß, Umgebungsunterschiede über immer mehr Generator-Parameter und Template-Bedingungen abzubilden. Ab einem gewissen Punkt ist niemand mehr in der Lage, aus dem ApplicationSet abzulesen, was auf welchem Cluster tatsächlich landet. Die wartbarere Struktur: Unterschiede gehören in die Kustomize-Overlays oder Helm-Values im Apps-Repo (overlays/prod, overlays/staging), das ApplicationSet bleibt dumm und reicht nur den Pfad durch.
Template-Ausdruckskraft. Mit goTemplate: true stehen Go-Template-Funktionen zur Verfügung (lower, replace, Sprig-Funktionen) — wichtig, weil Application-Namen DNS-konform sein müssen und Verzeichnisnamen das nicht immer sind. Wer einzelne generierte Applications vom Template abweichen lassen muss, kann mit templatePatch gezielt patchen, statt ein zweites ApplicationSet zu pflegen.
Platform- und Team-Workloads trennen
Ein Pattern, das sich bewährt hat: separate ApplicationSets für Platform-Services und für Team-Applikationen. Der Monitoring-Stack, Ingress-Controller und cert-manager kommen aus einem eng kontrollierten Infra-Repo, mit eigenem ArgoCD-Project, das nur definierte Ziel-Namespaces und Ressourcen-Kinds erlaubt. Team-Apps laufen über den Git-Generator mit einem Project, das ihnen Autonomie im eigenen Namespace gibt — aber eben nur dort. Die AppProject-Ressource ist hier der eigentliche Sicherheitsmechanismus: Ohne sie kann jedes generierte Application-Objekt theoretisch cluster-weite Ressourcen anlegen.
Beim Rollout über viele Cluster fehlt ApplicationSets von Haus aus eine Staffelung — alle Applications synchen, sobald sich die Source ändert. Es gibt dafür Progressive Syncs als Alpha-Feature; wer das produktiv braucht, fährt heute robuster mit einem Umweg: getrennte ApplicationSets pro Wave (erst Staging-Cluster, dann Prod), gesteuert über Cluster-Labels.
ApplicationSets verwandeln ArgoCD von einem App-by-App-Tool in eine skalierbare GitOps-Plattform. Wer die Grundlagen von Application, Project und Sync-Mechanik noch festigen will, findet sie im ArgoCD-Lernpfad — und wer noch zwischen den großen GitOps-Tools schwankt, im Vergleich Argo CD vs. Flux.