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
- Start with
pausesteps requiring manual promotion before introducingAnalysisTemplateautomation - understand the rollout’s normal step-by-step behavior before trusting a metrics query to make the promote/abort decision unattended. - Set
failureLimitdeliberately, not to 0 - a single noisy sample shouldn’t abort a rollout; require a real, sustained signal. - Keep
scaleDownDelaySecondslong 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. - Use the
kubectl argo rollouts get rollout --watchview during rollouts - it renders the live canary/blue-green state (traffic weights, analysis results) far more legibly than rawkubectl 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.