Authoring a Helm chart

I'm currently in the process of writing a Helm chart for the PostgreSQL administration tool pgAdmin. I've only recently had time to sit down and play with Helm and by no stretch of the imagination claim to be an expert. However, I thought I'd write a short blog documenting my learnings thus far.

Helm is a light-weight package management layer that builds on top of standard Kubernetes templates. Helm introduces a few extra files but if you're experienced authoring Kubernetes templates then there is nothing to fear.

Firstly, you'll need to install Helm onto your local machine and Helm's server side component (Tiller) onto your Kubernetes cluster. Instructions of how to do this are available here: https://github.com/kubernetes/helm

Once you've got Helm installed, you can deploy pre-built Helm charts onto your Kubernetes cluster using the following command:

helm install stable/mysql  

A list of stable and incubator charts can be found on the public Kubernetes GitHub page. If you find a chart for the service you wish to deploy, then you can simply go ahead and utilise it. If you need to extend it or customise it then simply fork it on GitHub and add your modifications.

If, however, you do not find the service you are looking for, then you may need to author your own.

Helm allows you to quickly scaffold a new chart using the helm create mychart command. This will create you a directory structure as follows:

Chart.yaml  
values.yaml  
charts/  
templates/  
     - deployment.yaml
     - ingress.yaml
     - service.yaml
     - _helpers.tpl
     - NOTES.txt
Chart.yaml

This is the top level metadata file for your chart. Here you'll define things like the chart's name, version, description and links back to the chart's source code.

values.yaml

This file is where you'll provide configuration values that can be injected into your Kubernetes templates at install time.

charts/

The charts folder is where you can embed any other chart's that your chart is dependent on. However, it looks like this approach is being phased out in favour of the requirements.yaml file which can bring in dependencies for you. See this document for further details.

templates/

The templates folder is the home of your Kubernetes template files. Whether your defining deployments, secrets, ingress or any other resource type, you'll be doing so in here.

templates/_helpers.tpl

You may notice this file in the templates folder. This file defines a couple of useful functions that will be evaluated at install time to expose commonly used variables, such as the chart's name.

templates/NOTES.txt

The contents of this file will be echoed to the user's terminal after a successful chart installation. You should use this file to give the user useful hints about what to do next post-install.

NOTES:  
1. Get the application URL by running these commands:  
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get svc -w eating-greyhound-pgadmin'
  export SERVICE_IP=$(kubectl get svc --namespace default eating-greyhound-pgadmin -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  echo http://$SERVICE_IP:5050
Golang Templating

If, like me, you're reasonably new to golang. Then you may be a little confused by all the {{...}} scattered throughout the files. Golang comes with a built in templating engine that allows for compilation time variable and function expansion. Various projects such as Sprig have then added additional functionality to this templating engine to make it super powerful. Helm, being one of the top contributors to the Sprig project, make heavy use of this templating throughout their scaffolded charts. It's worth spending a little time getting your head around how this works and what functions/variables are available within the scope of your scaffolded Helm chart.

Configuration Values

We've already discussed that most of your chart's install time configuration is going to be inside the values.yaml file. If you open up that file, you'll see it's a simple YAML key-value file. You can define custom values by extending or replacing the exsting key-value pairs.

...
image:  
  repository: postgres
  tag:
  pullPolicy: IfNotPresent
...

These values will then get injected into the various templates in the templates directory. Have a look in each of the template files and map {{ .Values.* }} directives back to the values file. This should give you a sense of how you can extend the values file to configure the template files to suit your scenario.

Secrets

Although you could simply define secret values, such as database passwords, in your values.yaml file. You may want to store them in a Kubernetes Secret resource. To do this, create a new secret.yaml file in the templates/ folder similar to the following:

apiVersion: v1  
kind: Secret  
metadata:  
  name: {{ template "fullname" . }}
  labels:
    app: {{ template "fullname" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
type: Opaque  
data:  
  database-password: {{ default "admin" .Values.db.password | b64enc | quote }}

Inside your deployment.yaml, you can then reference the secret value as a ENV var using something similar to:

containers  
- name: {{ .Chart.Name }}
  image ...
  env:
          - name: DEFAULT_USER
            value: {{ .Values.database.username }}
          - name: DEFAULT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: {{ template "fullname" . }}
                key: database-password
   ...

As you can see above, the configuration value database.username is coming from the values.yaml file, but the database.password value is coming from the secrets resource.

Extending Templates

Remember, these are just standard Kubernetes template files. By default there is a deployment, service and ingress. If you need to add more functionality like persistent volumes, etc. then simply add the relevant yaml files to the template folder.
You can check that your chart is syntactically correct at any point by running helm lint.

Package and Deploy

When you're ready to deploy the chart to your cluster, simply package it into a tarball.

helm package .  

This will create a .tgz archive in the local directory, you can then install this chart onto your Kubernetes cluster.

helm install mychart-0.1.0.tgz  

If you want to override any of the values in the values.yaml file, you can do so with arguments.

helm install --set db.username=myuname,db.password=mypw mychart-0.1.0.tgz  

Assuming the install goes successfully, your deployment will now be running on your Kubernetes cluster. You can see previous Helm installs using helm list.

At this point, you can continue to develop your chart, iteratively deploying to the cluster to validate it works as expected.

P.S.

The pgAdmin Helm chart that I'm working on is currently available here: https://github.com/jjcollinge/pgadmin-chart. It's very primitive so any issues or contributions are welcome.