waytoeasylearn

Helm Chart Variables

This tutorial will discuss what helm chart variables/helm chart template variables are and how to use them.

Helm Chart Variables

As we discussed in the  earlier tutorial , I need to use the variables to inject a value within a  with  block that is out of its scope.

In the same way, within the  range looping , if I wanted to assign or process any specific element, I need to set it to variable and process it within the block. For that, I need to set a value to a variable.

The syntax for that is the name of the variable with a dollar sign ($), and the special assignment operator that we will be using is  :=  And then the value that we are reading from the template or a literal value.

Let’s go ahead and do an example and get a better understanding. Let me modify the template. Within the template, I’m going to add few lines.

So what I’m doing over here, I have a with block, and outside the block, I’m reading the release name and assigning it to a variable called relName.

And that is starting with the dollar sign ($) that is the syntax and the operated to assign the value is := and this particular variable can be accessed across the template and within the with block.

So I will read that specific variable that is $relName and print it with the key Release. Earlier, when we are accessing this particular value Release.Name that is the built-in object I was not able to read.

Now what I am doing I am assigning it to a variable, and that variable will have the scope across the template.

And over here, I have added a dash at the beginning and the end because it’s not going to print anything, and if I don’t provide this particular dash, it’s going to add a new line character. And there will be a new line or empty line within that YAML file. Even though that is valid, that’s not recommended.

Now let me go ahead and execute the dry run.

It’s able to print the release name. That’s the name that I provided as a part of the dry run statement. And there is no newline character where we are reading or assigning the variable.

All other lines within the block are the same as we had earlier. And this is proof where we can assign a built-in object into a variable and read it within the with block.

Example on looping

Now let us see another example where I will assign an element within the range into a variable and read it within the range block.

So within the template, let me go ahead and edit the template.

So within the range, what I’m doing, I’m assigning that specific element that is Values.LangUsed as a part of topping, and I’m going to have the index.

After that, I am printing both the index and the individual element read within the collection.

So I’ll be having key as the index and value as the actual element within the collection. Now I’m going to do a dry run.

So I’m getting the index and the actual value. This is another way of reading the variable. And as a part of the variable, another essential thing to understand is the dollar.

The dollar is a global variable, and this particular variable will always point to the root context. So I can access the global objects using the variable dollar.

Let us see some examples about the dollar variable as well. So here I am, adding the value by accessing the global variable dollar. So that will have the root context.

Now I can access the global object. Here I am accessing the global object, Chart.Name, Chart.Version,  Release  details, as well as the Chart.AppVersion.

Let me go ahead and execute this particular chart, and for almost all the examples, I’m using the dry run because there is no need to go ahead and deploy.

If a dry run is good, it will get deployed into the Kubernetes cluster. Once we learn every concept, we can consolidate everything together and release them into the  Kubernetes  cluster.

I’m going to do a dry run of the chart mychart.

So I’m able to get the chart releases and release Name, the version of the helm that we’re using, and what it is managed by.

So in a quick summary, we learned three major concepts.

That is, how to inject variables into a block, assign an individual element of a range, access the individual element and its index within the range looping, and access the global variable dollar and use the global objects in the helm chart.

Helm Logo Documentation

The chart template developer’s guide.

This guide provides an introduction to Helm’s chart templates, with emphasis on the template language.

Templates generate manifest files, which are YAML-formatted resource descriptions that Kubernetes can understand. We’ll look at how templates are structured, how they can be used, how to write Go templates, and how to debug your work.

This guide focuses on the following concepts:

  • The Helm template language
  • Using values
  • Techniques for working with templates

This guide is oriented toward learning the ins and outs of the Helm template language. Other guides provide introductory material, examples, and best practices.

Getting Started with a Chart Template

In this section of the guide, we’ll create a chart and then add a first template. The chart we created here will be used throughout the rest of the guide.

To get going, let’s take a brief look at a Helm chart.

As described in the Charts Guide , Helm charts are structured like this:

The templates/ directory is for template files. When Tiller evaluates a chart, it will send all of the files in the templates/ directory through the template rendering engine. Tiller then collects the results of those templates and sends them on to Kubernetes.

The values.yaml file is also important to templates. This file contains the default values for a chart. These values may be overridden by users during helm install or helm upgrade .

The Chart.yaml file contains a description of the chart. You can access it from within a template. The charts/ directory may contain other charts (which we call subcharts ). Later in this guide we will see how those work when it comes to template rendering.

A Starter Chart

For this guide, we’ll create a simple chart called mychart , and then we’ll create some templates inside of the chart.

From here on, we’ll be working in the mychart directory.

A Quick Glimpse of mychart/templates/

If you take a look at the mychart/templates/ directory, you’ll notice a few files already there.

  • NOTES.txt : The “help text” for your chart. This will be displayed to your users when they run helm install .
  • deployment.yaml : A basic manifest for creating a Kubernetes deployment
  • service.yaml : A basic manifest for creating a service endpoint for your deployment
  • _helpers.tpl : A place to put template helpers that you can re-use throughout the chart

And what we’re going to do is… remove them all! That way we can work through our tutorial from scratch. We’ll actually create our own NOTES.txt and _helpers.tpl as we go.

When you’re writing production grade charts, having basic versions of these charts can be really useful. So in your day-to-day chart authoring, you probably won’t want to remove them.

A First Template

The first template we are going to create will be a ConfigMap . In Kubernetes, a ConfigMap is simply a container for storing configuration data. Other things, like pods, can access the data in a ConfigMap.

Because ConfigMaps are basic resources, they make a great starting point for us.

Let’s begin by creating a file called mychart/templates/configmap.yaml :

TIP: Template names do not follow a rigid naming pattern. However, we recommend using the suffix .yaml for YAML files and .tpl for helpers.

The YAML file above is a bare-bones ConfigMap, having the minimal necessary fields. In virtue of the fact that this file is in the templates/ directory, it will be sent through the template engine.

It is just fine to put a plain YAML file like this in the templates/ directory. When Tiller reads this template, it will simply send it to Kubernetes as-is.

With this simple template, we now have an installable chart. And we can install it like this:

In the output above, we can see that our ConfigMap was created. Using Helm, we can retrieve the release and see the actual template that was loaded.

The helm get manifest command takes a release name ( full-coral ) and prints out all of the Kubernetes resources that were uploaded to the server. Each file begins with --- to indicate the start of a YAML document, and then is followed by an automatically generated comment line that tells us what template file generated this YAML document.

From there on, we can see that the YAML data is exactly what we put in our configmap.yaml file.

Now we can delete our release: helm delete full-coral .

Adding a Simple Template Call

Hard-coding the name: into a resource is usually considered to be bad practice. Names should be unique to a release. So we might want to generate a name field by inserting the release name.

TIP: The name: field is limited to 63 characters because of limitations to the DNS system. For that reason, release names are limited to 53 characters. Kubernetes 1.3 and earlier limited to only 24 characters (thus 14 character names).

Let’s alter configmap.yaml accordingly.

The big change comes in the value of the name: field, which is now {{ .Release.Name }}-configmap .

A template directive is enclosed in {{ and }} blocks.

