Official Helm Chart Template Guide Summary

Balman RawatBalman Rawat
15 min read

Command Cheat sheet

https://helm.sh/docs/intro/cheatsheet/

Add/Search/Install/Uninstall repo

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update bitnami
helm repo list

helm install wordpress bitnami/wordpress
helm list
helm status wordpress
helm get all wordpress
helm get values wordpress
helm uninstall wordpress #--keep-history uninstall while keeping history

Search Repositories

helm search repo
helm search hub
helm search bitnami
helm search repo wordpress
helm search hub wordpress

Installation Order Helm

  • namespace
  • NetworkPolicy

  • ResourceQuota

  • LimitRange

  • PodSecurityPolicy

  • PodDisruptionBudget

  • ServiceAccount

  • Secret

  • SecretList

  • ConfigMap

  • StorageClass

  • PersistentVolume

  • PersistentVolumeClaim

  • CustomResourceDefinition

  • ClusterRole

  • ClusterRoleList

  • ClusterRoleBinding

  • ClusterRoleBindingList

  • Role

  • RoleList

  • RoleBinding

  • RoleBindingList

  • Service

  • DaemonSet

  • Pod

  • ReplicationController

  • ReplicaSet

  • Deployment

  • HorizontalPodAutoscaler

  • StatefulSet

  • Job

  • CronJob

  • Ingress

  • APIService

Customizing Chart before Installing

helm show values bitnami/wordpress

More Installation Methods

The helm install command can install from several sources:

  • A chart repository (as we've seen above)

  • A local chart archive (helm install foo foo-0.1.1.tgz)

  • An unpacked chart directory (helm install foo path/to/foo)

  • A full URL (helm install foo https://example.com/charts/foo-1.2.3.tgz)

'helm upgrade' and 'helm rollback':

An upgrade takes an existing release and upgrades it according to the information you provide. Because Kubernetes charts can be large and complex, Helm tries to perform the least invasive upgrade.

NOTE: It will only update things that have changed since the last release.

To see new upgrade too place:

helm get values wordpress

To rollback:

helm rollback wordpress 1

Helpful Options for Install/Upgrade/Rollback

  • --timeout

  • --wait It will wait for as long as the --timeout value. If timeout is reached, the release will be marked as FAILED.

  • --no-hooks: This skips running hooks for the command

  • --recreate-pods (only available for upgrade and rollback)

Creating your own chart

helm create webhotel
helm package webhotel
helm install my-hotel ./webhotel

## debugging chart
helm install --debug --dry-run goodly-guppy ./webhotel

NOTES:

  • By virtue of the fact that this file is in the mychart/templates/ directory, it will be sent through the template engine.

  • We recommend using the extension .yaml for YAML files and .tpl for helpers.

  • The template directive {{ .Release.Name }} injects the release name into the template.

  • The Release object is one of the built-in objects for Helm

Built-in Objects

Objects are passed into a template from the template engine. And your code can pass objects around. There are even a few ways to create new objects within your templates, like with the tuple .

Objects can be simple, and have just one value. Or they can contain other objects or functions. For example, the Release object contains several objects (like Release.Name) and the Files object has a few functions.

NOTE: The built-in values always begin with a capital letter.

Release Object

This object describes the release itself. It has several objects inside of it:

  • Release.Name

  • Release.Namespace

  • Release.IsUpgrade: true if the current operation is an upgrade or rollback.

  • Release.IsInstall: true if the current operation is an install.

  • Release.Revision: The revision number for this release. On install, this is 1, and it is incremented with each upgrade and rollback.

  • Release.Service: The service that is rendering the present template. On Helm, this is always Helm.

Values Object

Values passed into the template from the values.yaml file and from user-supplied files. By default, Values is empty.

Chart Object

The contents of the Chart.yaml file. Any data in Chart.yaml will be accessible here. For example {{ .Chart.Name }}-{{ .Chart.Version }} will print out the mychart-0.1.0.

The available fields are listed in the Charts Guide

Subchart Object

This provides access to the scope (.Values, .Charts, .Releases etc.) of subcharts to the parent. For example .Subcharts.mySubChart.myValue to access the myValue in the mySubChart chart.

Files Object

This provides access to all non-special files in a chart. While you cannot use it to access templates, you can use it to access other files in the chart. See the section Accessing Files for more.

    • Files.Get is a function for getting a file by name (.Files.Get config.ini)

      • Files.GetBytes is a function for getting the contents of a file as an array of bytes instead of as a string. This is useful for things like images.

      • Files.Glob is a function that returns a list of files whose names match the given shell glob pattern.

      • Files.Lines is a function that reads a file line-by-line. This is useful for iterating over each line in a file.

      • Files.AsSecrets is a function that returns the file bodies as Base 64 encoded strings.

      • Files.AsConfig is a function that returns file bodies as a YAML map.

Capabilities Object

This provides information about what capabilities the Kubernetes cluster supports.

    • Capabilities.APIVersions is a set of versions.

      • Capabilities.APIVersions.Has $version indicates whether a version (e.g., batch/v1) or resource (e.g., apps/v1/Deployment) is available on the cluster.

      • Capabilities.KubeVersion and Capabilities.KubeVersion.Version is the Kubernetes version.

      • Capabilities.KubeVersion.Major is the Kubernetes major version.

      • Capabilities.KubeVersion.Minor is the Kubernetes minor version.

      • Capabilities.HelmVersion is the object containing the Helm Version details, it is the same output of helm version.

      • Capabilities.HelmVersion.Version is the current Helm version in semver format.

      • Capabilities.HelmVersion.GitCommit is the Helm git sha1.

      • Capabilities.HelmVersion.GitTreeState is the state of the Helm git tree.

      • Capabilities.HelmVersion.GoVersion is the version of the Go compiler used.

Template Object

Contains information about the current template that is being executed

    • Template.Name: A namespaced file path to the current template (e.g. mychart/templates/mytemplate.yaml)

      • Template.BasePath: The namespaced path to the templates directory of the current chart (e.g. mychart/templates).

Values Files

This object provides access to values passed into the chart. Its contents come from multiple sources:

  • The values.yaml file in the chart

  • If this is a subchart, the values.yaml file of a parent chart

  • A values file if passed into helm install or helm upgrade with the -f flag (helm install -f myvals.yaml ./mychart)

  • Individual parameters passed with --set (such as helm install --set foo=bar ./mychart)

NOTE: values.yaml is the default, which can be overridden by a parent chart's values.yaml, which can in turn be overridden by a user-supplied values file, which can in turn be overridden by --set parameters.

Deleting a default:

helm install stable/drupal --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null

Template Functions and Pipelines

So far, we've seen how to place information into a template. But that information is placed into the template unmodified. Sometimes we want to transform the supplied data in a way that makes it more useable to us.

Template Function

List of available functions: https://helm.sh/docs/chart_template_guide/function_list/

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ quote .Values.favorite.drink }}
  food: {{ quote .Values.favorite.food }}

