Skip to content
Published on

ArgoCD ApplicationSet and Progressive Rollout: A Practical Guide to GitOps Multi-Cluster Deployment

Authors
  • Name
    Twitter

1. What is ArgoCD ApplicationSet

ApplicationSet is an extension resource of ArgoCD that automatically generates multiple Applications from a single template. Instead of manually managing dozens to hundreds of Applications in multi-cluster, multi-tenant, or monorepo environments, you can automate them declaratively.

1.1 Why ApplicationSet?

Traditional approach:
  app-dev.yaml    -> Application (dev)
  app-staging.yaml -> Application (staging)
  app-prod.yaml   -> Application (prod)
  ... x 50 services = 150 YAMLs to manage manually

ApplicationSet:
  appset.yaml -> One template automatically generates all 150

2. ApplicationSet Generator Types

2.1 List Generator

The simplest form. Generates Applications from an explicit list of parameters:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-envs
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: dev
            url: https://dev-k8s.example.com
            namespace: myapp-dev
          - cluster: staging
            url: https://staging-k8s.example.com
            namespace: myapp-staging
          - cluster: prod
            url: https://prod-k8s.example.com
            namespace: myapp-prod
  template:
    metadata:
      name: 'myapp-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: 'k8s/overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

2.2 Git Generator (Directory)

