Building first Helm Chart with Spring Boot Microservices

Share on:

When someone talks about managing the kubernetes cluster then I can very much assume that a particular someone is running a lot of kubectl, kubeadm command on terminal and which is quite obvious because managing kubernetes cluster is not as easy as it seems to be.

Managing kubernets cluster means checking cluster, pods, nodes, application deployment, replicas, load-balancer and the list goes one. So the question which arises in my mind -

Is there an easy way to manage this or at least some part of it?

The answer is Yes and its Helm Chart.

What is Helm Chart?

Helm Chart is an application package manager for the kubernetes cluster, just like we have apt packager manager in Linux.

So what can we do with Helm Chart -

  • Define k8s application
  • Install k8s application
  • Upgrade k8s application

Most important aspect of the Helm Chart is you do not have to use Kubernetes CLI(command line interface) and neither you need to remember complex kubernetes commands to manage the kubernetes manifest.

Lets start playing with Helm Chart.

1. How to Install Helm Chart?

Installing Helm is fairly easy and there are various package manager available for you -

Homebrew

1brew install helm

Chocolatey

1choco install kubernetes-helm

Scoop

1scoop install helm

GoFish

1gofish install helm

Snap

1sudo snap install helm --classic

Binary

If you do not like any of the above packager manager then you could download the binaries as well

  • Get the Binary - Download Binary
  • Unpack it using - tar -zxvf helm-vxxx-xxxx-xxxx.tar.gz
  • Move it - mv linux-amd64/helm /usr/local/bin/helm

Using Script

There is one more way to install latest Helm Version using script. Refer to the following terminal command for installing latest version of Helm -

1$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
2$ chmod 700 get_helm.sh
3$ ./get_helm.sh

Verify Helm Chart Installation

I am assuming you might have choose one of the installation option for Helm Chart.

To verify the installation use the following command

1$ which helm
2/usr/local/bin/helm

2. Let’s create Our First Helm Chart

Before we create a our First Helm Chart, we need to have kubernetes cluster up and running. Use the following command to verify the status of kubernetes cluster -

1$ kubectl get all
1NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
2service/kubernetes   ClusterIP   10.233.0.1   <none>        443/TCP   27d

If your kubernetes cluster is up and running then you should see default service .i.e. service/kubernetes .

If you do not know how to prepare or push your docker image to docker hub then I would highly recommend you to follow my guide from Step 1 to step 7

And for setting up local kubernetes cluster you can refer to - 12 Steps for Installing a Production Ready Kubernetes Cluster.

Well enough with the per-requisites , lets run some Helm Commands for creating our first Helm Chart

1$ helm create springboot

That is it and the basic Helm Chart skeleton with the name springboot is ready.

(Spoiler Alert - We are going to create our first Helm Chart for Springboot application but do not worry the same steps can be used for deploying any other application as well.)

3. Helm Chart Structure

Before we deep dive into the nitty gritty of Helm Chart, let’s go through the Helm Chart Skeleton. Run the following command to see the tree structure of our Springboot Helm Chart -

1$ tree springboot
 1springboot
 2├── Chart.yaml
 3├── charts
 4├── templates
 5│ ├── NOTES.txt
 6│ ├── _helpers.tpl
 7│ ├── deployment.yaml
 8│ ├── hpa.yaml
 9│ ├── ingress.yaml
10│ ├── service.yaml
11│ ├── serviceaccount.yaml
12│ └── tests
13│     └── test-connection.yaml
14└── values.yaml

(*Note - If you do not have tree command installed then use - sudo apt-get install tree or sudo yum -y install tree )

Inside Helm Chart ecosystem we define every configuration as YAML configuration. In the next section we will go through each YAML configuration

Chart.yaml

This file contains all the metadata about our Helm Chart for example -

1apiVersion: v2 #mandatory
2name: springboot #mandatory
3description: A Helm chart for Kubernetes
4type: application
5version: 0.1.0 #mandatory
6appVersion: 1.16.0

You do not need to define each configuration here but you must need to define -

  1. apiVersion
  2. name
  3. version

Other configurations are optional

Versioning - Each chart should has its own version number and it should follow the Semantic Versioning 2.0 aka SemVer 2. But do not get confuse with apiVersion, there are no strict rules for apiVersion.

values.yaml

As the name suggests we have do something with the values and yes you are right about it. This configuration file holds values for the configuration.

