helmfile — it’s like a helm for your helm!
Hi,
So at Transit we’ve been migrating our workloads to GKE. Naturally, we’ve been using helm to package them.
Until recently, we had a pretty OK bash script to install desired releases to various clusters, but wanted a more declarative (and readable!) approach.
My buddy (and CNCF ambassador extraordinaire) Archy told me about helmfile so we checked it out and a few hours of YAML later our bash script was history and we were using helmfile
in production to manage all our releases.
Let’s take a look at what works for us.
Environments
So you can declare environments in helmfile
, e.g.:
environments:
production:
staging:
test:
Helmfile structure
There’s a couple of ways to split up your helmfiles
.
E.g. you could have 1 helmfile
per environment, but then again helmfile
comes with a -e
flag for that.
We went with the approach of having a helmfile.d
directory with multiple helmfiles
in it, each representing a namespace
:
helmfile.d/
├── default.yaml
├── logging.yaml
├── monitoring.yaml
├── rt-data.yaml
├── sqlproxy.yaml
└── tracing.yaml
We will take a look at what one of these looks like, but first check out the values
files structure.
Values
It’s worth noting that just like there are many approaches to structuring your helmfile(s)
, there is a lot of flexibility with regards to how to declare your values
. In fact you can have it such that you do not even need separate values
files, but instead just declare values
directly in the helmfile(s)
.
However, in our case, we simply re used the values
files we had already made for our now deprecated bash script. So the structure is /values/<namespace>/<release>/<env>.yaml
as seen here:
values
├── default
│ └── my-nginx-ingress-release
│ ├── test.yaml
│ ├── production.yaml
│ └── staging.yaml
├── logging
│ ├── my-elasticsearch-release
│ │ ├── test.yaml
│ │ ├── production.yaml
│ │ └── staging.yaml
│ └── my-kibana-release
│ ├── test.yaml
│ ├── production.yaml
│ └── staging.yaml
├── monitoring
│ ├── my-grafana-release
│ │ ├── production.yaml
│ │ └── staging.yaml
│ ├── my-prometheus-release
│ │ ├── production.yaml
│ │ └── staging.yaml
│ └── my-prometheus-operator-release
│ └── test.yaml
├── rt-data
│ └── my-rt-server-release
│ ├── test.yaml
│ ├── production.yaml
│ └── staging.yaml
├── sqlproxy
│ └── my-gcloud-sqlproxy-release
│ ├── test.yaml
│ ├── production.yaml
│ └── staging.yaml
└── tracing
└── my-jaeger-release
├── test.yaml
├── production.yaml
└── staging.yaml
Ok great. So now there was something to consider, we did not want every release
installed in every environment (notice the values
files in the monitoring
namespace).
What release goes where
Have a look at the monitoring.yaml
helmfile
from our helmfile.d
directory and thanks to YAML’s readability you‘ll see how we can declare in which environment(s) a given release
should be installed (also check out the sweet Release Template (templates:
)feature which is covered in the docs).
templates:
monitoring: &monitoring
chart: stable/{{`{{ .Release.Name }}`}}
missingFileHandler: Error
namespace: monitoring
values:
- ../values/monitoring/{{`{{ .Release.Name }}`}}/{{`{{ .Environment.Name }}`}}.yamlreleases:- name: my-grafana-release
installed: {{ eq .Environment.Name "staging" "production" }}
<<: *monitoring- name: my-prometheus-release
installed: {{ eq .Environment.Name "staging" "production" }}
<<: *monitoring- name: my-prometheus-operator-release
installed: {{ eq .Environment.Name "test" }}
<<: *monitoring
Notice the use of Go templating for the value of installed
? Pretty sweet!
Conclusion
Helmfile
rocks, a big thank you to everyone working on this amazing tool with special shoutouts to Mumoshu who was very quick to resolve two issues that were getting in the way of the above approach.
So if you are looking for a helm
for your helm
, swing by https://github.com/roboll/helmfile