Kustomize allows us to build configuration without templates. We can define base configuration files and then transform them - for example, a typical use-case with Kubernetes might be defining a Deployment
and then updating the replicas
(replica count) property depending on the environment. Instead of parameterising this value, we define it in an overlay. The file structure might look like this:
1 | |-- base |
The base deployment.yaml
file would contain our Deployment definition. The overlay replica_count.yaml
would contain the same kind
and name
metadata properties as the deployment manifest in base, but then just the properties we want to overlay. For example:
1 | apiVersion: apps/v1 |
The Kustomizaiton files define references to the resources that should be included. For the base file, it would look like this:
1 | resources: |
For the overlay Kustomization file, it would also define how the overlay resources should be applied. There’s a few ways to do this. In this case, we can define the overlay files as patches
(see docs here).
1 | resources: |
The important thing to note is that bases (defined as resources in the Kustomization file for the overlay) have no knowledge of overlays - a base could be used in multiple overlays. We can also reference bases in Git (for example, github.com/robselway/kustomizeexample/examples/base?ref=v1.0.0
).
Kustomize has lots of handy features and short-cuts for common use-cases. For example, if you’re updating an image
property you don’t have to define another file - you can do it inline in the overlay Kustomization file:
1 | images: |
There’s also a short-cut for updating the replica count, so defining a file like we did above isn’t strictly necessary.
For a full list of possibilities with Kustomize, see the docs here.
Installing Kustomize
The documentation outlines a few ways to install Kustomize. I’m running Ubuntu so used the go get
command but changed $GOBIN
to be /user/local/bin
, which is already part of $PATH
. This requires elevated permmissions.
1 | sudo GOBIN=/usr/local/bin/ GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3 |
Handling generic configuration
Although Kustomize is intended for use with Kubernetes manifest files, it can also be used to generically compose configuration.
For example, you might have generic configuration containing data like IP addresses for allow lists, or company information such as trading name and VAT number. Kustomize allows us to compose this information together and overlay additional information depending on the tenant (for example, additional office IP addresses in production for an internal application). This configuration could then be presented as JSON files for use outside of Kubernetes.
Kustomize only requires two properties in a manifest file:
.kind
.metadata.name
As long as we have these, we can define whatever we like for the rest of the documents.
The full example is on GitHub. We start with the following file structure:
1 | |-- base |
company_info.yaml
looks like this:
1 | kind: custom-config |
And security_config.yaml
looks like this:
1 | kind: custom-config |
In this example we simply want to append some additional IP addresses to .config.office_ips
. We can do this using the patchesJson6902
functionality in Kustomize:
1 | apiVersion: kustomize.config.k8s.io/v1beta1 |
security_config_jsonpatch.yml
looks like this:
1 | - op: add |
And finally, the output of kustomize build overlays/production
:
1 | config: |
Splitting the files and converting to JSON
For my use case, I want the end result to be separate JSON files for each document. I can accomplish this with a bash script (thanks to this StackOverlow answer for the inspiration):
1 | # Version 4.11.2 of yq (https://github.com/mikefarah/yq) |
This leaves me with the following output:
1 | |-- .outputs |
Validation
Kustomize provies a structured way to validate the output of our configuration. We can create a validator plugin (a bash script in this case) and reference it using a standard YAML manifest file. The file structure we had above will have the following added to it:
1 | |-- kustomize |
The validator.yaml
looks like this (the apiVersion
is deconstructed to resolve to the correct file path - the .metadata.name
property does not matter):
1 | apiVersion: security.example.com/v1 |
This file is referenced in the Kustomization file like so:
1 | validators: |
In this validator I want to check that a particular IP address is always included in our security-config
file, in the .config.office_ips
list. The script looks like this (inspired from the same StackOverflow answer):
1 |
|
The script must not alter the output (Kustomize will check and throw an error if this happens). There is one exception - a label of validated-by
can be added.
We also need to use the --enable_alpha_plugins
flag when calling kustomize build
, otherwise you will see an error like this:
1 | Error: external plugins disabled; unable to load external plugin |
On a successful validation, the output proceeds as normal. When validation fails, we get a friendly error messasge:
1 | Important IP addresses missing from config: 4.4.4.4 |
Conclusion
Kustomize offers a structured and declarative way of composing configuration. As it only requires minimal metadata in each YAML file to work, we can use it for any configuration schema we like.
Verifying the configuration is an added bonus - although it’s worth thinking through any security and stability implications of allowing the tool to run a script in your environment.
The validation I demonstrated here is relatively simple. If you are composing Kubernetes manifests, you could use a tool like kubeval
to check the files against the K8s OpenAPI schemas.
Further reading
- Documentation of all Kustomize capabilies (I always seem to struggle to find this)
- Transformer plugins - similar to my validation example, but you can alter the output
- Great post demonstrating further JSON patch 6902 operations
- Secrets with Kustomize - remember you could also use
kustomize edit add secret
to create the secret at runtime, instead of adding sensitive information to source control