Do not worry it is fairly simple to understand once you look at the following values.yaml

 1replicaCount: 1
 2image:
 3  repository: rahulwagh17/kubernetes:jhooq-k8s-springboot #updated url
 4  pullPolicy: IfNotPresent
 5  tag: ""
 6imagePullSecrets: []
 7nameOverride: ""
 8fullnameOverride: ""
 9serviceAccount:
10  create: true
11  annotations: {}
12  name: ""
13podAnnotations: {}
14podSecurityContext: {}
15
16securityContext: {}
17service:
18  type: ClusterIP
19  port: 8080  #updated port
20ingress:
21  enabled: false
22  annotations: {}
23  hosts:
24    - host: chart-example.local
25      paths: []
26  tls: []
27resources: {}
28
29autoscaling:
30  enabled: false
31  minReplicas: 1
32  maxReplicas: 100
33  targetCPUUtilizationPercentage: 80
34
35nodeSelector: {}
36
37tolerations: []
38
39affinity: {}

It looks quite enormous but trust me you do not need to write or remember every configuration by heart. Helm Create command generates the bare minimum values.yaml for you.

Since in this example we are going to try our Helm Chart for spring boot application, lets go through the configuration which we need to modify for deploying our spring boot application.

  1. repository : image:repository: rahulwagh17/kubernetes:jhooq-k8s-springboot
  2. port: 8080

So in the whole values.yaml you need to update the configuration at two places repository and port

Note :You should have a docker image of your spring boot application uploaded into the docker hub

Alright now we are ready with our Chart.yaml and values.yaml, lets jump to next configuration yamls

deployment.yaml

The next configuration we need to update is deployment.yaml and as the name suggest it is used for deployment purpose. So lets see how does it looks like

 1apiVersion: apps/v1
 2kind: Deployment
 3metadata:
 4  name: {{ include "springboot.fullname" . }}
 5  labels:
 6    {{- include "springboot.labels" . | nindent 4 }}
 7spec:
 8{{- if not .Values.autoscaling.enabled }}
 9  replicas: {{ .Values.replicaCount }}
10{{- end }}
11  selector:
12    matchLabels:
13      {{- include "springboot.selectorLabels" . | nindent 6 }}
14  template:
15    metadata:
16    {{- with .Values.podAnnotations }}
17      annotations:
18        {{- toYaml . | nindent 8 }}
19    {{- end }}
20      labels:
21        {{- include "springboot.selectorLabels" . | nindent 8 }}
22    spec:
23      {{- with .Values.imagePullSecrets }}
24      imagePullSecrets:
25        {{- toYaml . | nindent 8 }}
26      {{- end }}
27      serviceAccountName: {{ include "springboot.serviceAccountName" . }}
28      securityContext:
29        {{- toYaml .Values.podSecurityContext | nindent 8 }}
30      containers:
31        - name: {{ .Chart.Name }}
32          securityContext:
33            {{- toYaml .Values.securityContext | nindent 12 }}
34          image: "{{ .Values.image.repository }}"   #update here
35          imagePullPolicy: {{ .Values.image.pullPolicy }}
36          ports:
37            - name: http
38              containerPort: 8080     #update here
39              protocol: TCP           #comment it
40              #livenessProbe:           #comment it
41              #httpGet:           #comment it
42              #path: /           #comment it
43              #port: http           #comment it
44              #readinessProbe:           #comment it
45              #httpGet:           #comment it
46              #path: /           #comment it
47              #port: http           #comment it
48          resources:
49            {{- toYaml .Values.resources | nindent 12  }}
50      {{- with .Values.nodeSelector }}
51      nodeSelector:
52        {{- toYaml . | nindent 8 }}
53      {{- end }}
54      {{- with .Values.affinity }}
55      affinity:
56        {{- toYaml . | nindent 8 }}
57      {{- end }}
58      {{- with .Values.tolerations }}
59      tolerations:
60        {{- toYaml . | nindent 8 }}
61      {{- end }}

Do not get scared we do not need to update each and every configuration. But we need to update the containerPort to 8080

  1. containerPort : 8080

Because we need to deploy our spring boot application at port 8080.

service.yaml

The service.yaml is basically used for exposing our kubernetes springboot deployment as service. The good thing over here we do not need to update any configuration here.

 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: {{ include "springboot.fullname" . }}
 5  labels:
 6    {{- include "springboot.labels" . | nindent 4 }}
 7spec:
 8  type: {{ .Values.service.type }}
 9  ports:
