backend / cicd / 08_progressive_delivery.md

Progressive delivery on Kubernetes — Argo Rollouts & Flagger

4 min read source

Progressive delivery on Kubernetes — Argo Rollouts & Flagger

Progressive delivery = deployment strategies (05_deployment_strategies.md) run as an automated, metric-gated process: shift a slice of traffic, watch the metrics, promote or roll back — no human staring at Grafana. Canary/blue-green describe the shape; progressive delivery is the machinery.

Why a stock k8s Deployment can’t do this

A Deployment gives you rolling updates only — and rolling has three gaps:

  • No traffic control: replicas flip old→new; you can’t say “5% of traffic to v2.” With 4 pods, the granularity is 25%, decided by pod count, not intent.
  • No metric gate: readiness probes (../17_kubernetes/04_probes_and_hpa.md) check “is the pod up,” not “did the error rate double.” A pod that starts cleanly and corrupts 2% of requests rolls out to 100%.
  • No automatic rollback: a bad rollout sits there until a human runs kubectl rollout undo.

Progressive-delivery controllers close all three: traffic shifting via ingress/mesh, promotion gated on real metrics, rollback automatic on failure.

Argo Rollouts

Replaces Deployment with a Rollout CRD — same pod template, plus a strategy:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 5          # 5% of traffic to the canary
        - pause: {duration: 10m}
        - analysis:             # metric gate — promotion stops here on failure
            templates:
              - templateName: success-rate
        - setWeight: 25
        - pause: {duration: 10m}
        - setWeight: 50
        - pause: {}             # manual approval gate (pause forever until promoted)
  # ...template as in a Deployment

The gate is an AnalysisTemplate — typically a Prometheus query with a pass condition:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: success-rate
      interval: 1m
      failureLimit: 3
      successCondition: result[0] >= 0.99
      provider:
        prometheus:
          address: http://prometheus:9090
          query: |
            sum(rate(http_requests_total{service="checkout",status!~"5.."}[5m]))
            / sum(rate(http_requests_total{service="checkout"}[5m]))

Analysis fails → Rollout aborts and traffic snaps back to stable. It also supports blueGreen (preview service, prePromotionAnalysis, instant switch) — the blue-green flow from 05_deployment_strategies.md with the gate built in.

Fine-grained traffic percentages need an ingress/mesh integration (NGINX Ingress, ALB, Istio, Linkerd/SMI); without one, Rollouts approximates weight by scaling replica counts.

Flagger

Same goal, inverted ergonomics: you keep your plain Deployment; Flagger is an operator that watches it plus a Canary custom resource. On image change it clones the Deployment into -primary, sends weighted traffic to the canary through the mesh/ingress, steps the weight up while checking metrics (built-in success-rate/latency checks, plus custom PromQL), and promotes or rolls back. Webhooks slot in load tests or manual gates.

Argo Rollouts Flagger
Model replace Deployment with Rollout CRD keep Deployment; operator + Canary CR alongside
Control explicit imperative steps you author declarative thresholds; controller runs the loop
Ecosystem Argo (pairs with Argo CD / GitOps) Flux family; broad mesh/ingress matrix
Manual gates first-class (pause) via webhooks
Pick when you want scripted, visible step sequences you want hands-off convergence semantics

Both are CNCF-standard; the interview answer is knowing one concretely and the trade-off table.

What makes a good gate metric

Gate on symptoms users feel, measured on the canary pods only (label-scoped queries):

Pitfalls

  • Not enough traffic for significance: 5% of a low-QPS service = a handful of requests per interval; one flaky request “fails” the gate, or real breakage passes. Use longer windows, higher starting weights, or synthetic load via webhooks.
  • Sticky/stateful traffic: weighted routing assumes any pod can serve any request; session affinity or in-pod state skews both traffic and metrics (09_stateful_vs_stateless.md — stateless services are the prerequisite).
  • Database migrations don’t canary: schema is shared by old and new versions simultaneously — expand/contract discipline still applies (../08_databases/sql/13_zero_downtime_migrations.md).
  • Gate on averages: a 1% cohort disaster vanishes in a global average — scope queries to canary pods, alert on ratios not counts.
  • Metrics lag (scrape + rate windows) — pauses shorter than ~2× the window gate on noise.

Interview angle

  • “How would you automate a canary release on Kubernetes?” — name the Deployment’s three gaps, then Rollout steps: weight → pause → analysis → promote/abort, with a Prometheus success-rate gate.
  • “Argo Rollouts vs Flagger?” — CRD-replacement + explicit steps vs operator + declarative thresholds; both need ingress/mesh for true traffic weights.
  • “What metrics gate a rollout?” — canary-scoped error rate + tail latency + one business metric; thresholds derived from SLOs.
  • “When does canary analysis fail you?” — low traffic, sticky sessions, shared DB schema, average-masking — and what you do about each.