Progressive Delivery with Argo Rollouts

Canary and blue-green deployments, automated analysis, and traffic shaping - Argo Rollouts as a drop-in Deployment replacement

Introduction

ArgoCD deploys whatever’s declared in Git - it doesn’t control how a rolling update happens once it’s applied, because that’s a Deployment/Kubernetes concern, not a GitOps concern. Argo Rollouts is a separate, complementary project (from the same Argo family, but its own controller and CRDs) that replaces Deployment with a Rollout resource supporting canary and blue-green strategies, automated metric-based analysis, and integration with traffic-splitting providers - the piece that actually answers “how do we roll this out safely.”

Installing Argo Rollouts

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# The kubectl plugin - not required, but the standard way to inspect rollouts
kubectl krew install argo-rollouts
kubectl argo rollouts version

The controller and the kubectl argo rollouts plugin are separate installs - the controller reconciles Rollout objects in the cluster; the plugin is a kubectl extension for visualizing and controlling them (kubectl argo rollouts get rollout, promote, abort) from the command line.

A Rollout Resource

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app
spec:
  replicas: 5
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: myregistry/web-app:2.3.0
          ports:
            - containerPort: 8080
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }
        - setWeight: 100

A Rollout’s spec.template/spec.selector are otherwise identical to a Deployment’s - migrating an existing workload is a matter of changing kind: Deployment to kind: Rollout and adding a strategy block, not a rewrite. setWeight: 10 shifts 10% of traffic (or, without a traffic-management provider configured, 10% of the pod replica count as an approximation) to the new version; pause: { duration: 5m } holds there before continuing. Omitting a duration on pause holds indefinitely until a human runs kubectl argo rollouts promote web-app.

Automated Analysis

Manual pauses catch regressions only if someone’s watching. AnalysisTemplate automates the judgment call by querying a metrics provider (Prometheus is the most common) at each step:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
    - name: service-name
  metrics:
    - name: success-rate
      interval: 1m
      count: 5
      successCondition: result[0] >= 0.95
      failureLimit: 2
      provider:
        prometheus:
          address: http://prometheus.monitoring:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",code!~"5.."}[2m]))
            /
            sum(rate(http_requests_total{service="{{args.service-name}}"}[2m]))
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app
spec:
  strategy:
    canary:
      analysis:
        templates:
          - templateName: success-rate
        args:
          - name: service-name
            value: web-app
        startingStep: 1   # begin analysis after step index 1 (setWeight: 10)
      steps:
        - setWeight: 10
        - pause: { duration: 2m }
        - setWeight: 50
        - pause: { duration: 2m }
        - setWeight: 100

The controller runs the Prometheus query every interval for count samples; if successCondition fails more than failureLimit times, the rollout automatically aborts and rolls back to the previous stable ReplicaSet with no human intervention - the actual point of progressive delivery: a bad canary is caught and reverted by the same signal that would have paged someone, before it reaches 100% of traffic.

Blue-Green

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app
spec:
  strategy:
    blueGreen:
      activeService: web-app-active
      previewService: web-app-preview
      autoPromotionEnabled: false
      scaleDownDelaySeconds: 300

Blue-green runs the full new version alongside the full old version behind two separate Services - previewService lets you (or an automated smoke test) hit the new version directly before cutting activeService’s selector over to it with kubectl argo rollouts promote. autoPromotionEnabled: false requires that explicit promotion; scaleDownDelaySeconds keeps the old ReplicaSet running (for a fast kubectl argo rollouts undo) for a grace period after promotion rather than scaling it down immediately.

Traffic Management Providers

setWeight without a configured traffic provider only approximates percentage traffic via replica-count ratios (10% weight ≈ 10% of pods, which is only an accurate proxy for traffic split if requests are evenly load-balanced across pods). For precise, request-level traffic splitting, Argo Rollouts integrates with a service mesh or ingress controller’s native weighting:

spec:
  strategy:
    canary:
      trafficRouting:
        istio:
          virtualService:
            name: web-app-vsvc
            routes:
              - primary

Supported providers include Istio, NGINX Ingress, ALB Ingress (AWS), SMI, and Traefik - each requires its own one-time setup (an Istio VirtualService referencing the Rollout, for instance) beyond the Rollout resource itself.

ArgoCD Integration

Argo Rollouts and ArgoCD are separate controllers but work together naturally: ArgoCD applies the Rollout manifest from Git like any other resource, and its own UI includes a Rollout visualization extension showing canary steps and weights directly in the Application view - no special ArgoCD configuration is required beyond having both controllers installed in the cluster.

Best Practices

  1. Start with pause steps requiring manual promotion before introducing AnalysisTemplate automation - understand the rollout’s normal step-by-step behavior before trusting a metrics query to make the promote/abort decision unattended.
  2. Set failureLimit deliberately, not to 0 - a single noisy sample shouldn’t abort a rollout; require a real, sustained signal.
  3. Keep scaleDownDelaySeconds long enough for a genuine rollback window on blue-green - scaling down the old ReplicaSet immediately on promotion removes the fast-rollback safety net that’s the whole point of the strategy.
  4. Use the kubectl argo rollouts get rollout --watch view during rollouts - it renders the live canary/blue-green state (traffic weights, analysis results) far more legibly than raw kubectl get rollout -o yaml.

Conclusion

Argo Rollouts is what turns “we deployed the new version” into “we deployed the new version, watched a real health signal at each traffic increment, and would have automatically reverted if it regressed” - the safety mechanism that a plain Deployment’s rolling update, with no concept of pausing or analysis, simply doesn’t have.

Additional Resources