10    - port: {{ .Values.service.port }}
11      targetPort: http
12      protocol: TCP
13      name: http
14  selector:
15    {{- include "springboot.selectorLabels" . | nindent 4 }}

4. Run $ helm template springboot

Now we are into the step no 4 and we are pretty much ready with our first Helm Chart for our spring boot application.

But wait - Wouldn’t it be nice if you could see the service.yaml, deployment.yaml with its actual values before running the helm install command?

Yes you can do that with the helm template command -

1$ helm template springboot

(You can not run the above command from inside the springboot directory, so you should get out from the springboot directory and then execute the command)

After running the above command it should return you will service.yaml, deployment.yaml and test-connection.yaml with actual values

 1---
 2# Source: springboot/templates/serviceaccount.yaml
 3apiVersion: v1
 4kind: ServiceAccount
 5metadata:
 6  name: RELEASE-NAME-springboot
 7  labels:
 8    helm.sh/chart: springboot-0.1.0
 9    app.kubernetes.io/name: springboot
10    app.kubernetes.io/instance: RELEASE-NAME
11    app.kubernetes.io/version: "1.16.0"
12    app.kubernetes.io/managed-by: Helm
13---
14# Source: springboot/templates/service.yaml
15apiVersion: v1
16kind: Service
17metadata:
18  name: RELEASE-NAME-springboot
19  labels:
20    helm.sh/chart: springboot-0.1.0
21    app.kubernetes.io/name: springboot
22    app.kubernetes.io/instance: RELEASE-NAME
23    app.kubernetes.io/version: "1.16.0"
24    app.kubernetes.io/managed-by: Helm
25spec:
26  type: ClusterIP
27  ports:
28    - port: 8080
29      targetPort: http
30      protocol: TCP
31      name: http
32  selector:
33    app.kubernetes.io/name: springboot
34    app.kubernetes.io/instance: RELEASE-NAME
35---
36# Source: springboot/templates/deployment.yaml
37apiVersion: apps/v1
38kind: Deployment
39metadata:
40  name: RELEASE-NAME-springboot
41  labels:
42    helm.sh/chart: springboot-0.1.0
43    app.kubernetes.io/name: springboot
44    app.kubernetes.io/instance: RELEASE-NAME
45    app.kubernetes.io/version: "1.16.0"
46    app.kubernetes.io/managed-by: Helm
47spec:
48  replicas: 1
49  selector:
50    matchLabels:
51      app.kubernetes.io/name: springboot
52      app.kubernetes.io/instance: RELEASE-NAME
53  template:
54    metadata:
55      labels:
56        app.kubernetes.io/name: springboot
57        app.kubernetes.io/instance: RELEASE-NAME
58    spec:
59      serviceAccountName: RELEASE-NAME-springboot
60      securityContext:
61        {}
62      containers:
63        - name: springboot
64          securityContext:
65            {}
66          image: "rahulwagh17/kubernetes:jhooq-k8s-springboot"
67          imagePullPolicy: IfNotPresent
68          ports:
69            - name: http
70              containerPort: 8080
71              protocol: TCP
72          resources:
73            {}
74---
75# Source: springboot/templates/tests/test-connection.yaml
76apiVersion: v1
77kind: Pod
78metadata:
79  name: "RELEASE-NAME-springboot-test-connection"
80  labels:
81    helm.sh/chart: springboot-0.1.0
82    app.kubernetes.io/name: springboot
83    app.kubernetes.io/instance: RELEASE-NAME
84    app.kubernetes.io/version: "1.16.0"
85    app.kubernetes.io/managed-by: Helm
86  annotations:
87    "helm.sh/hook": test-success
88spec:
89  containers:
90    - name: wget
91      image: busybox
92      command: ['wget']
93      args: ['RELEASE-NAME-springboot:8080']
94  restartPolicy: Never

I love this command because it locally renders the templates by replacing all the placeholders with its actual values.

There is one more sanitary command lint provided by helm which you could run to identify possible issues forehand.

1$ helm lint springboot
1==> Linting springboot
2[INFO] Chart.yaml: icon is recommended
3
41 chart(s) linted, 0 chart(s) failed

As you can see the command output 1 chart(s) linted, 0 chart(s) failed. So there is no failure and you are good to go.

5. helm -debug -dry-run

The next check which we are going to do is -dry-run. Helm is full of such useful utility which allows developer to test its configuration before running the final install command

Use the following -dry-run command to verify your Spring Boot Helm Chart

1$ helm install springboot --debug --dry-run springboot