Automatic directory-based discovery in a monorepo structure:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/org/cluster-addons.git
        revision: HEAD
        directories:
          - path: addons/*
          - path: addons/exclude-this
            exclude: true
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: addons
      source:
        repoURL: https://github.com/org/cluster-addons.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'

2.3 Cluster Generator

Automatically deploy to all clusters registered in ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: monitoring-stack
  namespace: argocd
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: production
  template:
    metadata:
      name: 'monitoring-{{name}}'
    spec:
      project: infrastructure
      source:
        repoURL: https://github.com/org/monitoring.git
        path: helm-chart
        helm:
          valueFiles:
            - 'values-{{metadata.labels.region}}.yaml'
      destination:
        server: '{{server}}'
        namespace: monitoring

2.4 Matrix Generator (Combination)

Cross-combines two Generators:

spec:
  generators:
    - matrix:
        generators:
          - git:
              repoURL: https://github.com/org/apps.git
              revision: HEAD
              directories:
                - path: apps/*
          - clusters:
              selector:
                matchLabels:
                  env: production

3. Progressive Sync: Staged Rollout

3.1 Concept

Progressive Sync is a strategy that deploys Applications created by ApplicationSet in stages rather than all at once. You can proceed with validation in order: QA, then Staging, then Production.

3.2 Configuration

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-progressive
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: qa
            url: https://qa-k8s.example.com
          - cluster: staging
            url: https://staging-k8s.example.com
          - cluster: prod-east
            url: https://prod-east-k8s.example.com
          - cluster: prod-west
            url: https://prod-west-k8s.example.com
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: cluster
              operator: In
              values:
                - qa
          # Deploy QA first -> auto Sync
        - matchExpressions:
            - key: cluster
              operator: In
              values:
                - staging
          maxUpdate: 100%
          # After QA succeeds, staging auto
        - matchExpressions:
            - key: cluster
              operator: In
              values:
                - prod-east
                - prod-west
          maxUpdate: 50%
          # prod rolls out 50% at a time
  template:
    metadata:
      name: 'myapp-{{cluster}}'
      labels:
        cluster: '{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: 'k8s/overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: myapp
      syncPolicy:
        automated:
          prune: true

4. Argo Rollouts: Canary and Blue-Green

4.1 Canary Deployment

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 5m }
        - analysis:
            templates:
              - templateName: success-rate
            args:
              - name: service-name
                value: myapp
        - setWeight: 60
        - pause: { duration: 10m }
        - setWeight: 100
      canaryService: myapp-canary
      stableService: myapp-stable
      trafficRouting:
        istio:
          virtualServices:
            - name: myapp-vsvc
              routes:
                - primary
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:v2
          ports:
            - containerPort: 8080

4.2 AnalysisTemplate (Automatic Rollback)

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
    - name: service-name
  metrics:
    - name: success-rate
      interval: 30s
      count: 10
      successCondition: result[0] >= 0.95
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{
              service="{{args.service-name}}",
              status=~"2.."
            }[5m])) /
            sum(rate(http_requests_total{
              service="{{args.service-name}}"
            }[5m]))

4.3 Blue-Green Deployment

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp-bluegreen
spec:
  replicas: 5
  strategy:
    blueGreen:
      activeService: myapp-active
      previewService: myapp-preview
      autoPromotionEnabled: false
      prePromotionAnalysis:
        templates:
          - templateName: success-rate
      scaleDownDelaySeconds: 300
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:v2

5. Production Setup: ApplicationSet + Rollouts Integration

5.1 Directory Structure

repo/
├── apps/
│   ├── frontend/
│   │   ├── base/
│   │   │   ├── rollout.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays/
│   │       ├── dev/
│   │       ├── staging/
│   │       └── prod/
│   └── backend/
│       ├── base/
│       └── overlays/
└── appsets/
    └── all-apps.yaml

5.2 Integrated ApplicationSet

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-apps
  namespace: argocd
spec:
  generators:
    - matrix:
        generators:
          - git:
              repoURL: https://github.com/org/gitops.git
              revision: HEAD
              directories:
                - path: apps/*
          - list:
              elements:
                - env: dev
                  cluster: https://dev-k8s.example.com
                - env: staging
                  cluster: https://staging-k8s.example.com
                - env: prod
                  cluster: https://prod-k8s.example.com
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: env
              operator: In
              values: [dev]
        - matchExpressions:
            - key: env
              operator: In
              values: [staging]
        - matchExpressions:
            - key: env
              operator: In
              values: [prod]
  template:
    metadata:
      name: '{{path.basename}}-{{env}}'
      labels:
        app: '{{path.basename}}'
        env: '{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/gitops.git
        targetRevision: HEAD
        path: '{{path}}/overlays/{{env}}'
      destination:
        server: '{{cluster}}'
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

6. ArgoCD CLI Operations Commands

# Check ApplicationSet status
argocd appset list
argocd appset get myapp-progressive

# Check specific Application Sync status
argocd app get myapp-prod --refresh

# Manual Sync (when manual approval is required in Progressive)
argocd app sync myapp-staging

# Check Rollout status (Argo Rollouts kubectl plugin)
kubectl argo rollouts status myapp -n myapp
kubectl argo rollouts get rollout myapp -n myapp -w

# Manual Canary promotion
kubectl argo rollouts promote myapp -n myapp

# Rollback
kubectl argo rollouts undo myapp -n myapp

7. Best Practices

  1. Progressive Sync is essential: Always use staged rollout for production deployments
  2. Integrate AnalysisTemplate: Set up metrics-based automatic rollback
  3. Use ApplicationSet instead of App of Apps: Dramatically reduces management complexity
  4. Leverage Matrix Generator: Automate app x environment combinations
  5. Use Sync Waves: Control the order of dependent resources
  6. Integrate Notifications: Send deployment status alerts via Slack/Telegram

8. Quiz

Q1. What is the core role of ApplicationSet?

Automatically generates multiple ArgoCD Applications from a single template. Automates multi-cluster/multi-environment deployments.

Q2. How does the Matrix Generator work?

It cross-combines (Cartesian product) the results of two Generators to create Applications for all combinations.

Q3. What does maxUpdate: 50% mean in Progressive Sync?

Only 50% of the Applications in that step are synced simultaneously, and after they succeed, the remaining 50% proceed.

Q4. What happens when an Argo Rollouts AnalysisTemplate fails?

It automatically rolls back. When the failureLimit is exceeded, the Rollout enters a Degraded state and reverts to the previous version.

Q5. What does autoPromotionEnabled: false mean in Blue-Green?

After verifying the Preview environment, you must manually promote to switch to Active. The kubectl argo rollouts promote command is required.

Q6. Why configure trafficRouting in Canary deployments?

To precisely control the ratio of actual traffic. By integrating with Istio/Nginx/ALB, you implement Canary based on traffic ratio rather than pod count.

Q7. What are the advantages of ApplicationSet over the App of Apps pattern?

(1) Managed as a single resource (2) Automatic discovery through Generators (3) Progressive Sync support (4) Eliminates code duplication