~/oleksandr-ponomarov

Portfolio · Notes · Dotfiles

Search everything

Search case studies, engineering notes, and Dotfiles documentation.

    ← all notes

    Demystifying ArgoCD's ApplicationSet — Pt. 1

    Learn how list, cluster, and matrix generators turn a single ApplicationSet into repeatable multi-cluster deployments.

    ApplicationSet is a custom resource (CR) that describes how to create a set of Application resources. This CR includes an Application template and an area to define the source of truth. ApplicationSet supports several sources of truth through a variety of generators.

    Generators

    Generators are a cornerstone of ApplicationSet, enabling the dynamic creation of Application resources based on various inputs. A wide range of generators is available, each designed to serve specific use cases, with the ArgoCD maintainers continuously expanding the list. Each generator comes with its own unique interface and supported options. Notably, the matrix generator allows you to combine the parameters produced by two separate generators, adding even greater flexibility.

    We’ll cover the most straightforward yet widely used generators, suitable for addressing a broad range of common use cases.

    Generator: list

    Very simple — generates parameters based on an arbitrary list of key/value pairs (as long as the values are string values). These key/value pairs can be referenced as variables in the .spec.template.

    Example:

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
    name: example
    spec:
    generators:
    - list:
    elements:
    - appName: atlantis
    namespace: automation
    - appName: crossplane
    namespace: crossplane-system
    template:
    metadata:
    name: "example"
    spec:
    source:
    helm:
    valueFiles:
    - "{{ appName }}-values.yaml"
    destination:
    namespace: "{{ namespace }}"

    Generator: cluster

    Allows you to target Kubernetes clusters configured and managed by ArgoCD. Since the clusters are configured through native Kubernetes secrets (object of kind: Secrets) with argocd.argoproj.io/secret-type: cluster annotation, the ApplicationSet controller will parse these secrets to generate parameters for each cluster.

    This generator provides the following parameters:

    • name: Cluster name in ArgoCD - the name field of the secret.
    • server: Server URI - the server field of the secret.
    • metadata.labels.*: Key/value pairs for each label of secret.
    • metadata.annotations.*: Key/value pairs for each annotation of the secret.

    The cluster generator is a map that, by default, targets all Kubernetes clusters configured and managed by ArgoCD, but it also allows you to target a specific cluster using a selector, such as label. The in-cluster label in the example below basically means “the current cluster where ArgoCD is running”.

    Example:

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
    name: example
    spec:
    generators:
    - clusters:
    selector:
    matchLabels:
    name: in-cluster
    template:
    metadata:
    name: "example"
    spec:
    source:
    helm:
    valueFiles:
    - "{{ metadata.labels.env }}-values.yaml"
    destination:
    server: "{{ server }}"

    Generator: matrix

    Special type of generator that combines parameters from two other generators.

    For example, you might want to deploy a list of applications (list generator) across all your clusters (cluster generator).

    Example:

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
    name: example
    spec:
    generators:
    - matrix:
    generators:
    - list:
    elements:
    - appName: example1
    - appName: example2
    - clusters:
    selector:
    matchLabels:
    name: in-cluster
    template:
    metadata:
    name: "application-{{ appName }}"
    spec:
    source:
    path: "path-within-repo/kustomize/overlays/{{ appName }}"
    destination:
    server: "{{ server }}"

    Real Life Examples

    Once you’re familiar with these three commonly used generators, you can combine them to create various input permutations, enabling efficient management of multiple environments, applications, or clusters within a single file. Whether this approach suits your needs depends on your specific use case. I find it particularly effective in scenarios where a monorepo is well-organized with clear ownership, such as using CODEOWNERS to assign specific teams to maintain their respective ApplicationSets. With a well-defined convention, such setup can also facilitate automated updates of these files using tools like Renovate.

    The example configuration below demonstrates the true flexibility:

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
    name: elasticsearch-exporter
    spec:
    generators:
    - matrix:
    generators:
    - list:
    elements:
    - clusterName: california
    clusterNamespace: blue
    revision: "main"
    environment: "development"
    - clusterName: monaco
    clusterNamespace: green
    revision: "main"
    environment: "development"
    - clusterName: california
    clusterNamespace: blue
    revision: "elasticsearch-exporter-v1.3.0"
    environment: "staging"
    - clusterName: monaco
    clusterNamespace: green
    revision: "elasticsearch-exporter-v2.0.0"
    environment: "staging"
    - clusters:
    selector:
    matchExpressions:
    - key: environment
    operator: In
    values:
    - "{{ environment }}"
    - key: name
    operator: In
    values:
    - "in-cluster"
    template:
    metadata:
    name: "elasticsearch-exporter-{{ clusterName }}"
    spec:
    source:
    repoURL: [email protected]:org/repo.git
    targetRevision: "{{ revision }}"
    path: "clusters/elasticsearch-exporter/overlays/{{ clusterName }}"
    destination:
    server: "{{ server }}"
    namespace: "{{ clusterNamespace }}"

    Conclusion

    This post provides a concise overview of real-life use cases for the most common generators in ArgoCD’s ApplicationSet. For a deeper dive into practical examples and detailed options on leveraging variables produced by these generators, particularly when configuring the .source field for manifests, check out this post.

    Comments