If there is something wrong with your Helm chart configuration then it will going to prompt you immediately.

6. helm install

Alright lets run the install command.

1$ helm install myfirstspringboot springboot

Lets break down the command because if you are doing it for the first time then it might be little confusing for you.

There are two name which we used during the installation command -

  1. myfirstspringboot : It’s a release name for helm chart otherwise helm will generate its own release name that is why we have assigned this name.
  2. springboot : It is our actual chart name which we created in Step 2.

7. Verify the helm install

Next question which comes into my mind - “How to see all the releases? "

Use the following list command to list down all the releases -

1$ helm list -a

It should return you the release .i.e. - myfirstspringboot which we did in the Step 6.

Lets do some cross verification using kubectl commands also, so that we can make sure helm has done its work.

1$ kubectl get all

Now we have installed our first helm chart of springboot application. Lets test the spring boot application by accessing it

1$ curl 10.233.28.240:8080/hello
1Hello - Jhooq-k8s

As you can it return with a response Hello - Jhooq-k8s.

Kudos if you made this far then I would say you learned “How to create and install your first Helm Chart”

8. Upgrade helm release

There is one more feature of Helm Chart which is helm upgrade. It makes it easy for devops to release the new version of application.

Lets take the same myfirstspringboot release and update its replicaCount from 1 to 2

But before we need to update the replicaCount. First we need to udpate the version in Chart.yaml from 0.1.0 to 0.1.1

1apiVersion: v2
2name: springboot
3description: A Helm chart for Kubernetes
4type: application
5version: 0.1.1
6appVersion: 1.16.0

Alright now lets update the replicaCount in values.yaml

 1replicaCount: 2
 2image:
 3  repository: rahulwagh17/kubernetes:jhooq-k8s-springboot
 4  pullPolicy: IfNotPresent
 5  tag: ""
 6imagePullSecrets: []
 7nameOverride: ""
 8fullnameOverride: ""
 9serviceAccount:
10  create: true
11  annotations: {}
12  name: ""
13podAnnotations: {}
14podSecurityContext: {}
15
16securityContext: {}
17service:
18  type: ClusterIP
19  port: 8080
20ingress:
21  enabled: false
22  annotations: {}
23  hosts:
24    - host: chart-example.local
25      paths: []
26  tls: []
27resources: {}
28
29autoscaling:
30  enabled: false
31  minReplicas: 1
32  maxReplicas: 100
33  targetCPUUtilizationPercentage: 80
34
35nodeSelector: {}
36
37tolerations: []
38
39affinity: {}

Okay now we are done with the update and ready to make our new release upgrade. Use the following command -

1$ helm upgrade myfirstspringboot .

You should see a message with your release name .i.e. - Release “myfirstspringboot” has been upgraded. Happy Helming!

Verify your helm upgrade by running following list command

As you can see now the revision count is 2.

Lets again run kubectl get deployment

1$ kubectl get deployments

And now you can see we have successfully upgraded the replica count from 1 to 2.

9. Rollback Helm release

In the Step 8 we upgraded the Helm chart release from version 1 to version 2.

So let’s see one more rollback feature of Helm Chart.

1$ helm rollback myfirstspringboot 1

As you can see from the above screenshot we successfully rolled back the release to the previous version. But one interesting thing about Helm is, it still updates the REVISION to 3

1$ helm list -a

To confirm that we have actually rolled back our Helm Chart release, lets run some kubectl commands

1$ kubectl get deployments

Now as you can see from the screenshot we now have only one replica running for “myfirstspringboot” deployments.

10. Delete Helm release

Wouldn’t it be nice if you need to run only one command to delete your Helm release and you do not have to do anything else.

1$ helm delete myfirstspringboot

As you can see now you have successfully delete your release with single helm delete command.

Summary

As I can summarize what we have learned -

  1. We started from scratch and as a first step we installed Helm Chart on the development box
  2. Then we created our first Helm Chart using $ helm create springboot
  3. After the helm chart creation, we went through the structure of Helm Chart
  4. We explored some of the utility commands provided by the helm .i.e. $ helm template springboot
  5. Then we have seen how to do -dry-run for the Helm Release
  6. After verifying our Helm Chart then we executed helm install $ myfirstspringboot springboot
  7. We used $helm list -a to verify the helm release
  8. Then we have used $ helm upgrade command to upgrade the release
  9. Finally, we rolled back and delete the release

You can follow all the above steps for almost any application but provided you have the docker image uploaded to either docker hub or some other container registry.