ApplicationSet controllers in Argo CD are designed to automate the creation and management of Argo CD Applications. When you want to deploy the same application across multiple clusters, or with slightly different configurations per cluster, you’d typically create multiple Application resources. ApplicationSet automates this by observing a source of truth (like a Git repository or a cluster list) and generating Application resources based on that.
Let’s see it in action. Imagine you have a standard guestbook application you want to deploy to several clusters, but with different server values in the argocd-cm ConfigMap to distinguish them.
First, ensure you have ApplicationSet installed in your Argo CD instance. If not, you can install it via the Argo CD UI or CLI:
argocd-plugin install --name applicationset --repo https://raw.githubusercontent.com/argoproj-labs/applicationset/v0.5.0 --chart applicationset
Now, let’s define an ApplicationSet that targets a list of clusters.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook-per-cluster
spec:
generators:
- list:
elements:
- cluster:
server: https://kubernetes.default.svc
name: in-cluster
url: http://my-git-repo.example.com/apps/guestbook
- cluster:
server: https://192.168.1.100:6443
name: cluster-1
url: http://my-git-repo.example.com/apps/guestbook
- cluster:
server: https://192.168.1.101:6443
name: cluster-2
url: http://my-git-repo.example.com/apps/guestbook
template:
metadata:
name: 'guestbook-{{cluster.name}}'
spec:
project: default
source:
repoURL: '{{ .url }}'
targetRevision: HEAD
path: '{{cluster.name}}' # Assuming each cluster has its own sub-path in Git
destination:
server: '{{cluster.server}}'
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
When this ApplicationSet is applied, it will generate three Application resources:
guestbook-in-clustertargetinghttps://kubernetes.default.svcguestbook-cluster-1targetinghttps://192.168.1.100:6443guestbook-cluster-2targetinghttps://192.168.1.101:6443
Each generated Application will have its spec.source.repoURL, spec.source.path, and spec.destination.server populated from the list elements. The {{cluster.name}} and {{cluster.server}} are template variables that get substituted.
This mechanism allows you to manage a fleet of applications with minimal GitOps boilerplate. Instead of managing dozens or hundreds of individual Application manifests, you manage a single ApplicationSet that defines the pattern for generating them.
The generators field is where the magic starts. ApplicationSet supports several types of generators:
list: As shown above, you can provide a direct list of elements, each containing cluster details and application source information. This is great for a fixed, known set of deployments.git: This generator scans a Git repository for directories or files matching a pattern. For example, you could have a structure like:
And your ApplicationSet would look like:apps/ guestbook/ cluster-1/ kustomization.yaml cluster-2/ kustomization.yaml
This generates anspec: generators: - git: repoURL: http://my-git-repo.example.com/apps.git revision: HEAD directories: - path: apps/guestbook/*/ # This will find 'cluster-1', 'cluster-2' etc. template: # ... rest of the templateApplicationfor each directory found.cluster: This generator uses the clusters already registered with Argo CD. It can filter these clusters based on labels.spec: generators: - clusters: selector: matchLabels: environment: production template: # ... rest of the template, likely using {{ .server }} and {{ .name }}clusterAddon: Similar tocluster, but generates applications for each cluster that has a specific Argo CDClusterAddonconfigured.matrix: Combines multiple generators. If you have alistgenerator and agitgenerator, thematrixgenerator will create a Cartesian product of their elements, generating anApplicationfor every combination. This is powerful for scenarios where you need to deploy a set of applications to a set of clusters.
The template section defines the Application manifest that ApplicationSet will generate. It uses Go templating syntax ({{ }}) to reference values from the generator’s elements. You can use variables like {{ .path }}, {{ .url }}, {{ .cluster.name }}, {{ .cluster.server }}, and any other fields you define in your generator elements.
A key aspect of ApplicationSet is its ability to pass arbitrary data into the template. In the list generator example, we included a url field for each element. This url field is then directly accessible within the template as {{ .url }}. This flexibility is crucial for customizing each generated Application.
When you define a template.spec.source.path, you can make it dynamic. For instance, if your Git repository is structured such that each cluster has its own dedicated directory (e.g., apps/guestbook/cluster-1, apps/guestbook/cluster-2), you can use path: 'guestbook/{{cluster.name}}' in the template. This tells Argo CD to look for the application manifests within the guestbook/<cluster-name> directory in the specified repoURL.
One thing many users overlook is the power of template.metadata.name. You are not limited to just using variables from the generator for parts of the Application manifest itself. You can construct unique names for your generated Application resources based on the generator’s context. For example, name: '{{ .cluster.name }}-app' or name: '{{ .path | base }}-{{ .cluster.name }}'. This allows for very granular naming conventions for your applications.
The ApplicationSet controller continuously reconciles the desired state (defined by the ApplicationSet and its generators) with the actual state (the generated Application resources). If you add a new cluster to your list or a new directory to your Git repo, ApplicationSet will detect it and create the corresponding Application. Conversely, if you remove an element from the source, ApplicationSet will delete the generated Application.
The next logical step after mastering ApplicationSet for cluster-specific deployments is to explore how to manage application configurations that vary within a cluster, perhaps using Helm or Kustomize parameters that are also dynamically generated.