Template Function syntax

functionName arg1 arg2...

Pipelines

Pipelines are an efficient way of getting several things done in sequence. Let's rewrite the above example using a pipeline.


apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  food: {{ .Values.favorite.food | upper | quote }}

### above snippet will be translated to below👇 yaml
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: melting-porcup-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"

Using Default Function

drink: {{ .Values.favorite.drink | default "tea" | quote }}
drink: {{ .Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }}

In some places, an if conditional guard may be better suited than default.\

Using the lookup function

The lookup function can be used to look up resources in a running cluster. The synopsis of the lookup function is lookup apiVersion, kind, namespace, name -> resource or resource list

BehaviorLookup function
kubectl get pod mypod -n mynamespacelookup "v1" "Pod" "mynamespace" "mypod"
kubectl get pods -n mynamespacelookup "v1" "Pod" "mynamespace" ""
kubectl get pods --all-namespaceslookup "v1" "Pod" "" ""
kubectl get namespace mynamespacelookup "v1" "Namespace" "" "mynamespace"
kubectl get namespaceslookup "v1" "Namespace" "" ""

Example:

(lookup "v1" "Namespace" "" "mynamespace").metadata.annotations

NOTE: To test lookup against a running cluster, helm template|install|upgrade|delete|rollback --dry-run=server should be used

Operators are functions

For templates, the operators (eq, ne, lt, gt, and, or and so on) are all implemented as functions. In pipelines, operations can be grouped with parentheses ((, and )).

Flow Control

Helm's template language provides the following control structures:

  • if/else for creating conditional blocks

  • with to specify a scope

  • range, which provides a "for each"-style loop

If/Else

{{ if PIPELINE }}
  # Do something
{{ else if OTHER PIPELINE }}
  # Do something else
{{ else }}
  # Default case
{{ end }}

## Example
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}

### Values
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: "true"

Modifying scope using with

The next control structure to look at is the with action. This controls variable scoping. Recall that . is a reference to the current scope. So .Values tells the template to find the Values object in the current scope.

###Syntax:
{{ with PIPELINE }}
  # restricted scope
{{ end }}

### Example
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  release: {{ .Release.Name }}

Looping with the range action

##Example
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}   

## Tuple example
sizes: |-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}

In addition to lists and tuples, range can be used to iterate over collections that have a key and a value (like a map or dict).

Variables

In Helm templates, a variable is a named reference to another object. It follows the form $name. Variables are assigned with a special assignment operator: :=. We can rewrite the above to use a variable for Release.Name.

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- $relname := .Release.Name -}}
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ $relname }}
  {{- end }}

ariables are particularly useful in range loops. They can be used on list-like objects to capture both the index and the value:

  toppings: |-
    {{- range $index, $topping := .Values.pizzaToppings }}
      {{ $index }}: {{ $topping }}
    {{- end }}

#result
toppings: |-
      0: mushrooms
      1: cheese
      2: peppers
      3: onions

For data structures that have both a key and a value, we can use range to get both. For example, we can loop through .Values.favorite like this:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}

#result
apiVersion: v1
kind: ConfigMap
metadata:
  name: eager-rabbit-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

Variables are normally not "global". They are scoped to the block in which they are declared. Earlier, we assigned $relname in the top level of the template. That variable will be in scope for the entire template. But in our last example, $key and $val will only be in scope inside of the {{ range... }}{{ end }} block.

