Overlays over templating
As Kubernetes grew in popularity, people started looking for effective ways to manage their application manifests, their declarative descriptions of the Kubernetes resources they needed to run their apps on a cluster.
There’s a bit of difficulty in that — how can I use the same app manifests to target multiple environments? How can I produce multiple variants?
The most common solution is to use some kind of templating, usually Go templates along with Helm. Helm comes with many additional drawbacks and advantages, but we’re going to ignore those for today. We’re going to focus on the problems with templating.
An Alternative API
If used by enough people, you arrive at parameterizing every value in a template. At this point, you’ve provided an alternative API schema that contains an out-of-date subset of the full API [1].
Here’s a bit of the Datadog Helm chart while you ponder that:
To be fair the above is not the API provided by the Helm chart (that would be the values.yaml), but on every Helm chart I’ve worked on I’ve had to dive into the templates. And a template plus its values is difficult to reason about, more difficult than the manifests themselves.
Tooling
Bad tooling is a corollary of providing an alternative API.
Most tooling will be built off of the original API, and so the community has to build plugins (e.g. helm-kubeval).
I think the community has actually done a very good job of this, and I would credit that to Helm’s usefulness as a package manager. But why is it a templating tool and a package manager? And do we need a package manager for deploying our bespoke (in-house) applications?
Overlays
Overlays are a way to accomplish our original goal — producing variants — without templating.
You have a base, an overlay, and the variant. The base and the variant are in the same language, and the overlay operation should be crystal clear. Probably more easily understood through an example.
Imagine some card game, with rounds. You start with this hand (the base).
In 1 round, you experience the following change (the overlay):
Your resulting hand would be this:
Both the initial hand and the resulting hand — the base and the variant — are clear to anybody who gets cards. At no point is there a poorly-maintained template.
This idea, of having the input and the variant be in the same language, is the core of declarative application management (DAM). As the white paper on this topic states, the ideal tool should contain the ability to instantiate multiple variants while exposing and teaching the Kubernetes APIs [1]. Templating does allow for variants, but it obfuscates the APIs.
Kustomize as an implementation of DAM
The tool built from the principles in the Declarative App Management white paper is Kustomize. In Kustomize you define a base — a set of Kubernetes resources. You then apply one or more overlays to customize the base in ways that you’d obviously want to in Kubernetes, like add resources or labels or annotations. You produce a variant, usually ready for a specific environment.
They key thing that makes this a great tool is that the input is crystal clear (it’s just normal Kubernetes manifests). Overlays are similarly easy to understand. The whole process is well-scoped and easily understandable—the tool is not doing more than it should.
Example
Here’s my own adapted version of Kustomize’s hello world, after playing around with it for a while. It’s a really fun tool to play around with.
Prerequisites
Install kustomize and kubectl. You can also optionally run a Kubernetes cluster.
Clone the repo
And you should see the full base kustomization. A kustomization is a kustomization.yaml file, or a directory containing one of those files. You can think of it as a general set of Kubernetes resources, with some extra metadata.
We have a ConfigMap, a Service of type LoadBalancer, and a Deployment. Let’s deploy just the base, and see what we get.
Now if I go to the load balancer’s external IP, I see a simple website.
Overlays
Now let’s add a very simple overlay. An overlay (in Kustomize) is a kustomization that depends on another kustomization.
Here is overlays/dev/kustomization.yaml
Let’s look at the diff between the base and the overlay
That’s it—we just replaced the variant label with the value dev. Now let’s use it to actually make a change to our app. We’ll build another overlay, for the staging environment. Here’s our kustomization.yaml.
And the new ConfigMap that we’re merging with the one in the base:
We can see the effects on our website
What else can we do with a kustomization?
Here’s a non-exhaustive list:
- Target multiple resource sources to form the base
- Add common labels or annotations
- Use CRDs
- Modify the image or tag of a resource
- Prepend or append values to the names of all resources and references
- Add namespaces to all resources
- Patch resources
- Change the number of replicas for a resource
- Generate Secrets and ConfigMaps, and control the behaviour of ConfigMap and Secret generators
- Substitute name references (e.g. an environment variable)
The more I think about this, the more obvious and appealing the idea is. Although I’m sure I have many of these experiences in front of me:
Sources
- Declarative App Management
- Kustomize docs
Wow! You read the whole thing. People who make it this far sometimes
want to receive emails when I post something new.
I also have an RSS feed.