A quick guide to designing charts in Helm

Unsplash by Frank Eiffert



Helm is a powerful tool for applying, updating and managing applications in Kubernetes. The Helm community creates many open source charts. You can deploy Redis, Nginx or Prometheus Operator with a single command. And they come with everything you need, like Ingress.



The Mail.ru Cloud Solutions team hastranslated an article describing a quick way to create a basic chart, showing useful commands and sharing best practices. He does not dwell on aspects of the Go templating language, as most of them are covered in the Helm documentation. This tutorial provides more abstract aspects and ideas for improving your workflow.



Creating a basic chart structure



Start with a simple command that will create an example chart structure:



$ helm create basic
Creating basic
$ tree basic
basic/
β”œβ”€β”€ charts
β”œβ”€β”€ Chart.yaml
β”œβ”€β”€ templates
β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”œβ”€β”€ _helpers.tpl
β”‚   β”œβ”€β”€ ingress.yaml
β”‚   β”œβ”€β”€ NOTES.txt
β”‚   β”œβ”€β”€ serviceaccount.yaml
β”‚   β”œβ”€β”€ service.yaml
β”‚   └── tests
β”‚       └── test-connection.yaml
└── values.yaml


That's all it takes to create a deployment-ready chart. This chart allows you to deploy an application with all the necessary components. If you take a look at values.yaml, you can see that this application will deploy Nginx.



Expanding a chart is as easy as creating:



$ helm install basic


The template team is your best friend



Immediately before installation and after changes to the chart, you should check if everything is handled properly in the templates.



To check what exactly will be deployed to the cluster, use the command:



$ helm template basic


The command will output every YAML generated by all templates. If you only want to see the result of one template, use:



$ helm template basic -x templates/service.yaml


The result will be something like this:



---
# Source: basic/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-basic
  labels:
    app.kubernetes.io/name: basic
    helm.sh/chart: basic-0.1.0
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/managed-by: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: basic
    app.kubernetes.io/instance: release-name


To test a template with custom values ​​use:



$ helm template basic -x templates/service.yaml -f \ mypath/tocustomvalues.yaml


The generated template can be tested on the cluster using the command:



$ helm install basic --dry-run --debug


LINT!



Before submitting to the repository, you can add one more step to clearly check the code - linting (statistical analysis):



$ helm lint basic/
==> Linting basic/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, no failures


Helm works with functions



If you look into the directory of chart templates, you can see _helpers.tpl. Here you can add your functions, they will be available throughout the chart. An example function might look like this:



{{/*
Expand the name of the chart.
*/}}
{{- define "basic.name" -}} 
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 
{{- end -}}


The use of a function in a template is indicated using a function include:



app.kubernetes.io/name: {{ include "basic.name" . }}


Meta labels



An important part of Kubernetes is the correct use of labels. This is very important if you are using Helm to deploy multiple manifests. To easily add tags to find Helm-managed resources, you can use your own functions:



{{/*
Common labels
*/}}
{{- define "basic.labels" -}} 
app.kubernetes.io/name: {{ include "basic.name" . }}
helm.sh/chart: {{ include "basic.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}


And now it's easy to add a few tags to the chart:



apiVersion: v1
kind: Service
metadata:
  name: {{ include "basic.fullname" . }}
  labels:
{{ include "basic.labels" . | indent 4 }}
...


If you want to find services created using this chart, then use the command:



$ kubectl get svc -l helm.sh/chart=basic-0.1.0


Comments will save you



There are two types of comments:



  • # Is a simple comment that remains in the resulting YML after processing.
  • {{- / * ... * / -}} is a comment that is discarded by the templating engine.




# app files volume
        {{- /*
          App files are configmaps created by cloud-app-play-files chart.
          App files contains files specific for app and environment.
          App name should be same as in deployment of cloud-app-play-files chart.
        */ -}} 
        {{- if .Values.include.appDir }}
        - name: {{ $appName }}-files
          configMap:
            name: {{ $appName }}-files


The template output will be like this:



       # app files volume
       - name: app-notification-files
         configMap:
           name: app-notification-files


As you can see, the generated manifest contains only a simple type comment. Which type to use is up to you. But template comments are useful if you are defining more complex pipelines or dependencies in YAML.



It is also important to remember that comments starting with # are parsed too. If you put a Go template in a comment, it will be evaluated. So comments can be templates too.



Be sure to keep documentation



Chart documentation is indispensable, especially if you want to publish a chart. The easiest way to create and maintain documents is to use the Golang package named helm-docs . With it you can generate a README.md containing tables of values, versions and descriptions from values.yaml and chart.yaml, or use other custom files.





Example taken from https://github.com/norwoodj/helm-docs



As you can see, adding the name with - in the comments results in one row in the table. In addition, the table contains additional information such as chart description, name, and version. Helm-docs can be integrated into a pre-commit along with linting. It's easy to do this:



$ helm-docs
INFO[2020-07-23T15:30:38+02:00] Found Chart directories [.]                 
INFO[2020-07-23T15:30:38+02:00] Generating README Documentation for chart .


The magic of subcharts



When your chart becomes the architecture-making monster, it's best to split some of the chart components into smaller ones. These are called subcharts or child charts.



Subcharts are deployed at the same time as the main chart. Values ​​for subcharts can be provided in the same values.yaml file as for the main chart. You can also connect them from GitHub, but for the article I will create a subchart locally.



To get started with a new subchart that the main chart depends on, create a charts directory inside the main chart folder. Then create a basic chart:



$ mkdir charts
$ cd charts
$ helm create subbasic


To connect a subchart, change the definition of the main chart:



apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: basic
version: 0.1.0
dependencies:
  - name: subbasic       


Now, every time the command is launched helm install, not only the main chart is expanded, but also the subchart. To override the subchart reference name when deploying to the service, add the following commands to the values.yaml of the main chart:



subbasic:
 service:
   type: NodePort
 nameOverride: "jojo"


Now run the command templateand see the modified output of the subchart service. The service type will change along with the name:



---
# Source: basic/charts/subbasic/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-jojo
  labels:
    app.kubernetes.io/name: jojo
    helm.sh/chart: subbasic-0.1.0
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/managed-by: Tiller
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: jojo
    app.kubernetes.io/instance: release-name


Important note: subcharts cannot take values ​​from parent charts.



Moments that are often forgotten



A few more points:



  1. Resource names - 63 characters maximum.
  2. Resource names can only be numbers, lowercase letters, "-" or ".".
  3. Chart size - no more than 1 MB . This is especially important if you are using file attachments.
  4. The chart has a parsing function .tpl.
  5. You can specify the resources that will remain after the command has removed the deployment of the chart helm delete.


Well that's all!



Now you can write your first chart. Attaching files is worth mentioning - charts are not suitable for adding files and saving their structure in directories. But on the recommendation page, you won't find information on what to chart and what not.



Helm is a fairly young tool, but with a lot of potential. Don't forget that linting, documentation generation, and even dry-run templates on a cluster can be part of the CI. Helm Workflow is already available for GitHub .



Good luck!



What else to read:



  1. Helm device and its pitfalls .
  2. Auto-generation of secrets in Helm .
  3. Our channel Around Kubernetes in Telegram .



All Articles