Using Helm charts with kpt
Recently I’ve been looking at kpt fn
as a driver for generating
configuration. The impetus is that kpt pkg
feels like that right way
to export and consume packages of configuraton in git repositories,
and this could work well in sympathy with other “GitOps”
tooling; however, I think that asking people to write
programs in YAML is a catastrophe, so there needs to be a way to
include other kinds of program.
Previously, I was concerned with packaging JavaScript
programs for use with kpt fn
. The real prize is to be
able to use kpt fn
as insulation around arbitrary means of
generating configuration, and not just a general-purpose programming
lanaguage. The first case in point must surely be Helm charts.
The official solutions
To start, I’ll examine the advice given in kpt
documentation:
Steps
- Fetch a Helm chart
- Expand the Helm chart
- Publish the kpt package
This is OK if you just want to have YAMLs you can apply to your
cluster then and there. But it’s far short of what you’d want as a
distributable package, since all the particulars for an environment
are decided statically in step 2. If you want there to be any
parameters to the package, you’ll have to go back and create them for
the expanded files with kpt cfg
, which is pretty underpowered
compared to Helm.
Somewhat undermining the advice quoted above, there is a Helm chart
template function available from the function
catalogue, but it doesn’t (yet?) work with kpt fn
– you have to run
it with docker
.
This may be a case of the examples running ahead of the released software; there are some technical barriers that would have to be overcome before the approach demonstrated worked well:
- Since it’s intended to work with any chart, it needs the chart to
be downloaded or vendored, and mounted into the container, which is
awkward for the otherwise streamlined user interface of
kpt fn
. - Similarly, values for the chart parameters have to be provided as a
file that gets mounted into the container, which subverts the
protocol of providing config in a
functionConfig
object.
In pursuit of an approach that produces a reusable package, and works
cleanly with kpt
, I’ll have to try another route.
Helm chart images
The helm-template
function does not satisfy because it needs you to
mount the chart and values into the container when you run it. So I
would like a method which
- doesn’t need you to do that; and,
- uses the function protocol (i.e,
functionConfig
) to supply parameters for the chart.
The git repo kpt-helm-demo demonstrates a method with those two properties.
The main trade-off is that you must build an image for each Helm chart you want to use. I do not see this as much of a disadvantage, since it’s easy to do generically, and the alternatives also have extra steps (like vendoring the chart).
This is how it works
The script run-helm.sh
in image/
speaks the
function protocol, by extracting values from the functionConfig
of
the input, running a Helm chart with those values, and assembling the
results for output as another ResourceList
.
The Dockerfile in image/
creates an image including
the script above, and the Helm chart named in build args.
With those, you can build a container image that will run a Helm chart:
$ docker build -t squaremo/flux-helm-chart ./image
(The image is so-named because I’ve made Flux the chart used by default. You don’t need to build to image to follow along with the rest of the post, since I’ve pushed it to Docker Hub.)
Then you can run that image with kpt fn
, but be aware that you need
at least one resource to provide input to the function, otherwise kpt fn
will exit without doing anything. There’s a namespace manifest in
instance/
to serve this purpose.
$ cat instance/ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: flux-system
$ kpt fn run instance/ --image=squaremo/flux-helm-chart -- releaseName=flux namespace=flux-system
The command line above explicitly mentions the image and gives some
parameters for the chart (actually for the helm template
invocation). It’s also possible to provide a config object, and to
provide values for the chart:
$ cat config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: squaremo/flux-helm-chart
data:
releaseName: flux
namespace: flux-system
values: |
git:
readonly: true
registry:
disableScanning: true
$ kpt fn run instance/ --fn-path=./config.yaml
Using the package elsewhere
The repository can be imported to another git repository using kpt pkg get
. If you do, say,
$ mkdir local-config
$ cd local-config
$ git init
$ kpt pkg get https://github.com/squaremo/kpt-helm-demo.git/instance flux-chart
.. you’ll get a local copy of the package which you can run:
$ kpt fn run flux-chart/instance --fn-path=flux-chart/config.yaml
You can now edit the config.yaml
and rerun the function to change
the generated files; and, use kpt pkg update
to get changes from
upstream.
Can’t I just write a config.yaml and run that?
Yes, you could. The image can be pulled from Docker Hub (or you can build your own, using the Dockerfile); the config file and a starter resource are all you need to run the function.
You will miss out on the benefit of kpt pkg
– being able to pull in
updates from upstream – but reasonably you might not care about that.
How is this different from just running the chart?
If you don’t care about kpt pkg
, you probably don’t care about using
kpt fn
either. So the premise of this post, using Helm charts in a
way that’s compatible with kpt
, would be moot.
Is this better than just shipping YAMLs?
I think so. It makes it easier to adjust a configuration to suit your needs, in the same way Helm makes that easier (and with the same downside – every chart has its own API).