In our first installment of this blog series we introduced you to Helm and gave you an overview. Need a quick refresh?
Read “Simplify Kubernetes deployments with Helm (Part 1)”
Now let’s dig a little deeper. Let’s investigate the Chart we have been using for our example. Instead of installing the Chart like we did in the first part, now we just want to download it so we use the fetch option of the Helm command:
helm fetch stable/mysql
This will download a compressed tarball of the mysql chart into the current directory. Extract and untar it and you will see the following structure:
.
├── Chart.yaml
├── README.md
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── pvc.yaml
│ ├── secrets.yaml
│ └── svc.yaml
└── values.yaml
As we wrote in the first blog in this series, a Helm Chart packages everything to create a deployment, or as Helm calls it, a release of our application. Let us now focus on service and the Kubernetes deployment creation — two of the most important building blocks of Kubernetes.
So let’s have a look at the templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
ports:
- name: mysql
port: 3306
targetPort: mysql
selector:
app: {{ template "fullname" . }}
This represents, of course, a fairly standard k8s service description, with the exception that most of the parameters still need to be rendered. So let’s look at the metadata part of this template. Most of these derive from what Helm calls predefined values. First, we see that the values necessary to generate the label named “chart” are stored in the Chart.yaml
file .Chart.Version: 0.2.6
and .Chart.Name: mysql.
The values for the release and heritage labels are set when the Helm install/upgrade command is being executed. Here is an example print out for the .Release map, when we ran the helm init
command:
Name:joyous-pike
Time:seconds:1500244897
nanos:353332089
Namespace:default
IsUpgrade:false
IsInstall:true
Revision:1
Service:Tiller
The service name parameter uses the variable “fullname” as it is defined in the _helpers.tpl
templates, a file you can find in almost all charts. For more information on the file naming conventions see the best practices guide
(https://github.com/kubernetes/helm/tree/master/docs/chart_best_practices)
We can see the result of the rendered service template, when we run the command (Note: don’t go into the details of Kubernetes services, we are only interested in how templates are rendered):
kubectl get services joyous-pike-mysql -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: 2017-07-09T06:09:43Z
labels:
app: joyous-pike-mysql <- fullname
chart: mysql-0.2.6 <- .Chart.Name-.Chart.Version
heritage: Tiller <- .Release.Service
release: joyous-pike <- .Release.Name
name: joyous-pike-mysql <- fullname
namespace: default
resourceVersion: "203260"
selfLink: /api/v1/namespaces/default/services/joyous-pike-mysql
uid: 33549c4a-646d-11e7-821b-08002744cb40
spec:
clusterIP: 10.0.0.210
ports:
- name: mysql
port: 3306
protocol: TCP
targetPort: mysql
selector:
app: joyous-pike-mysql <- fullname
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
One option we haven’t used is setting parameters via the values.yml
file, which we can do now as we check how the k8s deployment is being created. So let’s look at the deployment.yml
file. The metadata part is pretty much the same as it is with services, so we skip that and instead go to the very end of the file first. Remember how we created a mysql deployment in the Helm example in our first part? Well, here is the code that allowed us to deploy mysql with or without a persistent volume:
volumes:
- name: data
{{- if .Values.persistence.enabled }} <- is set in the values.yaml file (default)
persistentVolumeClaim:
claimName: {{ template "fullname" . }}
{{- else }}
emptyDir: {}
{{- end -}}
This is a simple example for a conditional statement, but this allows us to deploy our application in various environments, and even use it locally when we don’t have or need access to a persistent volume.
Let’s go back up a bit to where the containers for the pod are defined:
containers:
- name: {{ template "fullname" . }}
image: "{{ .Values.image }}:{{ .Values.imageTag }}"
imagePullPolicy: {{ .Values.imagePullPolicy | quote }}
resources:
{{ toYaml .Values.resources | indent 10 }}
As in the volume example parameter, values are set in the values.yaml
, but we see a few more constructs. The first one {{ .Values.imagePullPolicy | quote }}
puts quotes around the parameter, so the rendered yaml is compliant. The same is true for the second, more complex example {{ toYaml .Values.resources | indent 10 }}
. Whereas toYaml makes sure the value in .Values.resources
is read as valid yaml, indent 10
helps to ensure the rendered yaml file is formatted correctly.
As the last example, we want to glance over the use of the default
function:
- name: MYSQL_USER
value: {{ default "" .Values.mysqlUser | quote }}
Here the default is set to an empty string unless .Values.mysqlUser
is set either in the values.yaml
file or via --set
in the command line. The result of this is then put in quotes.
Here is a plugin that will make developing Helm Charts a little easier while working on a customer project that will use Helm extensively I found a great plugin that can help you with developing Helm configuration files. Perhaps a bit confusing, it’s called template. The installation is very simple:
helm plugin install https://github.com/technosophos/helm-template
It renders chart templates locally and displays the output while not requiring Tiller or any access to a Kubernetes cluster. The simplest way to run it is to execute the following command in your Charts directory:
helm template .
So now we have seen how to specify parameters for a particular Helm release. We showed some simple examples that can, of course, be applied to other Kubernetes components such as stateful and daemon sets. In the next installment in this series, we will work with secrets and configmaps, and show more complex use cases of the Helm building blocks.