Official Helm Chart Template Guide Summary
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 asFAILED
.--no-hooks
: This skips running hooks for the command--recreate-pods
(only available forupgrade
androllback
)
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.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 alwaysHelm
.
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
andCapabilities.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 ofhelm 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 chartIf this is a subchart, the
values.yaml
file of a parent chartA values file if passed into
helm install
orhelm upgrade
with the-f
flag (helm install -f myvals.yaml ./mychart
)Individual parameters passed with
--set
(such ashelm 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
Behavior | Lookup function |
kubectl get pod mypod -n mynamespace | lookup "v1" "Pod" "mynamespace" "mypod" |
kubectl get pods -n mynamespace | lookup "v1" "Pod" "mynamespace" "" |
kubectl get pods --all-namespaces | lookup "v1" "Pod" "" "" |
kubectl get namespace mynamespace | lookup "v1" "Namespace" "" "mynamespace" |
kubectl get namespaces | lookup "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 blockswith
to specify a scoperange
, 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 manifestsThe
NOTES.txt
is one exceptionBut 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.
A subchart is considered "stand-alone", which means a subchart can never explicitly depend on its parent chart.
For that reason, a subchart cannot access the values of its parent.
A parent chart can override values for subcharts.
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.
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.