Argo Rollouts: Blue-Green deployments

Kubernetes Argo Rollouts blue-green

8 min read | by Jordi Prats

Argo Rollouts is a Kubernetes controller and set of CRDs for progressive delivery. It can be used to orchestrate blue-green deployments, canary releases, and rollouts. We are going to take a look at how to use Argo Rollouts to perform a blue-green deployment.

You can also checkout canary deployments with Argo Rollouts.

Installing Argo Rollouts

First we'll have to make sure we have Argo Rollouts and it's CLI installed. In a nutshell, we can install it using kustomize and brew as follows:

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

Blue-Green deployment

To get started with a blue-green deployment, we'll start with a regular Deployment object that we'll use as the base for our blue-green deployment:

apiVersion: apps/v1 kind: Deployment metadata:  name: demo-deploy spec:  replicas: 2  selector:  matchLabels:  app: nginx  template:  metadata:  labels:  app: nginx  spec:  containers:  - name: nginx  image: nginx:latest  ports:  - containerPort: 80 

To transform it into a blue-green rollout we will only need to change the apiVersion, kind and set the strategy to blueGreen:

apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata:  name: bg-rollout spec:  replicas: 2  selector:  matchLabels:  app: bg-rollout  template:  metadata:  labels:  app: bg-rollout  spec:  containers:  - name: nginx  image: nginx:latest  ports:  - containerPort: 80  strategy:  blueGreen:  previewService: rollout-preview  activeService: rollout-active 

Before applying the blue-green rollout we'll have to create the preview and active services:

apiVersion: v1 kind: Service metadata:  name: rollout-preview spec:  ports:  - port: 80  targetPort: http  protocol: TCP  name: http  selector:  app: bg-rollout --- apiVersion: v1 kind: Service metadata:  name: rollout-active spec:  ports:  - port: 80  targetPort: http  protocol: TCP  name: http  selector:  app: bg-rollout 

As soon as we apply all these three objects we'll be able to see the blue-green rollout acting very similar to other Deployment objects:

$ kubectl describe rollout.argoproj.io/bg-rollout Name: bg-rollout Namespace: demo-rollout Labels: <none> Annotations: rollout.argoproj.io/revision: 1 API Version: argoproj.io/v1alpha1 Kind: Rollout (...) Events:  Type Reason Age From Message  ---- ------ ---- ---- -------  Normal RolloutAddedToInformer 12m rollouts-controller Rollout resource added to informer: demo-rollout/bg-rollout  Normal RolloutUpdated 27s rollouts-controller Rollout updated to revision 1  Normal NewReplicaSetCreated 27s rollouts-controller Created ReplicaSet bg-rollout-7f56bd89fc (revision 1)  Normal SwitchService 27s rollouts-controller Switched selector for service 'rollout-preview' from '' to '7f56bd89fc'  Normal ScalingReplicaSet 17s rollouts-controller Scaled up ReplicaSet bg-rollout-7f56bd89fc (revision 1) from 0 to 2  Normal RolloutCompleted 17s rollouts-controller Rollout completed update to revision 1 (7f56bd89fc): Initial deploy  Normal SwitchService 14s rollouts-controller Switched selector for service 'rollout-active' from '' to '7f56bd89fc' $ kubectl get pods NAME READY STATUS RESTARTS AGE bg-rollout-7f56bd89fc-4h7b7 1/1 Running 0 20s bg-rollout-7f56bd89fc-vk5b7 1/1 Running 0 20s 

We can now update the deployment to see how it will automatically switch the traffic between replicas:

apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata:  name: bg-rollout spec:  replicas: 2  selector:  matchLabels:  app: bg-rollout  template:  metadata:  labels:  app: bg-rollout  spec:  containers:  - name: nginx  image: nginx:latest  ports:  - containerPort: 80  env:  - name: UPDATE  value: "EXAMPLE"  strategy:  blueGreen:  previewService: rollout-preview  activeService: rollout-active 

Once applied, it will automatically create a new ReplicaSet and switch the traffic to it:

