In this post, we will take a look at 🟦/🟩 blue-green deployments in Kubernetes. With blue-green deployments, you deploy a new version of an application or service next to the live and stable version. After manual or automatic checks, you promote the new version to become the live version. Switching between versions is simply a networking change. This could be a change in a router configuration or, in the case of Kubernetes, a change in a Kubernetes service.
Note: there often is confusion about what is the 🟦 blue and what is the 🟩 green service; usually the green service is the live and stable one; the blue service is the newly deployed preview service you intend to promote; some documents switch it around; I sometimes do that as well, for instance on my YouTube channel 😉
A Kubernetes deployment resource does not have a StrategyType for blue-green deployments. It only supports RollingUpdate or Recreate. You can easily work around that with multiple deployments and services, as discussed by Nills Franssens here: Simple Kubernetes blue-green deployments.
When I need to do blue-green, I prefer using a progressive delivery controller such as Argo Rollouts or Flagger. They are both excellent pieces of software that make it easy to do blue-green deployments, in addition to canary deployments and automated tests. In this post, we will look at Argo Rollouts.
Want to see a video instead?
Installing Argo Rollouts
Installing Argo Rollouts is documented here. For a quick install, just do:
kubectl create namespace argo-rollouts kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
Argo Rollouts comes with a kubectl plugin for its CLI. Install it with
brew install argoproj/tap/kubectl-argo-rollouts. That allows you to run the CLI with
kubectl argo rollouts. If you do not use brew, install the plugin manually.
Deploy your application with a Rollout
Argo Rollouts uses a replacement for a Deployment resource: a Rollout. The YAML for a Rollout is almost identical to a Deployment except that the apiVersion and Kind are different. In the spec you can add a strategy section to specify whether you want a blueGreen or a canary rollout. Below is an example of a rollout for a simple API:
apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: superapi spec: replicas: 2 selector: matchLabels: app: superapi template: metadata: labels: app: superapi spec: containers: - name: superapi image: ghcr.io/gbaeke/super:1.0.2 resources: requests: memory: "128Mi" cpu: "50m" limits: memory: "128Mi" cpu: "50m" env: - name: WELCOME valueFrom: configMapKeyRef: name: superapi-config key: WELCOME ports: - containerPort: 8080 strategy: blueGreen: activeService: superapi-svc-active previewService: superapi-svc-preview autoPromotionEnabled: false
You will notice that the blueGreen strategy requires two services: an activeService and a previewService. Both settings refer to a Kubernetes service resource. Below is the activeService (previewService is similar and uses the same selector):
kind: Service apiVersion: v1 metadata: name: superapi-svc-active spec: selector: app: superapi type: ClusterIP ports: - name: http port: 80 targetPort: 8080
The only thing we have to do, in this example, is to deploy the rollout and the two services with kubectl apply. In this post, however, we will use Kustomize to deploy everything.
Deploying a rollout with Kustomize
To deploy the rollout and its services with Kustomize, we can use the kustomization.yaml below:
apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: blue-green nameSuffix: -geba namePrefix: dev- commonLabels: app: superapi version: v1 env: dev configurations: - https://argoproj.github.io/argo-rollouts/features/kustomize/rollout-transform.yaml resources: - namespace.yaml - rollout.yaml - service-active.yaml - service-preview.yaml configMapGenerator: - name: superapi-config literals: - WELCOME=Hello from v1! - PORT=8080
With Kustomize, we can ensure we deploy our resources to a specific namespace. Above, that is the blue-green namespace. We also add a prefix and suffix to the names of Kubernetes resources we create and we add labels as well (commonLabels). For this to work properly with a rollout, you have to add the configurations section. Without it, Kustomize will not know what to do with the rollout resource (kind=rollout).
Note that we also use a configMapGenerator that creates a ConfigMap that sets a welcome message. If you look at the rollout spec, you will see that the pod template uses it to set the WELCOME environment variable. The API that we deploy will respond with that message when you hit the root, for instance with curl.
To deploy with Kustomize, we can run
kubectl apply -k . from the folder holding kustomization.yaml and the manifests in the resources list.
Checking the initial rollout with the UI
When we initially deploy our application, there is only one version of our app. The rollout uses a ReplicaSet to deploy two pods, similarly to a Deployment. Both the activeService and the previewService point to these two pods.
Argo Rollouts has a UI you can start with
kubectl argo rollouts dashboard -n blue-green. The rollout is visualized as below:
In a tool like Octant, the resource viewer shows the relationships between the actual Kubernetes resources:
Above, you can clearly see the Rollout creates a ReplicaSet which, in turn, creates the Pods (click image to enlarge). Both services point to the same pods.
Upgrading to a new version
We will now upgrade to a new version of the application: v2. To simulate this, we can simply modify the WELCOME message in the ConfigMapGenerator in kustomization.yaml. When we run
kubectl apply -k . again, Kustomize will create a new ConfigMap with a different name (containing a hash) and will update that name in the pod template of the rollout. When you update the pod template of the rollout, the rollout knows it needs to upgrade with the blue-green strategy. This, again, is identical to how a deployment behaves. In the UI, we now see:
There are now two revisions, both backed by a ReplicaSet. Each ReplicaSet controls two pods. One set of pods is for the active service, the other set for the preview. We can click on the rollout to see those details:
Above, we can clearly see that revision one is the stable and active service. That is our initial v1 deployment. Revision 2 is the preview service, the v2 deployment. We can port forward to that service and view the welcome message:
In Octant, this is what we see in Resource Viewer:
Above, we can clearly see the rollout now uses two ReplicaSets to run the active and preview pods. The rollout also modified the service selectors and the labels on the pods by adding a label like
rollouts-pod-template-hash:758d6b4845. Each revision has its own hash.
Currently, the rollout is in a paused state. The Argo Rollouts UI shows this but you can also view this with the CLI by running
kubectl argo rollouts get rollout dev-superapi-geba:
Above the status is paused with a message of BlueGreenPause. You can clearly see the green service is the stable and active one (v1) and the blue service is the preview service (v2). We can now promote the preview service to become stable and active.
To promote the service, in the web UI, click Promote and then Sure?. With the CLI, just run
kubectl argo rollouts promote dev-superapi-geba. When you run the get command again, you will see:
Above, you can see the status as ✔️ Healthy. Revision 2 is now stable and active. Revision 1 will be scaled down by setting the number of pods in the ReplicaSet to 0. In the web UI, you now see:
Note that it is still possible to rollback to revision one by clicking the Rollback button or using the CLI. That will keep Revision 2 active and create a Revision 3 for you to preview. After clicking Promote and Sure? again, you will then make Revision 3 active which is the initial v1 service.
If you have the need for blue-green deployments, it is highly recommended to use a progressive delivery controller like Argo Rollouts. It makes the whole process more intuitive and gives you fine control over upgrade, abort, promote and rollback operations. Above, we looked at blue-green with a manual pause, check, and promote. There are other options, such as analysis based on metrics with an automatic promotion that we will look at in later posts.