The template directive {{ .Release.Name }} injects the release name into the template. The values that are passed into a template can be thought of as namespaced objects , where a dot ( . ) separates each namespaced element.

The leading dot before Release indicates that we start with the top-most namespace for this scope (we’ll talk about scope in a bit). So we could read .Release.Name as “start at the top namespace, find the Release object, then look inside of it for an object called Name ”.

The Release object is one of the built-in objects for Helm, and we’ll cover it in more depth later. But for now, it is sufficient to say that this will display the release name that Tiller assigns to our release.

Now when we install our resource, we’ll immediately see the result of using this template directive:

Note that in the RESOURCES section, the name we see there is clunky-serval-configmap instead of mychart-configmap .

You can run helm get manifest clunky-serval to see the entire generated YAML.

At this point, we’ve seen templates at their most basic: YAML files that have template directives embedded in {{ and }} . In the next part, we’ll take a deeper look into templates. But before moving on, there’s one quick trick that can make building templates faster: When you want to test the template rendering, but not actually install anything, you can use helm install ./mychart --debug --dry-run . This will send the chart to the Tiller server, which will render the templates. But instead of installing the chart, it will return the rendered template to you so you can see the output:

Using --dry-run will make it easier to test your code, but it won’t ensure that Kubernetes itself will accept the templates you generate. It’s best not to assume that your chart will install just because --dry-run works.

In the next few sections, we’ll take the basic chart we defined here and explore the Helm template language in detail. And we’ll get started with built-in objects.

Built-in Objects

Objects are passed into a template from the template engine. And your code can pass objects around (we’ll see examples when we look at the with and range statements). There are even a few ways to create new objects within your templates, like with the list function we’ll see later.

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.

In the previous section, we use {{.Release.Name}} to insert the name of a release into a template. Release is one of the top-level objects that you can access in your templates.

  • Release.Name : The release name
  • Release.Time : The time of the release
  • Release.Namespace : The namespace to be released into (if the manifest doesn’t override)
  • Release.Service : The name of the releasing service (always Tiller ).
  • Release.Revision : The revision number of this release. It begins at 1 and is incremented for each helm upgrade .
  • Release.IsUpgrade : This is set to true if the current operation is an upgrade or rollback.
  • Release.IsInstall : This is set to true if the current operation is an install.
  • Values : Values passed into the template from the values.yaml file and from user-supplied files. By default, Values is empty.
  • The available fields are listed in the Charts Guide
  • 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.
  • 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. Note, resources were not available before Helm v2.15.
  • Capabilities.KubeVersion provides a way to look up the Kubernetes version. It has the following values: Major , Minor , GitVersion , GitCommit , GitTreeState , BuildDate , GoVersion , Compiler , and Platform .
  • Capabilities.TillerVersion provides a way to look up the Tiller version. It has the following values: SemVer , GitCommit , and GitTreeState .
  • Name : A namespaced filepath to the current template (e.g. mychart/templates/mytemplate.yaml )
  • BasePath : The namespaced path to the templates directory of the current chart (e.g. mychart/templates ).

The values are available to any top-level template. As we will see later, this does not necessarily mean that they will be available everywhere .

The built-in values always begin with a capital letter. This is in keeping with Go’s naming convention. When you create your own names, you are free to use a convention that suits your team. Some teams, like the Helm Charts team, choose to use only initial lower case letters in order to distinguish local names from those built-in. In this guide, we follow that convention.

Values Files

In the previous section we looked at the built-in objects that Helm templates offer. One of these built-in objects is Values . This object provides access to values passed into the chart. Its contents come from four sources:

  • The values.yaml file in the chart
  • If this is a subchart, the values.yaml file of a parent chart
  • A values file is 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 )

The list above is in order of specificity: 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.

Values files are plain YAML files. Let’s edit mychart/values.yaml and then edit our ConfigMap template.

Removing the defaults in values.yaml , we’ll set just one parameter:

Now we can use this inside of a template:

Notice on the last line we access favoriteDrink as an attribute of Values : {{ .Values.favoriteDrink }} .

Let’s see how this renders.

Because favoriteDrink is set in the default values.yaml file to coffee , that’s the value displayed in the template. We can easily override that by adding a --set flag in our call to helm install :

Since --set has a higher precedence than the default values.yaml file, our template generates drink: slurm .

Values files can contain more structured content, too. For example, we could create a favorite section in our values.yaml file, and then add several keys there:

Now we would have to modify the template slightly:

While structuring data this way is possible, the recommendation is that you keep your values trees shallow, favoring flatness. When we look at assigning values to subcharts, we’ll see how values are named using a tree structure.

Deleting a default key

If you need to delete a key from the default values, you may override the value of the key to be null , in which case Helm will remove the key from the overridden values merge.

For example, the stable Drupal chart allows configuring the liveness probe, in case you configure a custom image. Here are the default values:

If you try to override the livenessProbe handler to exec instead of httpGet using --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] , Helm will coalesce the default and overridden keys together, resulting in the following YAML:

However, Kubernetes would then fail because you can not declare more than one livenessProbe handler. To overcome this, you may instruct Helm to delete the livenessProbe.httpGet by setting it to null:

At this point, we’ve seen several built-in objects, and used them to inject information into a template. Now we will take a look at another aspect of the template engine: functions and pipelines.

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 usable to us.

Let’s start with a best practice: When injecting strings from the .Values object into the template, we ought to quote these strings. We can do that by calling the quote function in the template directive:

Template functions follow the syntax functionName arg1 arg2... . In the snippet above, quote .Values.favorite.drink calls the quote function and passes it a single argument.

Helm has over 60 available functions. Some of them are defined by the Go template language itself. Most of the others are part of the Sprig template library . We’ll see many of them as we progress through the examples.

While we talk about the “Helm template language” as if it is Helm-specific, it is actually a combination of the Go template language, some extra functions, and a variety of wrappers to expose certain objects to the templates. Many resources on Go templates may be helpful as you learn about templating.

One of the powerful features of the template language is its concept of pipelines . Drawing on a concept from UNIX, pipelines are a tool for chaining together a series of template commands to compactly express a series of transformations. In other words, pipelines are an efficient way of getting several things done in sequence. Let’s rewrite the above example using a pipeline.

In this example, instead of calling quote ARGUMENT , we inverted the order. We “sent” the argument to the function using a pipeline ( | ): .Values.favorite.drink | quote . Using pipelines, we can chain several functions together:

Inverting the order is a common practice in templates. You will see .val | quote more often than quote .val . Either practice is fine.

When evaluated, that template will produce this:

Note that our original pizza has now been transformed to "PIZZA" .

When pipelining arguments like this, the result of the first evaluation ( .Values.favorite.drink ) is sent as the last argument to the function . We can modify the drink example above to illustrate with a function that takes two arguments: repeat COUNT STRING :

The repeat function will echo the given string the given number of times, so we will get this for output:

Using the default function

One function frequently used in templates is the default function: default DEFAULT_VALUE GIVEN_VALUE . This function allows you to specify a default value inside of the template, in case the value is omitted. Let’s use it to modify the drink example above:

If we run this as normal, we’ll get our coffee :

Now, we will remove the favorite drink setting from values.yaml :

Now re-running helm install --dry-run --debug ./mychart will produce this YAML:

In an actual chart, all static default values should live in the values.yaml, and should not be repeated using the default command (otherwise they would be redundant). However, the default command is perfect for computed values, which can not be declared inside values.yaml. For example:

In some places, an if conditional guard may be better suited than default . We’ll see those in the next section.

Template functions and pipelines are a powerful way to transform information and then insert it into your YAML. But sometimes it’s necessary to add some template logic that is a little more sophisticated than just inserting a string. In the next section we will look at the control structures provided by the template language.

Operators are functions

Operators are implemented as functions that return a boolean value. To use eq , ne , lt , gt , and , or , not etcetera place the operator at the front of the statement followed by its parameters just as you would a function. To chain multiple operations together, separate individual functions by surrounding them with parentheses.

Now we can turn from functions and pipelines to flow control with conditions, loops, and scope modifiers.

Flow Control

Control structures (called “actions” in template parlance) provide you, the template author, with the ability to control the flow of a template’s generation. 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

In addition to these, it provides a few actions for declaring and using named template segments:

  • define declares a new named template inside of your template
  • template imports a named template
  • block declares a special kind of fillable template area

In this section, we’ll talk about if , with , and range . The others are covered in the “Named Templates” section later in this guide.

The first control structure we’ll look at is for conditionally including blocks of text in a template. This is the if / else block.

The basic structure for a conditional looks like this:

Notice that we’re now talking about pipelines instead of values. The reason for this is to make it clear that control structures can execute an entire pipeline, not just evaluate a value.

A pipeline is evaluated as false if the value is:

  • a boolean false
  • a numeric zero
  • an empty string
  • a nil (empty or null)
  • an empty collection ( map , slice , tuple , dict , array )

In any other case, the condition is evaluated to true and the pipeline is executed.

Let’s add a simple conditional to our ConfigMap. We’ll add another setting if the drink is set to coffee:

Note that .Values.favorite.drink must be defined or else it will throw an error when comparing it to “coffee”. Since we commented out drink: coffee in our last example, the output should not include a mug: true flag. But if we add that line back into our values.yaml file, the output should look like this:

Controlling Whitespace

While we’re looking at conditionals, we should take a quick look at the way whitespace is controlled in templates. Let’s take the previous example and format it to be a little easier to read:

Initially, this looks good. But if we run it through the template engine, we’ll get an unfortunate result:

What happened? We generated incorrect YAML because of the whitespacing above.

mug is incorrectly indented. Let’s simply out-dent that one line, and re-run:

When we sent that, we’ll get YAML that is valid, but still looks a little funny:

Notice that we received a few empty lines in our YAML. Why? When the template engine runs, it removes the contents inside of {{ and }} , but it leaves the remaining whitespace exactly as is.

YAML ascribes meaning to whitespace, so managing the whitespace becomes pretty important. Fortunately, Helm templates have a few tools to help.

First, the curly brace syntax of template declarations can be modified with special characters to tell the template engine to chomp whitespace. {{- (with the dash and space added) indicates that whitespace should be chomped left, while -}} means whitespace to the right should be consumed. Be careful! Newlines are whitespace!

Make sure there is a space between the - and the rest of your directive. {{- 3 }} means “trim left whitespace and print 3” while {{-3}} means “print -3”.

Using this syntax, we can modify our template to get rid of those new lines:

Just for the sake of making this point clear, let’s adjust the above, and substitute an * for each whitespace that will be deleted following this rule. an * at the end of the line indicates a newline character that would be removed

Keeping that in mind, we can run our template through Helm and see the result:

Be careful with the chomping modifiers. It is easy to accidentally do things like this:

That will produce food: "PIZZA"mug:true because it consumed newlines on both sides.

For the details on whitespace control in templates, see the Official Go template documentation