$ kubectl describe rollout.argoproj.io/bg-rollout Name: bg-rollout Namespace: demo-rollout Labels: <none> Annotations: rollout.argoproj.io/revision: 2 API Version: argoproj.io/v1alpha1 Kind: Rollout (...) Events:  Type Reason Age From Message  ---- ------ ---- ---- -------  Normal RolloutAddedToInformer 13m rollouts-controller Rollout resource added to informer: demo-rollout/bg-rollout  Normal RolloutUpdated 2m1s rollouts-controller Rollout updated to revision 1  Normal NewReplicaSetCreated 2m1s rollouts-controller Created ReplicaSet bg-rollout-7f56bd89fc (revision 1)  Normal SwitchService 2m1s rollouts-controller Switched selector for service 'rollout-preview' from '' to '7f56bd89fc'  Normal ScalingReplicaSet 111s rollouts-controller Scaled up ReplicaSet bg-rollout-7f56bd89fc (revision 1) from 0 to 2  Normal RolloutCompleted 111s rollouts-controller Rollout completed update to revision 1 (7f56bd89fc): Initial deploy  Normal SwitchService 108s rollouts-controller Switched selector for service 'rollout-active' from '' to '7f56bd89fc'  Normal RolloutUpdated 4s rollouts-controller Rollout updated to revision 2  Normal NewReplicaSetCreated 4s rollouts-controller Created ReplicaSet bg-rollout-84bc9d88dc (revision 2)  Normal SwitchService 4s rollouts-controller Switched selector for service 'rollout-preview' from '7f56bd89fc' to '84bc9d88dc'  Normal RolloutNotCompleted 4s rollouts-controller Rollout not completed, started update to revision 2 (84bc9d88dc)  Normal ScalingReplicaSet 4s rollouts-controller Scaled up ReplicaSet bg-rollout-84bc9d88dc (revision 2) from 0 to 2  Normal SwitchService 1s rollouts-controller Switched selector for service 'rollout-active' from '7f56bd89fc' to '84bc9d88dc'  Normal RolloutCompleted 1s rollouts-controller Rollout completed update to revision 2 (84bc9d88dc): Completed blue-green update $ kubectl get pods NAME READY STATUS RESTARTS AGE bg-rollout-7f56bd89fc-4h7b7 1/1 Running 0 2m bg-rollout-7f56bd89fc-vk5b7 1/1 Running 0 2m bg-rollout-84bc9d88dc-lmvdf 1/1 Running 0 13s bg-rollout-84bc9d88dc-q2lxr 1/1 Running 0 13s 

We can also use the argo rollouts cli to better see the status of the rollout:

$ kubectl argo rollouts get rollout bg-rollout Name: bg-rollout Namespace: demo-rollout Status:  Healthy Strategy: BlueGreen Images: nginx:latest (stable, active) Replicas:  Desired: 2  Current: 2  Updated: 2  Ready: 2  Available: 2 NAME KIND STATUS AGE INFO  bg-rollout Rollout  Healthy 15m ├──# revision:2  └──⧉ bg-rollout-84bc9d88dc ReplicaSet  Healthy 105s stable,active  ├──□ bg-rollout-84bc9d88dc-lmvdf Pod  Running 105s ready:1/1  └──□ bg-rollout-84bc9d88dc-q2lxr Pod  Running 105s ready:1/1 └──# revision:1  └──⧉ bg-rollout-7f56bd89fc ReplicaSet  ScaledDown 3m42s $ kubectl get pods NAME READY STATUS RESTARTS AGE bg-rollout-84bc9d88dc-lmvdf 1/1 Running 0 119s bg-rollout-84bc9d88dc-q2lxr 1/1 Running 0 119s 

Blue-Green manual promotion

To manually promote the preview to active, we'll need to set autoPromotionEnabled to false:

apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata:  name: bg-rollout spec:  replicas: 2  selector:  matchLabels:  app: bg-rollout  template:  metadata:  labels:  app: bg-rollout  spec:  containers:  - name: nginx  image: nginx:latest  ports:  - containerPort: 80  env:  - name: UPDATE  value: "MANUAL PROMOTION"  strategy:  blueGreen:  previewService: rollout-preview  activeService: rollout-active  autoPromotionEnabled: false 

Once applied it will pause the rollout, for us to manually verify and promote it:

$ kubectl argo rollouts get rollout bg-rollout Name: bg-rollout Namespace: demo-rollout Status:  Paused Message: BlueGreenPause Strategy: BlueGreen Images: nginx:latest (active, preview, stable) Replicas:  Desired: 2  Current: 4  Updated: 2  Ready: 2  Available: 2 NAME KIND STATUS AGE INFO  bg-rollout Rollout  Paused 18m ├──# revision:3  └──⧉ bg-rollout-7956bb8bb6 ReplicaSet  Healthy 16s preview  ├──□ bg-rollout-7956bb8bb6-gvrgj Pod  Running 16s ready:1/1  └──□ bg-rollout-7956bb8bb6-sh8wx Pod  Running 16s ready:1/1 ├──# revision:2  └──⧉ bg-rollout-84bc9d88dc ReplicaSet  Healthy 4m48s stable,active  ├──□ bg-rollout-84bc9d88dc-lmvdf Pod  Running 4m48s ready:1/1  └──□ bg-rollout-84bc9d88dc-q2lxr Pod  Running 4m48s ready:1/1 └──# revision:1  └──⧉ bg-rollout-7f56bd89fc ReplicaSet  ScaledDown 6m45s 