However, there is one variable that is always global - $ -this variable will always point to the root context.

Named Template

It is time to move beyond one template, and begin to create others. In this section, we will see how to define named templates in one file, and then use them elsewhere. A named template (sometimes called a partial or a subtemplate) is simply a template defined inside of a file, and given a name. We'll see two ways to create them, and a few different ways to use them.

NOTE: An important detail to keep in mind when naming templates: template names are global. If you declare two templates with the same name, whichever one is loaded last will be the one used. Because templates in subcharts are compiled together with top-level templates, you should be careful to name your templates with chart-specific names.

Partials and _ files

  • Most files in templates/ are treated as if they contain Kubernetes manifests

  • The NOTES.txt is one exception

  • But files whose name begins with an underscore (_) are assumed to not have a manifest inside. These files are not rendered to Kubernetes object definitions, but are available everywhere within other chart templates for use.

Declaring and using templates with define and template

{{- define "MY.NAME" }}
  # body of template here
{{- end }}

### Example
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

Now we can embed this template inside of our existing ConfigMap, and then include it with the template action:

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}

## Result
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2016-11-02
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

NOTE: Note: a define does not produce output unless it is called with a template, as in this example.

Conventionally, Helm charts put these templates inside of a partials file, usually _helpers.tpl. Let's move this function there:

The include function

Because template is an action, and not a function, there is no way to pass the output of a template call to other functions; the data is simply inserted inline.

To work around this case, Helm provides an alternative to template that will import the contents of a template into the present pipeline where it can be passed along to other functions in the pipeline.

Here's the example above, corrected to use indent to indent the mychart.app template correctly

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
{{ include "mychart.app" . | indent 4 }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
{{ include "mychart.app" . | indent 2 }}

NOTE: It is considered preferable to use include over template in Helm templates simply so that the output formatting can be handled better for YAML documents.

Accessing Files Inside Templates

https://helm.sh/docs/chart_template_guide/accessing_files/

Sometimes it is desirable to import a file that is not a template and inject its contents without sending the contents through the template renderer. Helm provides access to files through the .Files object.

NOTES

  • It is okay to add extra files to your Helm chart. These files will be bundled. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects.

  • Some files cannot be accessed through the .Files object, usually for security reasons.

    • Files in templates/ cannot be accessed.

    • Files excluded using .helmignore cannot be accessed.

    • Files outside of a Helm application subchart, including those of the parent, cannot be access

Basic Examples

onfig1.toml:

message = Hello from config 1

config2.toml:

message = This is config 2

config3.toml:

message = Goodbye from config 3

Each of these is a simple TOML file (think old-school Windows INI files). We know the names of these files, so we can use a range function to loop through them and inject their contents into our ConfigMap.

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- $files := .Files }}
  {{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
  {{ . }}: |-
        {{ $files.Get . }}
  {{- end }}

### Result
apiVersion: v1
kind: ConfigMap
metadata:
  name: quieting-giraf-configmap
data:
  config1.toml: |-
        message = Hello from config 1

  config2.toml: |-
        message = This is config 2

  config3.toml: |-
        message = Goodbye from config 3

Creating a NOTES.txt File

https://helm.sh/docs/chart_template_guide/notes_files/

Subcharts and Global Values

Before we dive into the code, there are a few important details to learn about application subcharts.

  1. A subchart is considered "stand-alone", which means a subchart can never explicitly depend on its parent chart.

  2. For that reason, a subchart cannot access the values of its parent.

  3. A parent chart can override values for subcharts.

  4. Helm has a concept of global values that can be accessed by all charts.

Creating a Subchart

For these exercises, we'll start with the mychart/ chart we created at the beginning of this guide, and we'll add a new chart inside of it.

cd mychart/charts
helm create mysubchart
Creating mysubchart
rm -rf mysubchart/templates/*

Overriding Values from a Parent Values.yaml

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

##Chart name
mysubchart:
  dessert: ice cream

Global Chart Values

Global values are values that can be accessed from any chart or subchart by exactly the same name. Globals require explicit declaration. You can't use an existing non-global as if it were a global.

The Values data type has a reserved section called Values.global where global values can be set. Let's set one in our mychart/values.yaml file.

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

mysubchart:
  dessert: ice cream

global:
  salad: caesar

Example Config Map

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  salad: {{ .Values.global.salad }}

Sharing Templates with Subcharts

Parent charts and subcharts can share templates. Any defined block in any chart is available to other charts.

For example, we can define a simple template like this:

{{- define "labels" }}from: mychart{{ end }}

Recall how the labels on templates are globally shared. Thus, the labels chart can be included from any other chart.

While chart developers have a choice between include and template, one advantage of using include is that include can dynamically reference templates:

{{ include $mytemplate }}

The above will dereference $mytemplate. The template function, in contrast, will only accept a string literal.

0
Subscribe to my newsletter

Read articles from Balman Rawat directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Balman Rawat
Balman Rawat

I love to grease knots and bolts of SDLC, nurture the underlying infra, rightly automate, monitor systems and enable the dev teams to achieve more with less.