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