Helm is a package manager for kubernetes. It allows users to define and share applications in an reliable and repeatable fashion. Helm uses the go templating engine to help users customize charts to meet the needs of each individual deployment. For up-to-date installation instructions, please visit the Helm installation guide.
Before we dive into the details of Helm, lets take a second to understand the idea behind helm. It is useful to think of Helm as a templating engine. While you can use it to install applications into your k8s cluster, it's main use, for me at least, is to generate k8s manifests. I've always thought of Helm in terms of programming. The Helm chart has a lot of similarities to a class object in programing. On it's own, it may set some sane defaults or even have business logic defined to keep applicaions secure while adhearing to best practices. It also allows for engineers who are more familiar with kubernetes to define how applications should be deployed. This abstraction allows deployments to be used by engineers who may not be as familiar with kubernetes to build and deploy applications with ease. All the end users would need to supply are the values needed to tailor the chart to run their specific application. Usually these override files include things like where to find the container image, what version or tag to use, and what commands are needed to run the application.
With that said, lets take a look at the structure of a Helm chart. Helm has a few key components that every chart
needs to have in place. The general file structur of a chart should look something like this: Keep in mind that this
is a highly simplified version of a helm chart and this walk through will not cover all of the possible files that
can be included in a chart. If you run helm create NAME [flags]
to create a new chart, you will see a
lot more files than what is listed here.
$ tree helm-chart
helm-chart
├── Chart.yaml
├── templates
│ ├── pod.yaml
└── values.yaml
Lets dive in to each of these files and get a better look at what they do. The Chart.yaml
file is where
we define the metadata associated with the chart. In this walkthrough, we'll only focus on the required fields and
what they are for. The apiVersion: v2
field defines what version on the Helm chart api we are using.
Confusingly enough, v2 means you will be using the Helm 3 api. Then we have the name: helm-chart
field to
define the name of the chart. The description: A Helm chart for Kubernetes
field is used to describe what
the chart does and what it is used for. Finally, the version: 0.1.0
field is used to define the version of
the chart. This is useful for tracking changes to the chart and for users to know what version of the chart they are.
Interestingly enough, the version of the chart doesn't matter all that much unless you plan on publishing your chart
to a registry or sharing it with others. When you team Heml up with things like ArgoCD, you can point ArgoCD directly
to your Helm chart and you can use other things to track the version of your chart. That being said, it's a business
decision to make on how you want to manage and track version of your chart. It's just good to know that you have
other options.
---
apiVersion: v2
name: helm-chart
description: A Helm chart for Kubernetes
version: 0.1.0
The values.yaml
file is where we define the default values for the chart. This file is a great place
to look to find out what values can be overridden when you are deploying the chart. Here you can also find
information of the expected structure of the values that are expected to be passed in. We are going to build
a small chart that will help us deploy a single pod that is customizable for different images and commands to run.
Our values.yaml
file will only containe a few fields to keep things simple. The image
field will define our container image and the command
field will define the command that will be run
when the container starts.
image: "nginx:latest"
command: '["nginx", "-g", "daemon off;"]'
Now for the templates
directory. This is where all the magic happens. This directory is where we can
all of our template files and logic that we will need to generate our k8s manifests. I'm a big fan of naming the
files after the things they are going to build. In this case, we are going to build a pod, so we will name our file
pod.yaml
. This directory can contain things like tmpl
files too. Those are great for when
you have things that will be used in multiple files and you don't want to repeat yourself. A good example of things
to add to a template are things like labels and annotations.
---
apiVersion: v1
kind: Pod
metadata:
name: {{ .Release.Name }}-pod
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: {{ .Release.Name }}-container
image: {{ .Values.image }}
command: {{ .Values.command }}
Awesome, now with all that in place we can start to see how Helm can be used to generate k8s manifests. The helm template command is a great way to visualize what Helm will build from our chart. Let's run it against our basic chart to see what will be generated. I'll be inside the heml-chart directory when I run this command, but you can reference it from anywhere on your system. For this run we'll call our release "demo".
$ helm template demo .
---
# Source: helm-chart/templates/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: demo-container
image: nginx:latest
command: ["nginx", "-g", "daemon off;"]
The chart renders out as expected. Now we can see where Helm starts to shine. Using the same chart let's build
a few diferent pods and see how we can layer or override files to create a few varriations of the same chart.
We'll start by creating a new file called hello-world.yaml
. This file will only contain the values we need to
run a simple hello world container. The override files don't have to be in the same directory as the chart, but
they will need to be accissible to the helm command.
image: "hello-world:latest"
command: '["echo", "hello world"]'
helm template demo . -f ../hello-world.yaml
---
# Source: helm-chart/templates/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: demo-container
image: hello-world:latest
command: ["echo", "hello world"]
Using the same command and adding -f with the path to the override file, we are able to see the new values rendered
in our output. The image and command fields have been updated to reflect the values in the hello-world.yaml
file.
Let's take this one step further and see how we can use override files to layer on top of each other. We'll create
a new override file that will only change the image field. First, we'll run the single override file to see how
the default values will work. Then, we'll run the two override files together to see how they layer on top of each
other.
image: "alpine:latest"
$ helm template demo . -f ../alpine.yaml
---
# Source: helm-chart/templates/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: demo-container
image: apline:latest
command: ["nginx", "-g", "daemon off;"]
Since we only set the image field in the alpine.yaml
file, the command field defaulted to the value in our charts
values.yaml
file. Now let's see how the two override files layer on top of each other. We'll run the same command
$ helm template demo . -f ../hello-world.yaml -f ../alpine.yaml
---
# Source: helm-chart/templates/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: demo-container
image: apline:latest
command: ["echo", "hello world"]
The two override files layered on top of each other as expected. The image field was updated to the value in the
alpine.yaml
file and the command field was updated to the value in the hello-world.yaml
file. It's important to
know that odering of the override files matters. Each file has the ability to override the file defined before it,
so keep this in mind when ordering you override files. This is just a small example of what Helm can do. There are a lot of other features that Helm
has to offer. I hope this walkthrough has given you a good idea of what Helm can do and how it can be used to
generate k8s manifests.
Argocd app of apps definition.
Argo Workflows up and running
Automating workflows with github actions.
Simple Api using gin-gonic.
Building K8s manifests with Helm3
A collection of kubectl commands that have helped me a ton.
Configuring Role Based Access Control in Kubernetes.
Making images smaller with multi stage builds.
Keeping secrets out of your container images even while using private modules