As soon as we are happy with the preview we can promote it as follows:

$ kubectl argo rollouts promote bg-rollout rollout 'bg-rollout' promoted 

After a 30 seconds delay, the preview will be promoted to active:

$ kubectl argo rollouts get rollout bg-rollout Name: bg-rollout Namespace: demo-rollout Status:  Healthy Strategy: BlueGreen Images: nginx:latest (active, stable) Replicas:  Desired: 2  Current: 4  Updated: 2  Ready: 2  Available: 2 NAME KIND STATUS AGE INFO  bg-rollout Rollout  Healthy 19m ├──# revision:3  └──⧉ bg-rollout-7956bb8bb6 ReplicaSet  Healthy 60s stable,active  ├──□ bg-rollout-7956bb8bb6-gvrgj Pod  Running 60s ready:1/1  └──□ bg-rollout-7956bb8bb6-sh8wx Pod  Running 60s ready:1/1 ├──# revision:2  └──⧉ bg-rollout-84bc9d88dc ReplicaSet  Healthy 5m32s delay:27s  ├──□ bg-rollout-84bc9d88dc-lmvdf Pod  Running 5m32s ready:1/1  └──□ bg-rollout-84bc9d88dc-q2lxr Pod  Running 5m32s ready:1/1 └──# revision:1  └──⧉ bg-rollout-7f56bd89fc ReplicaSet  ScaledDown 7m29s $ kubectl argo rollouts get rollout bg-rollout Name: bg-rollout Namespace: demo-rollout Status:  Healthy Strategy: BlueGreen Images: nginx:latest (stable, active) Replicas:  Desired: 2  Current: 2  Updated: 2  Ready: 2  Available: 2 NAME KIND STATUS AGE INFO  bg-rollout Rollout  Healthy 19m ├──# revision:3  └──⧉ bg-rollout-7956bb8bb6 ReplicaSet  Healthy 88s stable,active  ├──□ bg-rollout-7956bb8bb6-gvrgj Pod  Running 88s ready:1/1  └──□ bg-rollout-7956bb8bb6-sh8wx Pod  Running 88s ready:1/1 ├──# revision:2  └──⧉ bg-rollout-84bc9d88dc ReplicaSet  ScaledDown 6m └──# revision:1  └──⧉ bg-rollout-7f56bd89fc ReplicaSet  ScaledDown 7m57s 

Blue-Green preview and active services

To be reach the correct ReplicaSet using the preview and active services, Argo Rollouts will automatically update the selector for each service:

$ kubectl get svc rollout-active -o yaml apiVersion: v1 kind: Service metadata: (...) spec: (...)  ports:  - name: http  port: 80  protocol: TCP  targetPort: http  selector:  app: bg-rollout  rollouts-pod-template-hash: 7956bb8bb6  sessionAffinity: None  type: ClusterIP status:  loadBalancer: {} $ kubectl get replicaset bg-rollout-7956bb8bb6 -o yaml apiVersion: apps/v1 kind: ReplicaSet metadata:  annotations:  rollout.argoproj.io/desired-replicas: "2"  rollout.argoproj.io/revision: "3"  creationTimestamp: "6T11:17:06Z"  generation: 2  labels:  app: bg-rollout  rollouts-pod-template-hash: 7956bb8bb6  name: bg-rollout-7956bb8bb6 (...) spec:  replicas: 2  selector:  matchLabels:  app: bg-rollout  rollouts-pod-template-hash: 7956bb8bb6  template:  metadata:  creationTimestamp: null  labels:  app: bg-rollout  rollouts-pod-template-hash: 7956bb8bb6  spec: (...) $ kubectl get pods bg-rollout-7956bb8bb6-gvrgj -o yaml apiVersion: v1 kind: Pod metadata:  creationTimestamp: "2025-03-16T11:17:06Z"  generateName: bg-rollout-7956bb8bb6-  labels:  app: bg-rollout  rollouts-pod-template-hash: 7956bb8bb6  name: bg-rollout-7956bb8bb6-gvrgj (...) spec: (...) 

Posted on 17/03/2025