Manage Kubernetes applications using Helm
The easy way.
Kubernetes is the most popular open source project on Github. It has captured the imagination of developers. Every other organisation is either in the process of moving their deployments to Kubernetes or planning to do in near future.
I remember when I started my journey with Kubernetes almost a year back, I was super excited about all this container orchestration thing. We started with 3–4 micro-services and our Jenkins pipelines had kubectl to deploy our Kubernetes resources. kubectl is a great tool to start with Kubernetes but as we moved along in our journey kubectl was not enough. We then started exploring Helm.
In this blog, I will get into details of Helm. We will start with what is Helm, why & how it can help you to manage our applications on Kubernetes.
I assume you have basic knowledge of Kubernetes resources like deployment, services etc and minikube. If not, then i would suggest to read below writing by Shekhar Gulati
Let’s start by setting up the overall agenda for this post first:-
- What is Helm?
- How Helm works?
- Then, we will cover all the different use cases where helm can help
- And finally, we will create & deploy helm charts
Let’s get started!
Helm is a Cloud Native Computing Foundation incubating project. It is built by developers from Google, Microsoft, Samsung, and along with developers from many other organizations. It has a thriving community.
There is an interesting blog on helm’s official website about its overall journey.
What is Helm?
Helm is a tool which helps to bind your Kubernetes resources into a bundle and then deploy it to Kubernetes cluster.
For example let’s suppose you have a Kava Microservice written in Spring Boot. The Kubernetes objects we will need are:
- A deployment object
- ConfigMap if you have any configuration data
- A secret if there are any credentials required for this app
- Kubernetes service of any type like NodePort, ClusterIp or Headless
- Finally there can be other objects too according to your requirement like ingress, ServiceAccount etc
To keep things simple, we can assume we have three Kubernetes object — Deployment, ConfigMap, and a Service. These objects need to be in sync with each other, for example if Deployment object require some env variable name X from ConfigMap, then it is expected that ConfigMap have the value of X. In the same way Kubernetes services works upon label selectors. So if we have removed a label from deployment object, then same label should be removed form service object too. So, the point here is that Kubernetes objects for a particular service are dependent to each other.
But maintaining this dependency among Kubernetes objects becomes really tough when you have a lot of Kubernetes resources. It is almost impossible to keep track of every released object.
Here Helm comes to rescue by automatically packaging your app’s Kubernetes resources into one bundle called Helm Chart and deploy it on Kubernetes as a Helm Release with a specific revision number and chart version.
Helm gives everything necessary to keep track of your app’s deployment on Kubernetes.
I would sum up with saying that helm is packaging and deployment tool for kubernetes resources.
High level architecture of Helm
Now, that we have understood what is Helm and what it is offers. Let’s understand its high level architecture.
Helm is a client-server based tool. So there two main components in Helm architecture:
Helm Client:- Helm client is a command line tool and has to be installed on developer machine or Jenkins machine in case of CI/CD.
Helm client can be used to:-
- Create Helm chart for a Kubernetes application.
- Push/Pull helm charts to helm repositories(Helm repositories are like docker hub or Github where you can actually store your helm charts.)
- Interact with helm server(Tiller) to get information, deploy, uninstall, rollback a helm chart.
Helm Server(Tiller):- Tiller is a server side component of helm which is basically responsible to perform task asked by helm client.
Tiller is installed on Kubernetes cluster as a pod and require permissions to install and uninstall helm charts on the Kubernetes cluster.
Installation:- The best way to install helm client and tiller server is to follow instruction available on the Helm official website
Key terms in Helm
Helm Chart: Chart is basically bundle of all of the Kubernetes resources/objects for an application. This objects are written in YAML just like Kubernetes objects. But the values for the keys of YAML files can be configured from different configs or values.yaml files. What are the config of values.yaml files?
Helm Config/Values:- Kubernetes resources are written in YAML, which have key-values pair. So values for the different Kubernetes properties in Kubernetes resource definition vary by environments. For example in secret.yaml file , the database password for DEV environment will be different from UAT environment. So, config or values files are the files which contains the values or configuration data you need at the time of deployment of your helm chart.
We will see how we can configure our data for various Kubernetes objects with different value files.
Helm Release:- When you deploy your helm chart on Kubernetes cluster using helm client it will send the chart data which includes Kubernetes resources definition and values/config to tiller. Tiller will then deploy these resources to Kubernetes cluster. This deployed instance of your helm chart is called a release. Every helm chart release should have a unique name mostly similar to name of your application.
Helm Revision:- Whenever tiller deploy a release it gives it a revision number through which we can easily keep tracks of deployment of our helm chart.It is a counter which get incremented on every successful install/upgrade/rollback of your helm chart.
Chart version:- Every helm chart have a default chart version when you create a helm chart using helm client. And for best practice you should maintain the chart number of your helm chart whenever you modify your helm chart.
Now that we have fair idea about the Helm architecture and the key terms related , we can get our hands dirty by playing around with helm.
So I assume you have running minikube cluster on your machine and you have installed helm client and tiller using the instructions link i pasted earlier.
So lets start with a very basic command:-
> helm version
Client: &version.Version{SemVer:"v2.12.1", GitCommit:"02a47c7249b1fc6d8fd3b94e6b4babf9d818144e", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}
Helm version command gives the version of helm client on your machine and and tiller installed Kubernetes cluster.
> helm ls
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
The helm ls command will give information about all the releases installed on your Kubernetes cluster. Since we have not installed any of the release using helm client it is giving blank results as of now.
Let’s learn Helm by using a hello-world application. I created a Github project that we will be using as our example use-case.
Let’s first understand what we have in this application. It is a simple example of Microservice architecture where in I have created two services.
- World -Service:- When you call this service on / address it will return a string as “world”
- Hello-Service:- When you call this service on / address it will return a string as “hello world” . This service actually makes a call to world-service and get message “world”. And append it will “hello” to return full sentence as “hello-world”. If it can not connect with world service then it return message as “World-Service is not reachable.”
Github repo includes the source for two Microservices and their corresponding Kubernetes resources. I will focus on the Kubernetes objects and helm charts only. There are all the instruction available to build these code using gradle in the README file.
Let’s explore the Kubernetes objects of each Microservices:
Hello-Service:- Hello service have 3 main k8s objects as deployment, ConfigMap, and service.
These are available at :-
helloservice/src/main/resources/kubernetes/
Have a look at these three yaml files in above directory:-
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-service-hello-service
namespace: demo
labels:
release: stable
app.kubernetes.io/name: hello-service
app.kubernetes.io/instance: hello-service
spec:
selector:
matchLabels:
release: stable
app.kubernetes.io/name: hello-service
replicas: 1
template:
metadata:
labels:
release: stable
app.kubernetes.io/name: hello-service
spec:
containers:
- name: hello-service
image: singhravindra/hello-service:latest
env:
- name: WORLD_SERVICE_URL
valueFrom:
configMapKeyRef:
name: hello-service-hello-service
key: world_service_address
- name: MESSAGE
valueFrom:
secretKeyRef:
name: hello-service-hello-service
key: message
ports:
- containerPort: 8080
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-service-hello-service
data:
message: hello
world_service_address: http://world-service-world-service:80
service.yaml
kind: Service
apiVersion: v1
metadata:
name: hello-service-hello-service
namespace: demo
labels:
release: stable
instance: hello-service
spec:
selector:
release: stable
app.kubernetes.io/name: hello-service
ports:
- protocol: TCP
port: 80
targetPort: 8080
name: http
type: NodePort
As you can see in deployment.yaml file we have a deployment object which have two environment variables linked to the configmap. Configmap have the actual values for these environment variables. Then, we have service and label selector of service object have the exact same key-value pair that we have in the deployment template spec.
To deploy these objects on Kubernetes using kubectl:-
> kubectl create namespace demo> cd helloservice/src/main/resources/kubernetes/> kubectl create -f ./
configmap/hello-service-hello-service created
deployment.apps/hello-service-hello-service created
service/hello-service-hello-service created
You can see all the 3 objects have been created.
Similarly you can deploy the world service too.
> cd worldservice/src/main/resources/kubernetes/> kubectl create -f ./
configmap/world-service-world-service created
deployment.apps/world-service-world-service created
service/world-service-world-service created
Let’s access these service using curl.
Get Minikube IP:-
> minikube ip
192.168.99.100
Get the nodeport of our hello-service using kubectl.
> kubectl get service -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-service-hello-service NodePort 10.104.136.224 <none> 80:31087/TCP 3m4s
world-service-world-service NodePort 10.106.24.93 <none> 80:31526/TCP 8s
NodePort of hello-service is 31087 and world-service is 31536.
> curl 192.168.99.100:31087/
hello world> curl 192.168.99.100:31536/
world
So as you can see we are able to deploy our both microservices and are able able to access them using curl.
Now why we need helm?
So all these 3 Kubernetes resources we have deployed for each microservices have there own unique identity in Kubernetes and there is no such thing which picturize them to be together or linked to a single microservice. You can not even rollback to a specific version of your Kubernetes application because you do not have any version history. And what if let’s suppose the values in configmap differ by environment env for example the values of MESSAGE field in configmap of hello-service needs to be removed in dev env but should remain in UAT and PROD env.
All these problems can be solved if we bundle up our all the 3 Kubernetes resources and give them a single identity — helm release name, and keep track of the various deployed version by using Revision number. And as we discussed earlier Tiller does all this for you.
So let’s start by using helm to deploy and track our Microservices.
First let’s cleanup what we have done so far.
> kubectl delete namespace demo
namespace "demo" deleted> kubectl create namespace demo
namespace/demo created
This will delete entire namespace along with all the resource we had deployed earlier and again create a fresh namespace.
Let’s create helm chart for hello-service:
> cd /hello-service/src/main/resources/helm/
> helm create hello-service
Creating hello-service
This will create a default helm chart as below directory structure:-
hello-service/
|
|- .helmignore # Contains patterns to ignore when packaging Helm charts.
|
|- Chart.yaml # Information about your chart
|
|- values.yaml # The default values for your templates
|
|- charts/ # Charts that this chart depends on
|
|- templates/ # The template files
|
|- templates/tests/ # The test files
We will not cover every little thing about this chart directory because my goal here is to enable you to create helm chart for your own applications but I would suggest to read https://helm.sh/docs/developing_charts/#charts and get know about everything about chart’s directory in detail.
If you go to templates directory, you will see four main files created there as below:-
templates/
|
|- _helpers.tpl
|- deployment.yaml
|- service.yaml
|- ingress.yaml
So in the templates folder you put all your kubernetes resource files, like for our example we had three files for deployment, configmap and service. We don’t require ingress so we can delete this file. And since configmap file is not created by default so we will just create a configMap.yaml file with below content.
confgiMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-{{ .Values.configMap.name }}
data:
message: {{ .Values.configMap.data.message }}
world_service_address: {{ .Values.configMap.data.world_service_address }}
So now we have there main YAML files in template folder of our hello-service chart.
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "hello-service.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "hello-service.name" . }}
helm.sh/chart: {{ include "hello-service.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "hello-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "hello-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "hello-service.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "hello-service.name" . }}
helm.sh/chart: {{ include "hello-service.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "hello-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
configMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-{{ .Values.configMap.name }}
data:
message: {{ .Values.configMap.data.message }}
world_service_address: {{ .Values.configMap.data.world_service_address }}
And all these have the default key-values pair created by helm only. So let’s just modify deployment and service files according to our requirement. We have our plane Kubernetes object definition in /kubernetes folder which we have deployed previously. So let’s just convert those into template files by taking values from {{ .values.* }} directives.
So the modified deployment and service files will look something like this:-
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Values.deployment.name }}
namespace: {{ .Values.namespace }}
labels:
release: {{ .Values.release.type }}
app.kubernetes.io/name: {{ include "hello-service.name" . }}
helm.sh/chart: {{ include "hello-service.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
istio: {{ include "hello-service.name" . }}
app: {{ include "hello-service.name" . }}
spec:
selector:
matchLabels:
release: {{ .Values.release.type }}
app.kubernetes.io/name: {{ include "hello-service.name" . }}
istio: {{ include "hello-service.name" . }}
app: {{ include "hello-service.name" . }}
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
release: {{ .Values.release.type }}
app.kubernetes.io/name: {{ include "hello-service.name" . }}
istio: {{ include "hello-service.name" . }}
app: {{ include "hello-service.name" . }}
spec:
containers:
- name: {{ .Values.deployment.name }}
image: {{ .Values.deployment.image }}
env:
- name: WORLD_SERVICE_URL
valueFrom:
configMapKeyRef:
name: {{ .Release.Name }}-{{ .Values.configMap.name }}
key: world_service_address
- name: MESSAGE
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-{{ .Values.secret.name }}
key: message
ports:
- containerPort: {{ .Values.deployment.port }}
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
service.yaml
kind: Service
apiVersion: v1
metadata:
name: {{ .Release.Name }}-{{ .Values.service.name }}
namespace: {{ .Values.namespace }}
labels:
release: {{ .Values.release.type }}
helm.sh/chart: {{ include "hello-service.chart" . }}
instance: {{ .Release.Name }}
managed-by: {{ .Release.Service }}
spec:
selector:
release: {{ .Values.release.type }}
app.kubernetes.io/name: {{ include "hello-service.name" . }}
ports:
- protocol: TCP
port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
name: http
type: {{ .Values.service.type }}
You must be wondering about things in {{ }} . So if you went through the guide about helm chart which i shared above then you must know the {{}} is template directive which is replaced by the values at the time of deployment of helm chart.
For example {{ .Release.Name }} will be replaced by the release name when you will deploy this helm chart. This we will see when we will deploy this helm chart. But i will recommend you go through that chart guide and know the basic details of helm chart.
Similarly {{ .Values }} refers the values from values.yaml file or the config file of your helm chart.
I have made a little advanced helm chart with basic k8s resource deployment, configmap, service, secret and horizontalautoscaler for both the microservices.
And i have created values.yaml file too. So if you check the values.yaml files for hello-service :-
# Default values for hello-service.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
namespace: demo
replicaCount: '1'
deployment:
name: hello-service
image: singhravindra/hello-service:latest
port: '8080'
secret:
name: hello-service
data:
message: 'aGVsbG8='
configMap:
name: hello-service
data:
world_service_address: 'http://world-service-world-service:80'
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
service:
name: hello-service
type: NodePort
port: '80'
targetPort: '8080'
ingress:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
paths: []
hosts:
- chart-example.local
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
istio:
enabled: true
virtualservice:
name: hello-service
release:
type: stable
So {{ .Values.deployment.name }} in the deployment file will be replaced by
deployment:
name: hello-service
at the time of deployment.
So if you need to rename you deployment at any point of time you just change it values in values.yaml file only.
You can create multiple values.yaml file for different environments like value-dev.yaml or values-uat.yaml etc.
So all the field in all the resources files with {{ .Values. }} have their corresponding values in the values.yaml file which is basic the config file of any helm release.
Now as we have helm chart ready with all the values in our values.yaml file. Let just deploy it.
> cd hello-service/src/main/resources/helm
> helm install --name=hello-service --namespace=demo -f hello-service/values.yaml hello-service
NAME: hello-service
LAST DEPLOYED: Sun Sep 1 01:09:42 2019
NAMESPACE: demo
STATUS: DEPLOYEDRESOURCES:
==> v1/ConfigMap
NAME DATA AGE
hello-service-hello-service 2 0s==> v1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
hello-service-hello-service 0/1 1 0 0s==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
hello-service-hello-service-574c5f547-cvwd2 0/1 ContainerCreating 0 0s==> v1/Secret
NAME TYPE DATA AGE
hello-service-hello-service Opaque 1 0s==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-service-hello-service NodePort 10.111.163.208 <none> 80:31959/TCP 0s==> v2beta1/HorizontalPodAutoscaler
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hello-service-hello-service Deployment/hello-service-hello-service <unknown>/20% 1 5 0 0sNOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace demo -o jsonpath="{.spec.ports[0].nodePort}" services hello-service)
export NODE_IP=$(kubectl get nodes --namespace demo -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
Magic, isn’t it!
Let’s first understand the helm install command first.Although the detailed guide you can read at https://helm.sh/docs/helm/#helm-install and the basic thing which we have used above are as below:-
— name=hello-service :- This is how we give a release name to our release. Here hello-service is our release name.
— namespace=demo :- This is how we specify our namespace where all the resource has to be created.
-f hello-service/values.yaml :- This is the path from where {{ }} template directive will fill in all the values in our template files. As we can multiple values files so we can just provide required values files for different envs.
hello-service/ :- This is the path of our helm chart.
IF-Else in yaml files:- Let’s suppose you want some if else kind of things in the yaml files of your k8s resources. Helm template supports all these things too. For example in our case we have a resource called virtualservice.yaml, which is basiclly a istio resource, and we want it should be only installed if istio is enabled in our kubernetes cluster. So let’s have a look how we have enabled this requirement:-
{{- if .Values.istio.enabled }}
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: {{ .Release.Name }}-{{ .Values.istio.virtualservice.name }}
labels:
release: {{ .Values.release.type }}
helm.sh/chart: {{ include "hello-service.chart" . }}
instance: {{ .Release.Name }}
managed-by: {{ .Release.Service }}
spec:
hosts:
- "*"
gateways:
- http-gateway
http:
- match:
- uri:
prefix: /
route:
- destination:
port:
number: 80
host: {{ .Release.Name }}-{{ .Values.service.name }}.{{ .Values.namespace }}.svc.cluster.local
{{- end }}
So if you see the first line of virtualservice.yaml file it says if .Values.istio.enabled then only include the below block. And if you check the values of .Values.istio.enabled in values.yaml file :-
istio:
enabled: false
virtualservice:
name: hello-service
So at any point of time we can change it to true and redepoly it and next time virtualservice will be created along with all the other things.
This was one simple example of helm template, every one have their own different requirement and helm can used to implement them.
Anyways lets just deploy our world-service.
> cd world-service/src/main/resources/helm
> helm install --name=world-service --namespace=demo -f world-service/values.yaml world-service
NAME: world-service
LAST DEPLOYED: Sun Sep 1 01:27:33 2019
NAMESPACE: demo
STATUS: DEPLOYEDRESOURCES:
==> v1/ConfigMap
NAME DATA AGE
world-service-world-service 1 0s==> v1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
world-service-world-service 0/1 1 0 0s==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
world-service-world-service-db59f49d8-n8hq4 0/1 ContainerCreating 0 0s==> v1/Secret
NAME TYPE DATA AGE
world-service-world-service Opaque 1 0s==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
world-service-world-service NodePort 10.102.136.226 <none> 80:30873/TCP 0s==> v2beta1/HorizontalPodAutoscaler
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
world-service-world-service Deployment/world-service-world-service <unknown>/20% 1 5 0 0sNOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace demo -o jsonpath="{.spec.ports[0].nodePort}" services world-service)
export NODE_IP=$(kubectl get nodes --namespace demo -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
Now we have deployed both of the microservice using helm and we can access them using their nodeport.
Let’s use some basic helm commands to play with helm:-
helm ls
helm ls — namespace=demo
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
hello-service 1 Sun Sep 1 01:09:42 2019 DEPLOYED hello-service-0.1.0 1.0 demo
world-service 1 Sun Sep 1 01:27:33 2019 DEPLOYED world-service-0.1.0 1.0 demo
Now you can see their are two releases are deployed on our kubernetes cluster in demo namespace. And you can see their tracking details like revision, app version,chart version, deployed time etc.
helm upgrade
Let’s upgrade our world-service and change the message return from it from “world” to “world 2.0” . so our end goal is that when we will deploy 0.1.1 version of world service, then the actual message returned from world-servic ewill be “world 2.0” instead of “world”.
As the message is coming form env variable linked to the world-service secret and if we change the secret values from “ world” to “world 2.0” then it should work.
And if you check the secret.yaml file in template folder of helm chart. You will see that the value of MESSAGE if actually coming from {{ .Values.secret.data.message }} .
So we have to just change the value in values.yaml file as below:-
secret:
name: world-service
data:
message: 'd29ybGQgMi4w'
d29ybGQgMi4w is base64 encoded value of “world 2.0”.
And then deploy it again using helm with new values.
helm upgrade world-service -f world-service/values.yaml world-service/ — namespace=demo
Release “world-service” has been upgraded. Happy Helming!
LAST DEPLOYED: Sun Sep 1 01:37:44 2019
NAMESPACE: demo
STATUS: DEPLOYEDRESOURCES:
==> v1/ConfigMap
NAME DATA AGE
world-service-world-service 1 10m==> v1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
world-service-world-service 1/1 1 1 10m==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
world-service-world-service-db59f49d8-n8hq4 1/1 Running 0 10m==> v1/Secret
NAME TYPE DATA AGE
world-service-world-service Opaque 1 10m==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
world-service-world-service NodePort 10.102.136.226 <none> 80:30873/TCP 10m==> v2beta1/HorizontalPodAutoscaler
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
world-service-world-service Deployment/world-service-world-service <unknown>/20% 1 5 1 10mNOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace demo -o jsonpath="{.spec.ports[0].nodePort}" services world-service)
export NODE_IP=$(kubectl get nodes --namespace demo -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
By this we have upgraded our world service and lets just check its revision number :-
> helm ls — namespace=demo
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
hello-service 1 Sun Sep 1 01:09:42 2019 DEPLOYED hello-service-0.1.0 1.0 demo
world-service 2 Sun Sep 1 01:37:44 2019 DEPLOYED world-service-0.1.0 1.0 demo
Now you can see that revision number of world-service is 2.
Now just restart the world-service pod so that it can fetch new values of message env variable from secret. And hit nodeport of hello-service from browser or using curl.
curl “192.168.99.100:31959/”
hello world 2.0
As you can see response has been changed from “hello world” to “hello world 2.0”
helm rollback
Now let’s suppose you want to rollback you world-service to revision number 1 which has message as “world”.
helm rollback world-service 1
Rollback was a success! Happy Helming!
that’s it!
You just have to give release name and the revision number on which you want to rollback.
Again restart the world-service pod and hit hello-service nodeport using curl.
curl “192.168.99.100:31959/”
hello world
it’s back to “hello-world”.
helm history
With this command you can check all the history of any release.
helm history world-service
REVISION UPDATED STATUS CHART DESCRIPTION
1 Sun Sep 1 01:27:33 2019 SUPERSEDED world-service-0.1.0 Install complete
2 Sun Sep 1 01:47:07 2019 SUPERSEDED world-service-0.1.0 Upgrade complete
3 Sun Sep 1 01:49:27 2019 DEPLOYED world-service-0.1.0 Rollback to 1
As you can see this command shows entire history if any release, it is showing when we have upgraded and when we have roll back our world-service.
So with this i think we have covered our agenda of this post let’s have a recap what we have done so far:-
- We explored what is helm and it’s architecture
- We explored various basic terminology about helm like chart,release and config.
- We learned how we can create a helm chart and what a helm chart looks like.
- Then we deployed two Microservices with their helm charts.
- We explored how we can use helm template to perform logical operation in yaml files.
- We then upgraded release and then again rollback it to previous version.
- We checked history of one release.
Do comment your thoughts, queries or suggestion.
Happy Helming!