Mauricio M. Ribeiro

@maumribeiro

Canary Release with Kubernetes

March 28th 2017

Safely and easily deploying new version of your web app

Canary Release is the technique that we use to “softly” deploy a new version of an application into Production. It consists of letting only a part of the audience get access to the new version of the app, while the rest still access the “old” version one. This is very useful when we want to be sure about stability in case of any changes which may be breaking, and have big side effects.

The point is: canary release has never been easy to be put into practice. Depending on the environment we have, it can take so long to be put in place that we often prefer to leave this away.

However, with Docker containers and Kubernetes orchestration it is quite friendly to do that. Here I am going to show how to do a small canary deployment of a new version of a dummy website using this tool.

Those are the versions of our website:

On the left, musicstore v1; on the right, the new version v2

Setup environment

In order to use Kubernetes locally, we are going to install minikube and kubectl. We are going also need to install Docker.

With everything installed, we launch minikube in order to have Kubernetes up and running:

minikube start

As a response, the terminal will display:

Starting local Kubernetes cluster...
Starting VM...
SSH-ing files into VM...
Setting up certs...
Starting cluster components...
Connecting to cluster...
Setting up kubeconfig...
Kubectl is now configured to use the cluster.

Second step: call this command in order to run Docker inside Minikube:

eval $(minikube docker-env)

Setting up DB and backend

For this app, the backend side will be an API developped in Go, querying against a Mongo DB. We are not going into details about this part, as it is not the goal here to show how to develop a REST API in Go.

To deploy the backend, clone the repository and build the docker image with the command below:

docker build -t api-musicstore .

Optional step: insert some data in the DB:

Enter the docker container with the following command:

docker exec -t -i <mongo_container> /bin/bash

where <mongo_container> is the id of the container running against the mongo image. You can discover it after running

docker ps
CONTAINER ID        IMAGE                                                        COMMAND                  CREATED             STATUS              PORTS               NAMES
3304ee6ab94e api-musicstore:v1 "/go/bin/musicstore" 3 minutes ago Up 4 minutes k8s_backend.72a7365d_api-musicstore-931708012-g4grg_musicstore_442ad9d4-123c-11e7-ae23-0800275d1cac_9f1e978d
75b2eca8cf1f mongo "docker-entrypoint..." 4 minutes ago Up 4 minutes k8s_db.b9d32dcd_api-musicstore-931708012-g4grg_musicstore_442ad9d4-123c-11e7-ae23-0800275d1cac_67653438

In this case, <mongo_container> is 75b2eca8cf1f. So we run:

docker exec -t -i 75b2eca8cf1f /bin/bash

Inside the container, we open mongo command-line tool with the following command:

mongo

Once we are inside mongo, copy-paste the following contents and hit Enter:

That’s it! Now we have some seed data in our DB, let’s now deploy our REST API that will communicate with the website!

Deploying the backend: REST API

Normally, I like to organize my deployments and services inside namespaces. Here, I create a namespace musicstore to deploy my Music Store dummy application.

To create this namespace, copy this file locally and run the following command:

kubectl create -f namespace.yaml

After, copy these files and run the following command in order to create the API/DB deployment in Kubernetes:

kubectl create -f deployment.json
Note: configuration files for Kubernetes can be created in both structures JSON and YAML.

In order to create replica controllers, run this command:

kubectl create -f rc.yaml

Finally, we can create the service, and our backend is ready to run:

kubectl create -f service.json

We can see that our API is well deployed consulting the URL and opening it in a web browser:

minikube service api-musicstore --url --namespace=musicstore
http://<url>:32495
Don’t forget to specify the namespace musicstore, otherwise kubectl is going to search for the service in the default namespace and it is not going to find it!

Deploying version 1 of the Music Store dummy website

Now we are going to build the image for the version 1 of the website. In order to do this, you are going to need to clone this repository and run the following command:

docker build -t musicstore:v1 .

With our container built, we can proceed with the deployment. First, we create our service:

kubectl -f service.yaml

This will create the musicstore-website service. We are going to create the deployments, so that we can deploy our code to be served by this service. Let’s get its URL and keep it for later testing:

minikube service musicstore-website --url --namespace=musicstore
http://<url>:32000

Let’s take a look at the deployment config file: we are going to ask Kubernetes to create 3 replicas of this website in order to handle traffic and load balancing. All of the replicas will be, in a first moment, pointing to the image corresponding to the image of version v1 of the musicstore-website:

The deployment file of version 1 of our Music Store

Let’s deploy it:

kubectl create -f deployment-without-canary.json

Now, if you open the website you are going to see its version 1:

Website after first deployment

Deploying version 2 of the Music Store dummy website using canary deployment

First step in order to deploy version 2 is to create a docker image for it, just like we did for version 1.

So, copy the contents from here and run the following command in a terminal:

docker build -t musicstore:v2 .

As seen before, we created 3 replicas of our front-end service. As explained before, the idea of canary deployment is getting a part of the traffic and redirect it to a new version.

We are going to achieve this pointing one of the replicas of our service to the new version of the Music Store. Like this, most part of the traffic will still be redirected to version 1.

To achieve this, we run the following commands:

kubectl apply deployment.json
kubectl apply deployment-canary.json

With this command, we tell Kubernetes to apply this deployment to our environment. If we take a look in the file, we see that the only thing it changes regarding the deployment of version 1 is the label “canary” (the other version is considered “stable”), and the number of replicas (1 to the new version, and 2 serving the old one). And of course, it points to the Docker image musicstore:v2:

The Canary Release deployment file of version 2

And here is how the deployment of version 1 looks like, with 2 replicas pointing to the stable version:

Canary Release: depoloyment of version 1 with 2 replicas instead of 3

Now, we can see that some of the traffic are already seeing the version 2 of our Music Store dummy app:

Musicstore version 2

Rolling out the new version

Now we feel safe and sound to go for the new version. With the canary delpoyment, this is kind of easy: we just need to set all of the replicas to point to a stable version running the image of the version 2 of the Music Store:

kubectl apply deployment-new-version.json

This is how our deployment-new-version.json will look like:

Rollout file of version 2

Very good! Now, everybody will be able to see the new version of the Music Store web site!

All the files used in this tutorial can be found here.

A great example of Canary Release can be found here as well.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Mauricio M. Ribeiro

More Related Stories