Kubernetes blue/green deployment

Kubernetes has a really awesome built-in feature called Deployments.Deployments come with the ability to do rolling updates of containers when you update your application to a new version. Rolling updates are a great way to update applications because your app uses about the same amount of resources during an update as it does when not updating, all with minimal impact to performance and availability. 

However, there are many legacy applications out there that don't work well with rolling updates. Some applications simply need to deploy a new version and cut over to it right away. For this, we need to perform a blue/green deployment. With blue/green deployments a new copy of the application (green) is deployed alongside the existing version (blue). Then the ingress/router to the app is updated to switch to the new version (green). You then need to wait for the old (blue) version to finish the requests sent to it, but for the most part traffic to the app changes to the new version all at once.

The Blue Deployment

  A Kubernetes deployment specifies a group of instances of an application. Behind the scenes, it creates a replica set which is responsible for keeping the specified number of instances up and running.

We can create our "blue" deployment by saving the following yaml to a file blue.yaml

# cat blue.yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  name: nginx-1.10

spec:

  replicas: 2

  template:

    metadata:

      labels:

        name: nginx

        version: "1.10"

    spec:

      containers: 

        - name: nginx

          image: nginx:1.10

          ports:

            - name: http

              containerPort: 80

Create the deployment using the kubectl command

# kubectl apply -f blue.yaml

deployment "nginx-1.10" created

Once we have a deployment we can provide a way to access the instances of the deployment by creating a Service. Services are decoupled from deployments so that means that you don't explicitly point service at a deployment. What you do instead is specify a label selector which is used to list the pods that make up the service. When using deployments, this is typically set up so that it matches the pods for a deployment.

In In this case we have two labels, name=nginx and version=1.10. We will set these as the label selector for the service below. Save this to service.yaml.

# catservice.yaml

apiVersion: v1

kind: Service

metadata:

name: nginx

labels:

run: nginx

spec:

type: NodePort

ports:

  - port: 8080

targetPort: 80

protocol: TCP

name: http

  - port: 443

protocol: TCP

name: https

selector:

run: nginx

version: "1.10"

type: LoadBalancer

Creating the service will create a load balancer that is accessible outside the cluster.

# kubectl apply -f service.yaml

service "nginx" created

 we can test that the service is accessible and get the version.

# EXTERNAL_IP=$(kubectl get svc nginx -o jsonpath="{.status.loadBalancer.ingress[*].ip}")

# curl -s http://$EXTERNAL_IP/version | grep nginx

# kubectl get services

NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE

kubernetesClusterIP      10.96.0.1                443/TCP                        22h

my-nginxClusterIP      10.100.110.46            80/TCP                         18h

my-nginx-web-service   NodePort       10.97.35.15              80:32683/TCP                   22h

nginx                  NodePort       10.105.211.91            8080:32715/TCP,443:32580/TCP   5m9s

nginxservice           LoadBalancer   10.105.239.232   10.9.54.100   82:31691/TCP                   21h

# kubectl describe service nginx

Name:                     nginx

Namespace:                default

Labels:                   run=nginx

Annotations:              kubectl.kubernetes.io/last-applied-configuration

