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

  1. Fetch a Helm chart
  2. Expand the Helm chart
  3. 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

  1. doesn’t need you to do that; and,
  2. 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).


965 Words

2020-05-18 00:00 +0000