Finally, sometimes it’s easier to tell the template system how to indent for you instead of trying to master the spacing of template directives. For that reason, you may sometimes find it useful to use the indent function ( {{indent 2 "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.

The syntax for with is similar to a simple if statement:

Scopes can be changed. with can allow you to set the current scope ( . ) to a particular object. For example, we’ve been working with .Values.favorites . Let’s rewrite our ConfigMap to alter the . scope to point to .Values.favorites :

(Note that we removed the if conditional from the previous exercise)

Notice that now we can reference .drink and .food without qualifying them. That is because the with statement sets . to point to .Values.favorite . The . is reset to its previous scope after {{ end }} .

But here’s a note of caution! Inside of the restricted scope, you will not be able to access the other objects from the parent scope. This, for example, will fail:

It will produce an error because Release.Name is not inside of the restricted scope for . . However, if we swap the last two lines, all will work as expected because the scope is reset after {{end}} .

After looking at range , we will take a look at template variables, which offers one solution to the scoping issue above.

Looping with the range action

Many programming languages have support for looping using for loops, foreach loops, or similar functional mechanisms. In Helm’s template language, the way to iterate through a collection is to use the range operator.

To start, let’s add a list of pizza toppings to our values.yaml file:

Now we have a list (called a slice in templates) of pizzaToppings . We can modify our template to print this list into our ConfigMap:

Let’s take a closer look at the toppings: list. The range function will “range over” (iterate through) the pizzaToppings list. But now something interesting happens. Just like with sets the scope of . , so does a range operator. Each time through the loop, . is set to the current pizza topping. That is, the first time, . is set to mushrooms . The second iteration it is set to cheese , and so on.

We can send the value of . directly down a pipeline, so when we do {{ . | title | quote }} , it sends . to title (title case function) and then to quote . If we run this template, the output will be:

Now, in this example we’ve done something tricky. The toppings: |- line is declaring a multi-line string. So our list of toppings is actually not a YAML list. It’s a big string. Why would we do this? Because the data in ConfigMaps data is composed of key/value pairs, where both the key and the value are simple strings. To understand why this is the case, take a look at the Kubernetes ConfigMap docs . For us, though, this detail doesn’t matter much.

The |- marker in YAML takes a multi-line string. This can be a useful technique for embedding big blocks of data inside of your manifests, as exemplified here.

Sometimes it’s useful to be able to quickly make a list inside of your template, and then iterate over that list. Helm templates have a function that’s called just that: list .

The above will produce this:

In addition to lists, range can be used to iterate over collections that have a key and a value (like a map or dict ). We’ll see how to do that in the next section when we introduce template variables.

With functions, pipelines, objects, and control structures under our belts, we can turn to one of the more basic ideas in many programming languages: variables. In templates, they are less frequently used. But we will see how to use them to simplify code, and to make better use of with and range .

In an earlier example, we saw that this code will fail:

Release.Name is not inside of the scope that’s restricted in the with block. One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope.

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 .

Notice that before we start the with block, we assign $relname := .Release.Name . Now inside of the with block, the $relname variable still points to the release name.

Running that will produce this:

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

Note that range comes first, then the variables, then the assignment operator, then the list. This will assign the integer index (starting from zero) to $index and the value to $topping . Running it will produce:

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:

Now on the first iteration, $key will be drink and $val will be coffee , and on the second, $key will be food and $val will be pizza . Running the above will generate this:

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. This can be very useful when you are looping in a range and need to know the chart’s release name.

An example illustrating this:

So far we have looked at just one template declared in just one file. But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We’ll turn to that in the next section.

Named Templates

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.

In the “Flow Control” section we introduced three actions for declaring and managing templates: define , template , and block . In this section, we’ll cover those three actions, and also introduce a special-purpose include function that works similarly to the template action.

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 .

One popular naming convention is to prefix each defined template with the name of the chart: {{ define "mychart.labels" }} . By using the specific chart name as a prefix we can avoid any conflicts that may arise due to two different charts that implement templates of the same name.

Partials and _ files

So far, we’ve used one file, and that one file has contained a single template. But Helm’s template language allows you to create named embedded templates, that can be accessed by name elsewhere.

Before we get to the nuts-and-bolts of writing those templates, there is file naming convention that deserves mention:

  • 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.

These files are used to store partials and helpers. In fact, when we first created mychart , we saw a file called _helpers.tpl . That file is the default location for template partials.

Declaring and using templates with define and template

The define action allows us to create a named template inside of a template file. Its syntax goes like this:

For example, we can define a template to encapsulate a Kubernetes block of labels:

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

When the template engine reads this file, it will store away the reference to mychart.labels until template "mychart.labels" is called. Then it will render that template inline. So the result will look like this:

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

By convention, define functions should have a simple documentation block ( {{/* ... */}} ) describing what they do.

Even though this definition is in _helpers.tpl , it can still be accessed in configmap.yaml :

As mentioned above, template names are global . As a result of this, if two templates are declared with the same name the last occurrence will be the one that is used. Since templates in subcharts are compiled together with top-level templates, it is best to name your templates with chart specific names . A popular naming convention is to prefix each defined template with the name of the chart: {{ define "mychart.labels" }} .

Setting the scope of a template

In the template we defined above, we did not use any objects. We just used functions. Let’s modify our defined template to include the chart name and chart version:

If we render this, the result will not be what we expect:

What happened to the name and version? They weren’t in the scope for our defined template. When a named template (created with define ) is rendered, it will receive the scope passed in by the template call. In our example, we included the template like this:

No scope was passed in, so within the template we cannot access anything in . . This is easy enough to fix, though. We simply pass a scope to the template:

Note that we pass . at the end of the template call. We could just as easily pass .Values or .Values.favorite or whatever scope we want. But what we want is the top-level scope.

Now when we execute this template with helm install --dry-run --debug ./mychart , we get this:

Now {{ .Chart.Name }} resolves to mychart , and {{ .Chart.Version }} resolves to 0.1.0 .

The include function

Say we’ve defined a simple template that looks like this:

Now say I want to insert this both into the labels: section of my template, and also the data: section:

The output will not be what we expect:

Note that the indentation on app_version is wrong in both places. Why? Because the template that is substituted in has the text aligned to the right. 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 nindent to indent the mychart_app template correctly:

Now the produced YAML is correctly indented for each section:

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.

Sometimes we want to import content, but not as templates. That is, we want to import files verbatim. We can achieve this by accessing files through the .Files object described in the next section.

Accessing Files Inside Templates

In the previous section we looked at several ways to create and access named templates. This makes it easy to import one template from within another template. But 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. Before we get going with the template examples, though, there are a few things to note about how this works:

  • It is okay to add extra files to your Helm chart. These files will be bundled and sent to Tiller. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects.
  • Files in templates/ cannot be accessed.
  • Files excluded using .helmignore cannot be accessed.
  • Charts do not preserve UNIX mode information, so file-level permissions will have no impact on the availability of a file when it comes to the .Files object.

Basic example

Path helpers, glob patterns, configmap and secrets utility functions.

With those caveats behind, let’s write a template that reads three files into our ConfigMap. To get started, we will add three files to the chart, putting all three directly inside of the mychart/ directory.

config1.toml :

config2.toml :

config3.toml :

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.

This config map uses several of the techniques discussed in previous sections. For example, we create a $files variable to hold a reference to the .Files object. We also use the list function to create a list of files that we loop through. Then we print each file name ( {{.}}: |- ) followed by the contents of the file {{ $files.Get . }} .

Running this template will produce a single ConfigMap with the contents of all three files:

When working with files, it can be very useful to perform some standard operations on the file paths themselves. To help with this, Helm imports many of the functions from Go’s path package for your use. They are all accessible with the same names as in the Go package, but with a lowercase first letter. For example, Base becomes base , etc.

The imported functions are:

As your chart grows, you may find you have a greater need to organize your files more, and so we provide a Files.Glob(pattern string) method to assist in extracting certain files with all the flexibility of glob patterns .

.Glob returns a Files type, so you may call any of the Files methods on the returned object.

For example, imagine the directory structure:

You have multiple options with Globs:

(Not present in version 2.0.2 or prior)

It is very common to want to place file content into both configmaps and secrets, for mounting into your pods at run time. To help with this, we provide a couple utility methods on the Files type.

For further organization, it is especially useful to use these methods in conjunction with the Glob method.

Given the directory structure from the Glob example above:

You can import a file and have the template base-64 encode it to ensure successful transmission:

The above will take the same config1.toml file we used before and encode it:

Sometimes it is desirable to access each line of a file in your template. We provide a convenient Lines method for this.

Currently, there is no way to pass files external to the chart during helm install . So if you are asking users to supply data, it must be loaded using helm install -f or helm install --set .

This discussion wraps up our dive into the tools and techniques for writing Helm templates. In the next section we will see how you can use one special file, templates/NOTES.txt , to send post-installation instructions to the users of your chart.

Creating a NOTES.txt File

In this section we are going to look at Helm’s tool for providing instructions to your chart users. At the end of a helm install or helm upgrade , Helm can print out a block of helpful information for users. This information is highly customizable using templates.

To add installation notes to your chart, simply create a templates/NOTES.txt file. This file is plain text, but it is processed like as a template, and has all the normal template functions and objects available.

Let’s create a simple NOTES.txt file:

Now if we run helm install ./mychart we will see this message at the bottom:

Using NOTES.txt this way is a great way to give your users detailed information about how to use their newly installed chart. Creating a NOTES.txt file is strongly recommended, though it is not required.

Subcharts and Global Values

To this point we have been working only with one chart. But charts can have dependencies, called subcharts , that also have their own values and templates. In this section we will create a subchart and see the different ways we can access values from within templates.

Before we dive into the code, there are a few important details to learn about 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.

As we walk through the examples in this section, many of these concepts will become clearer.

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.

Notice that just as before, we deleted all of the base templates so that we can start from scratch. In this guide, we are focused on how templates work, not on managing dependencies. But the Charts Guide has more information on how subcharts work.

Adding Values and a Template to the Subchart

Next, let’s create a simple template and values file for our mysubchart chart. There should already be a values.yaml in mychart/charts/mysubchart . We’ll set it up like this:

Next, we’ll create a new ConfigMap template in mychart/charts/mysubchart/templates/configmap.yaml :

Because every subchart is a stand-alone chart , we can test mysubchart on its own:

Overriding Values of a Child Chart

Our original chart, mychart is now the parent chart of mysubchart . This relationship is based entirely on the fact that mysubchart is within mychart/charts .

Because mychart is a parent, we can specify configuration in mychart and have that configuration pushed into mysubchart . For example, we can modify mychart/values.yaml like this:

Note the last two lines. Any directives inside of the mysubchart section will be sent to the mysubchart chart. So if we run helm install --dry-run --debug mychart , one of the things we will see is the mysubchart ConfigMap:

The value at the top level has now overridden the value of the subchart.

There’s an important detail to notice here. We didn’t change the template of mychart/charts/mysubchart/templates/configmap.yaml to point to .Values.mysubchart.dessert . From that template’s perspective, the value is still located at .Values.dessert . As the template engine passes values along, it sets the scope. So for the mysubchart templates, only values specifically for mysubchart will be available in .Values .

Sometimes, though, you do want certain values to be available to all of the templates. This is accomplished using global chart values.

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.

Because of the way globals work, both mychart/templates/configmap.yaml and mychart/charts/mysubchart/templates/configmap.yaml should be able to access that value as {{ .Values.global.salad}} .

mychart/templates/configmap.yaml :

mychart/charts/mysubchart/templates/configmap.yaml :

Now if we run a dry run install, we’ll see the same value in both outputs:

Globals are useful for passing information like this, though it does take some planning to make sure the right templates are configured to use globals.

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:

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:

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

Avoid Using Blocks

The Go template language provides a block keyword that allows developers to provide a default implementation which is overridden later. In Helm charts, blocks are not the best tool for overriding because if multiple implementations of the same block are provided, the one selected is unpredictable.

The suggestion is to instead use include .

The .helmignore file

The .helmignore file is used to specify files you don’t want to include in your helm chart.

If this file exists, the helm package command will ignore all the files that match the pattern specified in the .helmignore file while packaging your application.

This can help in avoiding unnecessary or sensitive files or directories from being added in your helm chart.

The .helmignore file supports Unix shell glob matching, relative path matching, and negation (prefixed with !). Only one pattern per line is considered.

Here is an example .helmignore file:

We’d love your help making this document better. To add, correct, or remove information, file an issue or send us a pull request.

Debugging Templates

Debugging templates can be tricky simply because the templates are rendered on the Tiller server, not the Helm client. And then the rendered templates are sent to the Kubernetes API server, which may reject the YAML files for reasons other than formatting.

There are a few commands that can help you debug.

  • helm lint is your go-to tool for verifying that your chart follows best practices
  • helm install --dry-run --debug : We’ve seen this trick already. It’s a great way to have the server render your templates, then return the resulting manifest file.
  • helm get manifest : This is a good way to see what templates are installed on the server.

When your YAML is failing to parse, but you want to see what is generated, one easy way to retrieve the YAML is to comment out the problem section in the template, and then re-run helm install --dry-run --debug :

The above will be rendered and returned with the comments intact:

This provides a quick way of viewing the generated content without YAML parse errors blocking.

Wrapping Up

This guide is intended to give you, the chart developer, a strong understanding of how to use Helm’s template language. The guide focuses on the technical aspects of template development.

But there are many things this guide has not covered when it comes to the practical day-to-day development of charts. Here are some useful pointers to other documentation that will help you as you create new charts:

  • The Helm Charts project is an indispensable source of charts. That project is also sets the standard for best practices in chart development.
  • The Kubernetes Documentation provides detailed examples of the various resource kinds that you can use, from ConfigMaps and Secrets to DaemonSets and Deployments.
  • The Helm Charts Guide explains the workflow of using charts.
  • The Helm Chart Hooks Guide explains how to create lifecycle hooks.
  • The Helm Charts Tips and Tricks article provides some useful tips for writing charts.
  • The Sprig documentation documents more than sixty of the template functions.
  • The Go template docs explain the template syntax in detail.
  • The Schelm tool is a nice helper utility for debugging charts.

Sometimes it’s easier to ask a few questions and get answers from experienced developers. The best place to do this is in the Kubernetes Slack Helm channels:

  • #helm-users

Finally, if you find errors or omissions in this document, want to suggest some new content, or would like to contribute, visit The Helm Project .

YAML Techniques

Most of this guide has been focused on writing the template language. Here, we’ll look at the YAML format. YAML has some useful features that we, as template authors, can use to make our templates less error prone and easier to read.

Scalars and Collections

According to the YAML spec , there are two types of collections, and many scalar types.

The two types of collections are maps and sequences:

Scalar values are individual values (as opposed to collections)

Scalar Types in YAML

In Helm’s dialect of YAML, the scalar data type of a value is determined by a complex set of rules, including the Kubernetes schema for resource definitions. But when inferring types, the following rules tend to hold true.

If an integer or float is an unquoted bare word, it is typically treated as a numeric type:

But if they are quoted, they are treated as strings:

The same is true of booleans:

The word for an empty value is null (not nil ).

Note that port: "80" is valid YAML, and will pass through both the template engine and the YAML parser, but will fail if Kubernetes expects port to be an integer.

In some cases, you can force a particular type inference using YAML node tags:

In the above, !!str tells the parser that age is a string, even if it looks like an int. And port is treated as an int, even though it is quoted.

Strings in YAML

Much of the data that we place in YAML documents are strings. YAML has more than one way to represent a string. This section explains the ways and demonstrates how to use some of them.

There are three “inline” ways of declaring a string:

All inline styles must be on one line.

  • Bare words are unquoted, and are not escaped. For this reason, you have to be careful what characters you use.
  • Double-quoted strings can have specific characters escaped with \ . For example "\"Hello\", she said" . You can escape line breaks with \n .
  • Single-quoted strings are “literal” strings, and do not use the \ to escape characters. The only escape sequence is '' , which is decoded as a single ' .

In addition to the one-line strings, you can declare multi-line strings:

The above will treat the value of coffee as a single string equivalent to Latte\nCappuccino\nEspresso\n .

Note that the first line after the | must be correctly indented. So we could break the example above by doing this:

Because Latte is incorrectly indented, we’d get an error like this:

In templates, it is sometimes safer to put a fake “first line” of content in a multi-line document just for protection from the above error:

Note that whatever that first line is, it will be preserved in the output of the string. So if you are, for example, using this technique to inject a file’s contents into a ConfigMap, the comment should be of the type expected by whatever is reading that entry.

Controlling Spaces in Multi-line Strings

In the example above, we used | to indicate a multi-line string. But notice that the content of our string was followed with a trailing \n . If we want the YAML processor to strip off the trailing newline, we can add a - after the | :

Now the coffee value will be: Latte\nCappuccino\nEspresso (with no trailing \n ).

Other times, we might want all trailing whitespace to be preserved. We can do this with the |+ notation:

Now the value of coffee will be Latte\nCappuccino\nEspresso\n\n\n .

Indentation inside of a text block is preserved, and results in the preservation of line breaks, too:

In the above case, coffee will be Latte\n 12 oz\n 16 oz\nCappuccino\nEspresso .

Indenting and Templates

When writing templates, you may find yourself wanting to inject the contents of a file into the template. As we saw in previous chapters, there are two ways of doing this:

  • Use {{ .Files.Get "FILENAME" }} to get the contents of a file in the chart.
  • Use {{ include "TEMPLATE" . }} to render a template and then place its contents into the chart.

When inserting files into YAML, it’s good to understand the multi-line rules above. Often times, the easiest way to insert a static file is to do something like this:

Note how we do the indentation above: indent 2 tells the template engine to indent every line in “myfile.txt” with two spaces. Note that we do not indent that template line. That’s because if we did, the file content of the first line would be indented twice.

Folded Multi-line Strings

Sometimes you want to represent a string in your YAML with multiple lines, but want it to be treated as one long line when it is interpreted. This is called “folding”. To declare a folded block, use > instead of | :

The value of coffee above will be Latte Cappuccino Espresso\n . Note that all but the last line feed will be converted to spaces. You can combine the whitespace controls with the folded text marker, so >- will replace or trim all newlines.

Note that in the folded syntax, indenting text will cause lines to be preserved.

The above will produce Latte\n 12 oz\n 16 oz\nCappuccino Espresso . Note that both the spacing and the newlines are still there.

Embedding Multiple Documents in One File

It is possible to place more than one YAML documents into a single file. This is done by prefixing a new document with --- and ending the document with ...

In many cases, either the --- or the ... may be omitted.

Some files in Helm cannot contain more than one doc. If, for example, more than one document is provided inside of a values.yaml file, only the first will be used.

Template files, however, may have more than one document. When this happens, the file (and all of its documents) is treated as one object during template rendering. But then the resulting YAML is split into multiple documents before it is fed to Kubernetes.

We recommend only using multiple documents per file when it is absolutely necessary. Having multiple documents in a file can be difficult to debug.

YAML is a Superset of JSON

Because YAML is a superset of JSON, any valid JSON document should be valid YAML.

The above is another way of representing this:

And the two can be mixed (with care):

All three of these should parse into the same internal representation.

While this means that files such as values.yaml may contain JSON data, Helm does not treat the file extension .json as a valid suffix.

YAML Anchors

The YAML spec provides a way to store a reference to a value, and later refer to that value by reference. YAML refers to this as “anchoring”:

In the above, &favoriteCoffee sets a reference to Cappuccino . Later, that reference is used as *favoriteCoffee . So coffees becomes Latte, Cappuccino, Espresso .

While there are a few cases where anchors are useful, there is one aspect of them that can cause subtle bugs: The first time the YAML is consumed, the reference is expanded and then discarded.

So if we were to decode and then re-encode the example above, the resulting YAML would be:

Because Helm and Kubernetes often read, modify, and then rewrite YAML files, the anchors will be lost.

Appendix: Go Data Types and Templates

The Helm template language is implemented in the strongly typed Go programming language. For that reason, variables in templates are typed . For the most part, variables will be exposed as one of the following types:

  • string: A string of text
  • bool: a true or false
  • int: An integer value (there are also 8, 16, 32, and 64 bit signed and unsigned variants of this)
  • float64: a 64-bit floating point value (there are also 8, 16, and 32 bit varieties of this)
  • a byte slice ( []byte ), often used to hold (potentially) binary data
  • struct: an object with properties and methods
  • a slice (indexed list) of one of the previous types
  • a string-keyed map ( map[string]interface{} ) where the value is one of the previous types

There are many other types in Go, and sometimes you will have to convert between them in your templates. The easiest way to debug an object’s type is to pass it through printf "%t" in a template, which will print the type. Also see the typeOf and kindOf functions.

We are a Cloud Native Computing Foundation incubating project.

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications

7 Helm Best Practices with Examples

As we develop our charts, we’ll notice we have multiple solutions to most problems. When we can solve something in, say, eight different ways, as beginners, it may be hard to know which path would be best.

So let’s go through a list of general guidelines, requirements, and recommendations, just to get a sense of some optimal ways to deal with Helm charts.

#1. Chart Names and Versions

Chart names.

Chart names should only contain lowercase letters (and, if necessary, numbers). Also, if your name has to have more than one word, separate these words with hyphens '-', for example, my-nginx-app .

Valid names:

Invalid names:

Version Numbers

Helm recommends what is called SemVer2 notation (Semantic Versioning 2.0.0). The short explanation is this: version numbers are in the form of 1.2.3 (MAJOR.MINOR.PATCH). The first number represents the major version number, the second the minor, and the third is the patch number.

Let’s say you just created a new application last night. This could be version 0.1.0. You find a security bug, and you patch it. This would be version 0.1.1. Later, you find another bug, and you patch that too. You arrive at version 0.1.2. You make some very small changes to your app - like maybe you change a function so that it executes 2% faster - this could be version 0.2.0.

Notice how when you update the minor version number, the patch number gets reset to 0. And finally, you make a huge update to your app, adding a lot of new features and changing some functionalities. This way, you arrive at version 1.0.0.

#2. values.yaml file

Variable names.

All variable names in the values.yaml file should begin with lowercase letters.

Valid name:

Invalid name:

Often, your variable will need to contain multiple words to describe it better. Use what is called camelcase : the first word starts with a lowercase letter, but the next ones all start with a capital letter.

Examples of valid names:

Flat vs. Nested Values

We saw in our exercises that we can have nested values like this:

This provides some logical grouping. In the snippet above image is the parent variable with three children values. But this can be rewritten to a flat format, like this:

When to use nested format:

  • If you have many related variables and at least one of them is always used.

When to use flat format:

  • If you have very few related variables
  • If all your related variables are optional

Quote Strings

Always wrap your string values between quote signs. For instance:

The configuration above assigns a string value 'false' to the enabled variable. Consider the value assignment below:

This will assign a boolean type to the enabled variable.

Large Integers

Assume you have this:

When parsing your template files, this value might get converted to scientific notation and end up as 1.234567e+06 . If you run into this issue (which may happen especially with large numbers), you can define a number as a string.

Then, when you need to use this variable in your template files, prefix it with the type conversion function int (or int64 for very large numbers).

This way, the string “1234567” will get converted into the number 1234567 and won’t end up as 1.234567e+06 .

You can see other type conversion functions here .

Documentation for the values.yaml File

If when a user opens up a values.yaml file all they get is a long list of variables, it can be very hard for them to figure out what each of them is used for. That’s why you should always document each by adding a comment above it. The comment starts with a # . Example:

Notice how we start the comment with the actual variable name. It’s recommended you do it the same way to make searching easier. It also helps automatic documentation tools correlate these notes with the parameters that they describe.

To learn more about adding comments to your YAML file, check out our blog post: How to Add YAML Comments with Examples .

helm variable assignment

#3. Template Files

Template file names should all be lowercase letters. If they contain multiple words, these should be separated with dashes, '-'.

Examples of correct file names:

Incorrect file names:

These file names should indicate the kind of Kubernetes resource they define so that users can get an idea of what's in the file from a glance. For long resource names, like a Persistent Volume Claim, you might use their acronym (PVC in this case) and end up with pvc.yaml .

YAML Comments vs. Template Comments

A template comment follows this syntax:

You should document anything that is not immediately obvious. Often, your .tpl files (like _helpers.tpl ) will need to be commented on thoroughly, as it’s not easy to figure out what goes on there. Take a look at this:

It's pretty hard to figure out what is going on in the file above, and even if we understand what happens, we might wonder, “Why use trunc 63 to truncate to 63 characters?” We can add some comments to clarify it:

The file above becomes much easier to understand.

This type of comment (template comment) won’t be displayed if the user enters a command like helm install –debug to see what manifests this chart would generate. If you need these comments to be output in such commands, you can use YAML comments instead. These look like this:

#4. Dependencies

When your chart depends on other charts, you must declare in Chart.yaml the versions of these dependency charts. Instead of using an exact version like:

You should declare an approximate version by prefixing it with the tilde ~ sign.

This is equivalent to saying, “The version number starts with 1.8, and it can be anything higher than or equal to 1.8.2 but strictly lower than 1.9.0 (it can be 1.8.9 or 1.8.10, but not 1.9.0)”.

When available, use the more secure https:// for repository URLs instead of http:// . HTTP’s encryption and certificate authentication provides extra security for your requests. For instance, they make it harder for someone to perform a man-in-the-middle attack, which can be used to inject malicious code into our dependency charts.

#5. Recommended Labels

Kubernetes operators find objects by looking at the metadata - more specifically, the labels in the metadata section. There are many objects in the Kubernetes cluster, but only part of them are installed by and managed by Helm. Adding a label such as helm.sh/chart: nginx-0.1.0 makes it easy for operators to find all objects installed by the nginx chart, version 0.1.0.

Your templates should, preferably, generate templates with labels similar to this:

Check out this page to better understand how to use labels. To see an example of how labels are used in templates, use this command:

Then explore the sample/templates/ directory and sample/templates/_helpers.tpl file to see how these are defined in a clean, rather simple chart.

#6. Pods and PodTemplates

Whenever we declare Kubernetes objects such as Deployments, ReplicationControllers, ReplicaSets, DaemonSets, or StatefulSets, we include definitions of PodTemplates.

We tell Kubernetes the kind of pods that it will need to launch, hence a template for these pods. That is because something like a deployment, although just one object, might launch tens of pods. Here’s a list of best practices when we work with such sections.

Images Versions/Tags

Do not use an image tag such as latest or any other similar tag that doesn’t point to a specific, fixed image. The latest tag is what we’d call a “floating” tag, meaning the image version it points to is constantly changing; today, it might point to 1.2.3. Tomorrow, when a new version is launched, it might point to 1.2.4 or, even worse, to 2.0.1 (a major update with significant changes).

The components installed by the chart usually need to fit perfectly together, and the latest tag takes out the perfect fit guarantee. If there is a major update to one component, it might not work with the other pieces due to incompatible changes in the latest version.

The recommended practice is to point to a specific image version and use values.yaml as the place where it can be modified. See the sample values.yaml below:

Here is the deployment.yaml that pulls the image name and version (tag) from the values.yaml file:

If the user did not define a repository name in his values.yaml file, we use a default name for the chart, nginx. We didn’t do this in our example, but we should define a default version in a real production-ready chart. The line:

This way, we achieve the best of both worlds:

  • The chart will produce predictable results; the same images will be installed in all default installs (when the user doesn’t bother to choose his own settings).
  • The user still has the power to choose other versions for his images if he has some specific needs. For example, if his desired features only exist in a newer software version).

So we give our users predictable results and the freedom to choose if they so desire.

Include Selectors in Your PodTemplates

Consider the manifest below that would get generated by the default nginx/templates/deployment.yaml file created by the helm create nginx command:

As we mentioned earlier, objects like Kubernetes deployments can create multiple pods. And there will also be pods launched by other objects, resulting in many pods that are mixed together. There needs to be a way to know which pods belong to a group and which belong to another group.

This is exactly what selector labels help in doing.  Your selectors should be the label, or labels (can be one or more) that you know will never change for the entire duration that the object is running. Let us see this in the example below:

We chose these two as our selectors:

We can't choose app.kubernetes.io/version: "1.16.0" as a selector since the version label will definitely change in the future.

#7. Custom Resource Definitions

Custom Resource Definitions (CRDs) are basically a way to add extra features to Kubernetes. These require some special attention on our part when using them with Helm. To understand why to think about this:

Kubernetes is built to ingest definitions of many objects, even all at once. Say we send it some Deployment manifests for some MySQL pods, PersistentVolume manifests, PersistentVolumeClaims, and so on. We, as the users, don’t care in which order we send these to Kubernetes: But MySQL would not be able to work if it would not have a persistent volume where it can store its database.

Keep in mind that we do not specify anywhere, “Hey, first launch these persistent volumes, and only after that launch the MySQL pods.” But our cluster will know what to do. When we want to install CRDs with Helm, though, the story is different.

Let’s say we define a cool new object in our CRD. Now, we can use this new type of object in Kubernetes by starting our manifests with something like:

But Helm needs to send both things to our cluster: the CRD and the manifest of this object. Now, the order matters, or more specifically, the timing. A few seconds might pass until this new CRD is registered. If Kubernetes receives this object before the CRD is registered, it will not know what this is and how to create it since it does not know the CRD. It simply doesn’t know what this CoolObject is.

To tackle this, Helm gives you a special location for CRDs. You need to add a crds directory to the chart we have created. This directory should exist in the same place where we have the templates and charts directory.

Now Helm will install your CRDs first, then your other objects.

You can’t have a line like this in your CRD.

It should be the exact content you want to pass along:

Disadvantages of Installing CRDs with Helm

You cannot upgrade or delete CRDs with Helm.

You also cannot perform a –dry-run install to test if your chart works with Kubernetes without actually installing anything. The dry run would require the CRD to actually get installed so that the other objects in your chart get recognized on a dry run. But that defeats the purpose of not installing anything. Without the CRD, any objects in your chart that want to use this Custom Resource will fail on a dry run test.

ENROLL in our Helm For Beginner's Course to learn other concepts, such as creating Helm Charts, adding dependencies in them, and distributing them.

helm variable assignment

Helm charts provide a standardized way of packaging and deploying applications, making it easier for teams to collaborate and share their work. Follow the best practices listed in this article to create reliable Charts. Also, ensure that you perform tests on your charts before deploying in a production environment.

If you want a quick intro to Helm charts - how to create and install them, check out this video:

More on Helm:

  • Helper Files and Named Templates
  • Helm Flow Control and Conditionals
  • Helm Chart Dependencies
  • What is a Template Function and Pipeline in Helm?

Mumshad Mannambeth

Mumshad Mannambeth

Devops blog.

Subscribe for Exclusive Offers, Deals, and Latest Updates from KodeKloud!

Congratulations! You’ve subscribed to our newsletter and exclusive offers from KodeKloud! Stay tuned for inbox updates!

You are viewing info for Helm 3 - check the version FAQs or see to Helm 2 for prior versions.

Viewing Helm 3 release. For Helm 2 go here .

With functions, pipelines, objects, and control structures under our belts, we can turn to one of the more basic ideas in many programming languages: variables. In templates, they are less frequently used. But we will see how to use them to simplify code, and to make better use of with and range .

In an earlier example, we saw that this code will fail:

Release.Name is not inside of the scope that’s restricted in the with block. One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope.

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 .

Notice that before we start the with block, we assign $relname := .Release.Name . Now inside of the with block, the $relname variable still points to the release name.

Running that will produce this:

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

Note that range comes first, then the variables, then the assignment operator, then the list. This will assign the integer index (starting from zero) to $index and the value to $topping . Running it will produce:

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:

Now on the first iteration, $key will be drink and $val will be coffee , and on the second, $key will be food and $val will be pizza . Running the above will generate this:

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. This can be very useful when you are looping in a range need to know the chart’s release name.

An example illustrating this:

So far we have looked at just one template declared in just one file. But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We’ll turn to that in the next section.

We are a Cloud Native Computing Foundation incubating project.

© Helm Authors 2020 | Documentation distributed under CC-BY-4.0

© 2020 The Linux Foundation. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page.

IMAGES

  1. Variable in helm template towardslearning.in

    helm variable assignment

  2. Variable in helm template towardslearning.in

    helm variable assignment

  3. How to Use Environment Variables with Helm Charts

    helm variable assignment

  4. Creating a Helm Chart using helm@3

    helm variable assignment

  5. How to use script variable in Helm chart

    helm variable assignment

  6. How to Pull and Push Helm Charts {Step-by-Step Guide}

    helm variable assignment

VIDEO

  1. PHP Variable Scope & Operator

  2. Assignment Statement and Constant Variable

  3. 6 storing values in variable, assignment statement

  4. CSS Class 1, CSS introduction🔴Live

  5. R variable assignment

  6. Samurai4321

COMMENTS

  1. Helm

    One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope. In Helm templates, a variable is a named reference to another object. ... But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We'll turn ...

  2. Is it possible to define variables use if/else condition in helm chart

    Helm also includes (almost all of) a support template library called Sprig which includes a ternary function, which acts sort of like an inline "if" statement. For your example you could write For your example you could write

  3. How to Use Environment Variables with Helm Charts

    Mounting Environment Variables in a Kubernetes Deployment. 1. Add the following lines to the values.yaml file in your Helm chart: username: root. password: password. 2. Create a new file called secret.yaml and add it to the template folder. Add the following content to the file: apiVersion: v1.

  4. Helm Chart Variables

    Helm Chart Variables. This tutorial will discuss what helm chart variables/helm chart template variables are and how to use them. As we discussed in the earlier tutorial, I need to use the variables to inject a value within a with block that is out of its scope. In the same way, within the range looping, if I wanted to assign or process any ...

  5. Helm

    In helm templates, variables are less frequently used. But we will see how to use them to simplify code and make better use of with and range. In the Helm — Flow Control article, we have already ...

  6. Helm

    Flow Control. Control structures (called "actions" in template parlance) provide you, the template author, with the ability to control the flow of a template's generation. Helm's template language provides the following control structures: In addition to these, it provides a few actions for declaring and using named template segments ...

  7. Helm

    Helm - The Kubernetes Package Manager. Get Helm; Blog; Docs; Charts; v2.16.7 . v3.2.4 stable; v2.16.7 legacy; v2.14.0; Docs: Using Helm . Quickstart; ... One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope. In Helm templates, a variable is a named reference to another ...

  8. Variables

    One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope. \n In Helm templates, a variable is a named reference to another object.

  9. Helm

    A chart's values.yaml file. A values file supplied by helm install -f or helm upgrade -f. The values passed to a --set or --set-string flag on helm install or helm upgrade. When designing the structure of your values, keep in mind that users of your chart may want to override them via either the -f flag or with the --set option.

  10. 7 Helm Best Practices with Examples

    The configuration above assigns a string value 'false' to the enabled variable. Consider the value assignment below: enabled: false. This will assign a boolean type to the enabled variable. Large Integers. Assume you have this: number: 1234567. When parsing your template files, this value might get converted to scientific notation and end up as ...

  11. Helm

    Built-in Objects. Objects are passed into a template from the template engine. And your code can pass objects around (we'll see examples when we look at the with and range statements). There are even a few ways to create new objects within your templates, like with the tuple function we'll see later. Objects can be simple, and have just one value.

  12. Concat a string and a secret to build an env variable in a helm

    How to concatenate a secret value with a string to build an env variable to package in a helm template. We currently use kubernetes (v 1.6.3) to host our apps. When deploying a symfony 3.4 app, I needed to define the DATABASE_URL env. The DATABASE_URL store typically a string like db_motor://db_user:db_password@host:port/db_name

  13. How to access variable out of current range control in helm template

    As @KamolHasan notes in their answer, if you try to define a variable inside an {{ if }}...{{ end }} block, the scope of the variable is that block. Helm (and more specifically the Sprig extension template library) provides a ternary extension function to work around this.. ternary takes three arguments; in order, the true-value, the false-value, and the condition.

  14. Helm

    One way to work around scoping issues is to assign objects to variables that can be accessed without respect to the present scope. In Helm templates, a variable is a named reference to another object. ... But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We'll ...

  15. Kubernetes Helm, combine two variables with a string in the middle

    I'm trying to change the value of a variable if another variable it set by combining the two with a dash in the middle, I'm not sure of the syntax to do this, I'm thinking of somethings like: {{- $ ... Define a variable in Helm template. 70. How to make nested variables optional in Helm. 1. Use variable to define other variables. 2.

  16. How do I concatenate strings inside a variable assignment?

    How do I concatenate strings inside a variable assignment? Ask Question Asked 2 years, 9 months ago. Modified 2 years, 9 months ago. Viewed 3k times 3 I'm trying to assign the output of a function to a var and append to the string at the same time but can't figure out the right syntax. ... Define a variable in Helm template. 8. How to ...

  17. Helm

    Flow Control. Control structures (called "actions" in template parlance) provide you, the template author, with the ability to control the flow of a template's generation. Helm's template language provides the following control structures: In addition to these, it provides a few actions for declaring and using named template segments: In this ...

  18. Helm

    The above will first check to see if .name is empty. If it is not, it will return that value. If it is empty, coalesce will evaluate .parent.name for emptiness. Finally, if both .name and .parent.name are empty, it will return Matt.. ternary. The ternary function takes two values, and a test value. If the test value is true, the first value will be returned.

  19. helm template: how do i assign the result of a template to a variable

    The reason why I have this issue is because I am unable to use the template cassandra.fullname within a range, so I'm trying to put the value into a variable and use it in the range instead. So if there's a solution for that, it would also be accepted!

  20. Helm

    Templates. This part of the Best Practices Guide focuses on templates. Structure of templates/. The templates/ directory should be structured as follows:. Template files should have the extension .yaml if they produce YAML output. The extension .tpl may be used for template files that produce no formatted content.; Template file names should use dashed notation (my-example-configmap.yaml), not ...