Taking a Tour of Helmfile

Happy New Year's Eve everyone!

Well, the blog's been live for a few weeks now, and I've left you lovely folks hanging with a single post about how the blog is hosted. Well, what better way to ring in the new year than writing about one of my favorite infrastructure as code languages, Helm.

As every good DevOps engineer should, I maintain a small home lab for various side projects, from home automation, to infrastructure that comes in handy for other projects (e.g. running my own GitLab runners so I don't run out of CI/CD minutes on their free plan). Also, being a big fan of Kubernetes, I run most of my home lab applications on a small K3s cluster run on some Raspberry Pi's and other old hardware I have laying around. I'm planning to do a post that dives in depth on my K3s set up, but for now I want to talk about something that I find more interesting than that: Helmfile.

Now those familiar with K8s have probably heard about a tool called "Helm". Helm is an infrastructure as code lanuage that builds on top of the basic Kube manifest YAML framework and offers nice Go templating, variables, control and reptition structures, and more. Helm has largely become the community's favourite in terms of IaC solutions for K8s, and thus, has helm "charts" available for popular applications like NGINX, MongoDB, Kafka, etc. This makes it easy for the end user to simply grab a helm chart to deploy what they need, configure as they need it, without needing to worry about building YAML around the docker image for the app they need.

Now, you're probably wondering what Helmfile means with relation to Helm. Well, just as Docker Compose is a way to combine and orchestrate docker containers for local development set ups, Helmfile can be thought of as a way to combine an orchestrate Helm releases that depend on each other. This works particularly well for a home lab as you can have a single Helmfile that describes your home lab set up and maintain the state of your home lab centrally with a simple helmfile apply.

Enough with the hypotheticals though, let's dig into some code! Let me show you the current state of my home lab's Helmfile:

 1repositories:
 2- name: k8s-at-home
 3  url: https://k8s-at-home.com/charts/
 4- name: gitlab
 5  url: https://charts.gitlab.io
 6- name: longhorn
 7  url: https://charts.longhorn.io
 8
 9helmDefaults:
10  kubeContext: default
11
12releases:
13  - name: longhorn
14    namespace: longhorn-system
15    chart: longhorn/longhorn
16
17  - name: home-assistant
18    namespace: home-automation
19    chart: k8s-at-home/home-assistant
20    needs:
21    - longhorn-system/longhorn
22    values:
23    - persistence:
24        config:
25          enabled: true
26          storageClass: longhorn
27
28  - name: adguard-home
29    namespace: home-automation
30    chart: k8s-at-home/adguard-home
31
32  - name: gitlab-runner
33    namespace: default
34    chart: gitlab/gitlab-runner
35    values:
36    - imagePullPolicy: IfNotPresent
37
38      gitlabUrl: https://gitlab.com/
39
40      concurrent: 3
41
42      rbac:
43        create: true
44
45      runners:
46        tags: "home-lab"
47        image: ubuntu:20.04
48        secret: gitlab-runner-secret
49
50        namespace: default
51
52        builds:
53          cpuLimit: 200m
54          cpuLimitOverwriteMaxAllowed: 400m
55          memoryLimit: 256Mi
56          memoryLimitOverwriteMaxAllowed: 512Mi
57          cpuRequests: 100m
58          cpuRequestsOverwriteMaxAllowed: 200m
59          memoryRequests: 128Mi
60          memoryRequestsOverwriteMaxAllowed: 256Mi
61
62        services:
63          cpuLimit: 200m
64          memoryLimit: 256Mi
65          cpuRequests: 100m
66          memoryRequests: 128Mi
67
68        helpers:
69          cpuLimit: 200m
70          memoryLimit: 256Mi
71          cpuRequests: 100m
72          memoryRequests: 128Mi
73
74      resources:
75        limits:
76          memory: 256Mi
77          cpu: 200m
78        requests:
79          memory: 128Mi
80          cpu: 100m

Now, let's unpack some of this code for the sake of learning. First off, we have a "repositories" block. This block describes the various helm repositories we want to pull from. This is especially important these days as most helm charts are kept in their own repo now instead of a monorepo.

Next up, we define some defaults, in my case, I'm simply pointing Helmfile at my home lab's Kubernetes context.

Finally, we have a list of releases that we want this Helmfile to keep track of. Now there's a few pieces to explain here. First off, each release always has 3 components: a name, a namespace and a chart to deploy in that release. Those 3 components are pretty basic to Helm as that's really all you need to deploy the default configuration of a helm chart.

Now, since most of the time folks want to customize their helm chart to suit their K8s set up, or their particular needs, we need to include "values", which is helm speak for basically overriding the chart's defaults with some of your own configuration. You can see examples of this in my Home Assistant and GitLab runner chart where I'm including a values YAML right in my helmfile.yaml to tell Helmfile to override these particular settings.

Another important part to Helmfile is its dependency mechanism. You can see this in my Home Assistant release as well. Basically, I persist my Home Assistant config to a K8s PVC, and that PVC is maintained by the Longhorn storage class. So what that means in practice, is my Home Assistant chart must wait for my Longhorn chart as it depends on the resulting storage class from it. This dependency link is shown in the "needs" block in my Home Assistant release.

Once we have this helmfile.yaml created, we can test it out with a few basic helmfile commands:

  • helmfile diff will show what exactly will change between what's currently in K8s and what's in the code
  • helmfile apply will actually deploy the changes described in the helmfile against the current state of the K8s cluster's helm releases

This pretty much concludes this very brief intro to Helmfile. We've really just scratched the surface of helmfile though, and there's plenty more to learn about it. For those wanting to do so, you can find the official docs here in the Helmfile project's Readme.

See you next time!