Introduce
When our application is running in production, deploying a new version of the application always requires no downtime, there are several ways to do this and one of the ways to help us avoid downtime is Blue/Green Deployment.
In this post we will learn how to do Blue/Green Deployment manually, in the next post I will show you how to do it automatically using Argo Rollouts. This article I refer to from CNCF Presentation Template K8s Deployment.
Steps to follow
In this article, I use Minikube to run Kubernetes Cluster. We will proceed in the following steps:
- Call the instance that is running and receiving traffic from our users as version 1
- We deploy a new version of the application as version 2
- Wait for version 2 to run
- Switch traffic from version 1 to version 2
- Turn off version 1
In Practice
Let’s practice, creating a file named app-v1.yaml
to deploy our application version 1, configure it as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> apps/v1 <span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app<span class="token punctuation">-</span>v1 <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">version</span><span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">template</span><span class="token punctuation">:</span> <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">version</span><span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">annotations</span><span class="token punctuation">:</span> <span class="token key atrule">prometheus.io/scrape</span><span class="token punctuation">:</span> <span class="token string">"true"</span> <span class="token key atrule">prometheus.io/port</span><span class="token punctuation">:</span> <span class="token string">"9101"</span> <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">image</span><span class="token punctuation">:</span> containersol/k8s<span class="token punctuation">-</span>deployment<span class="token punctuation">-</span>strategies <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> http <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> probe <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8086</span> <span class="token key atrule">env</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> VERSION <span class="token key atrule">value</span><span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">livenessProbe</span><span class="token punctuation">:</span> <span class="token key atrule">httpGet</span><span class="token punctuation">:</span> <span class="token key atrule">path</span><span class="token punctuation">:</span> /live <span class="token key atrule">port</span><span class="token punctuation">:</span> probe <span class="token key atrule">initialDelaySeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">periodSeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">readinessProbe</span><span class="token punctuation">:</span> <span class="token key atrule">httpGet</span><span class="token punctuation">:</span> <span class="token key atrule">path</span><span class="token punctuation">:</span> /ready <span class="token key atrule">port</span><span class="token punctuation">:</span> probe <span class="token key atrule">periodSeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> |
In the file above, we declare a Deployment with two labels “app: my-app” and “version: v1.0.0”. I will explain why we need 2 labels.
Next, we create a Service for app-v1 with the file name is service.yaml
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1 <span class="token key atrule">kind</span><span class="token punctuation">:</span> Service <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> NodePort <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> http <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">80</span> <span class="token key atrule">targetPort</span><span class="token punctuation">:</span> http <span class="token comment"># Note here that we match both the app and the version</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">version</span><span class="token punctuation">:</span> v1.0.0 |
The main point here is the selector, as we can see it matches both the app and version labels, and the version label is what we really need to care about. It is the key for us to switch traffic between two versions of the application.
Create Deployment and Service.
1 2 | kubectl apply -f app-v1.yaml <span class="token operator">&&</span> kubectl apply -f service.yaml |
Check all pods are running, if you use Minikube then run as follows:
1 2 3 | <span class="token function">curl</span> <span class="token variable"><span class="token variable">$(</span>minikube <span class="token function">service</span> my-app --url<span class="token variable">)</span></span> <span class="token number">2022</span>-10-03T20:16:04+07:00 - Host: host-1, Version: v1.0.0 |
Otherwise, you can use port-forward.
1 2 | kubectl port-forward <span class="token operator"><</span>name of pod<span class="token operator">></span> <span class="token number">8080</span>:8080 |
After checking that our version 1 application is running, please turn off port-forward because we will continue to use it later.
Next, we will deploy the new version of the application (version 2). Create a file namedapp-v2.yaml
with config as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> apps/v1 <span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app<span class="token punctuation">-</span>v2 <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">version</span><span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">template</span><span class="token punctuation">:</span> <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">version</span><span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">annotations</span><span class="token punctuation">:</span> <span class="token key atrule">prometheus.io/scrape</span><span class="token punctuation">:</span> <span class="token string">"true"</span> <span class="token key atrule">prometheus.io/port</span><span class="token punctuation">:</span> <span class="token string">"9101"</span> <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>app <span class="token key atrule">image</span><span class="token punctuation">:</span> containersol/k8s<span class="token punctuation">-</span>deployment<span class="token punctuation">-</span>strategies <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> http <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> probe <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8086</span> <span class="token key atrule">env</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> VERSION <span class="token key atrule">value</span><span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">livenessProbe</span><span class="token punctuation">:</span> <span class="token key atrule">httpGet</span><span class="token punctuation">:</span> <span class="token key atrule">path</span><span class="token punctuation">:</span> /live <span class="token key atrule">port</span><span class="token punctuation">:</span> probe <span class="token key atrule">initialDelaySeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">periodSeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">readinessProbe</span><span class="token punctuation">:</span> <span class="token key atrule">httpGet</span><span class="token punctuation">:</span> <span class="token key atrule">path</span><span class="token punctuation">:</span> /ready <span class="token key atrule">port</span><span class="token punctuation">:</span> probe <span class="token key atrule">periodSeconds</span><span class="token punctuation">:</span> <span class="token number">5</span> |
You notice in the section labels, we will declare the label version as v2.0.0
. Next, we deploy the application version 2.
1 2 | kubectl apply -f app-v2.yaml |
Wait for all Pods to be running state before we continue.
1 2 | kubectl rollout status deploy my-app-v2 -w |
Check that the application version 2 was able to receive traffic.
1 2 | kubectl port-forward my-app-v2 <span class="token number">8080</span>:8080 |
Open another terminal and access the application version 2 and make sure it can receive traffic from the user.
1 2 | <span class="token function">curl</span> localhost:8080 |
If the version 2 application has run successfully and is able to receive traffic, then the next most important part is how to switch the traffic from version 1 to version 2. To do that, we simply update the label of the Service above to version 2. We can edit the YAML file or do it quickly with the patch command.
1 2 | kubectl patch <span class="token function">service</span> my-app -p <span class="token string">'{"spec":{"selector":{"version":"v2.0.0"}}}'</span> |
At this time, the traffic of the application is switched from version 1 to version 2.
Let’s test.
1 2 3 | <span class="token function">curl</span> <span class="token variable"><span class="token variable">$(</span>minikube <span class="token function">service</span> my-app --url<span class="token variable">)</span></span> <span class="token number">2022</span>-10-03T20:30:54+07:00 - Host: host-1, Version: v2.0.0 |
Ok, if you see the result is Version: v2.0.0
, then we have successfully implemented Blue/Green Deployment. If something happens and you want to go back to version 1, we simply update the label version again.
1 2 | kubectl patch <span class="token function">service</span> my-app -p <span class="token string">'{"spec":{"selector":{"version":"v1.0.0"}}}'</span> |
Finally, we turn off the application version 1.
1 2 | kubectl delete deploy my-app-v1 |
Done. Please like the DevOps VN page to receive notifications of the earliest posts.
Conclusion
So we have learned how to implement Blue/Green Deployment, as you can see, it’s not very complicated. But this is just a way to practice playing to know, so in the next post, we will learn how to implement Blue/Green Deployment for a real project with Argo Rollouts.