{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"run":"nginx"},"name":"nginx","namespace":"default"},"spec":{"p...

Selector:                 run=nginx

Type:                     NodePort

IP:                       10.105.211.91

Port:                     http  8080/TCP

TargetPort:               80/TCP

NodePort:                 http  32715/TCP

Endpoints:               

Port:                     https  443/TCP

TargetPort:               443/TCP

NodePort:                 https  32580/TCP

Endpoints:               

Session Affinity:         None

External Traffic Policy:  Cluster

Events:                   

# curl -s http://10.105.125.248:80

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and

working. Further configuration is required.

Thank you for using Nginx.

Now we are ready to deploy a new version.

Update the application :

A new Deployment will be created to update the application and the Service will be updated to point at the new version.

Create the Green Deployment 

The Green Deployment is created by updating to the next version. An entirely new Deployment will be created with different labels. Note that these labels don't match the Service yet and so requests will not be sent to pods in the Deployment

# cat green.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-1.11

spec:

selector:

matchLabels:

run: nginx-1.11

replicas: 2

template:

metadata:

labels:

run: nginx-1.11

spec:

containers:

        - name: nginx

image: nginx:1.11

ports:

        - name: http

          containerPort: 80

create the new deployment

# kubectl apply -f green.yaml

deployment "nginx-1.11" created

Now we have two deployments but the service is still pointing to the "blue" one.

To cut over to the "green" deployment, we will update the selector for the service. Edit the service.yaml and change the selector version to "1.11". That will make it so that it matches the pods on the "green" deployment.

# cat service.yaml

apiVersion: v1

kind: Service

metadata:

  name: nginx

  labels:

    run: nginx

spec:

  type: NodePort

  ports:

  - port: 8080

    targetPort: 80

    protocol: TCP

    name: http

  - port: 443

    protocol: TCP

    name: https

  selector:

    run: nginx

version: "1.11"

type: LoadBalancer

This apply will update the existing Nginx service in place.

# kubectl apply -f service.yaml

service "nginx" configured

Updating the selector for the service is applied immediately and so you should see that the new version of nginx is serving traffic

# EXTERNAL_IP=$(kubectl get svc nginx -o jsonpath="{.status.loadBalancer.ingress[*].ip}")

# curl -s http://$EXTERNAL_IP/version | grep nginx

Output json format

# curl -s http://10.105.125.248

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

Welcome to nginx!

If you see this page, the Nginx web server is successfully installed and

working. Further configuration is required.

For online documentation and support please refer to

nginx.org.
="http:>

Commercial support is available at

nginx.com.="http:>

Thank you for using nginx.

To check how many pods are running

# kubectl get pods

NAME                          READY     STATUS    RESTARTS   AGE

nginx-1.10-57f589cd6-27cdn    1/1       Running   0          2h

nginx-1.10-57f589cd6-lkbwr    1/1       Running   0          2h

nginx-1.11-68d55ccbfd-bcgkm   1/1       Running   0          1h

nginx-1.11-68d55ccbfd-h6hst   1/1       Running   0          1h

nginx-1.11-68d55ccbfd-pl67f   1/1       Running   0          1h

services

# kubectl get service

NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE

kubernetesClusterIP      10.96.0.1                443/TCP                        23h

my-nginxClusterIP      10.100.110.46            80/TCP                         19h

my-nginx-web-service   NodePort       10.97.35.15              80:32683/TCP                   23h

nginx                  NodePort       10.105.211.91            8080:32715/TCP,443:32580/TCP   73m

nginxservice           LoadBalancer   10.105.239.232   10.9.54.100   82:31691/TCP                   23h

Clean up the deployment

# kubectl get deployment

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE

my-nginx                0/2     2            0           19h

my-nginx-web-service1   0/2     2            0           23h

nginx-1.10              0/2     2            0           18h

nginx-1.11              0/2     2            0           8m38s

# kubectl delete deployment nginx-1.10

deployment "nginx-1.10" deleted

# kubectl delete deployment nginx-1.11

deployment "nginx-1.11" deleted

# kubectl get deployment

No resources found.

# kubectl get pods

No resources found.

# kubectl get service

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE

kubernetesClusterIP      10.96.0.1                443/TCP        3d

nginxLoadBalancer   10.105.125.248        80:32405/TCP   2h

# kubectl delete service nginx

service "nginx" deleted

# kubectl get service

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE

kubernetesClusterIP   10.96.0.1            443/TCP




Relevant Blogs:

Kubernetes Cheat sheet  

Config map 

canary deployment with istio   

How-to Guide for Archiving to AWS S3 Glacier

Recent Comments

No comments

Leave a Comment