Kustomize’s helmCharts field lets you inject Helm chart values directly into your Kubernetes manifests, but it’s not just a simple value merge; it’s a full-blown Helm render operation that Kustomize orchestrates.
Let’s see Kustomize in action with a simple example.
Imagine you have a kustomization.yaml that references a Helm chart:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
helmCharts:
- chart: ./my-chart
releaseName: my-release
valuesFile: ./my-values.yaml
And a my-chart directory containing a standard Helm chart structure (Chart.yaml, values.yaml, templates/deployment.yaml).
templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-app
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-app
template:
metadata:
labels:
app: {{ .Release.Name }}-app
spec:
containers:
- name: app
image: nginx:{{ .Values.imageTag }}
ports:
- containerPort: 80
my-values.yaml:
replicaCount: 3
imageTag: "1.21.6"
When you run kustomize build ., Kustomize will:
- Locate the Helm chart: It finds
./my-chart. - Read
my-values.yaml: It loads the specified values. - Render the Helm chart: It uses Helm’s templating engine to process
templates/deployment.yamlwith the provided values. - Inject the rendered manifest: The output of the Helm render (a complete
Deploymentobject) is then included in Kustomize’s output.
The generated output would look something like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-release-app
spec:
replicas: 3
selector:
matchLabels:
app: my-release-app
template:
metadata:
labels:
app: my-release-app
spec:
containers:
- image: nginx:1.21.6
name: app
ports:
- containerPort: 80
This mechanism allows you to leverage the power of Helm for templating complex applications while integrating them seamlessly into a Kustomize workflow, managing your entire Kubernetes configuration as code. You can also specify nameSuffix, namePrefix, namespace, and version to further customize how the Helm chart is integrated.
The true power lies in Kustomize’s ability to patch these Helm-rendered resources. You can have a helmCharts entry and patchesStrategicMerge or patchesJson6902 entries in the same kustomization.yaml. Kustomize applies its patches after the Helm chart has been rendered and its output injected. This means you can take a complex Helm chart, render it, and then apply granular, Kustomize-native adjustments to the resulting Kubernetes objects without modifying the original Helm chart’s templates or values.
For instance, if you wanted to add a label to the my-release-app Deployment generated by Helm, you could add:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
helmCharts:
- chart: ./my-chart
releaseName: my-release
valuesFile: ./my-values.yaml
patchesStrategicMerge:
- deployment-patch.yaml
And deployment-patch.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-release-app # Must match the rendered name
labels:
environment: production
This would result in the Deployment having both the labels generated by Helm and the environment: production label. The releaseName from the helmCharts field is crucial for targeting the correct resource in subsequent patches.
The most surprising aspect is that Kustomize doesn’t just merge values; it performs a full Helm rendering. This means any logic within your Helm templates, including if statements, range loops, and function calls, is executed. Kustomize effectively acts as a Helm client, but instead of helm install or helm template, it outputs the rendered Kubernetes manifests directly into its own build process. This allows for a declarative, GitOps-friendly way to manage Helm-templated applications alongside other Kubernetes resources.
When you start using helmCharts with multiple charts or charts that have complex dependencies, you’ll quickly encounter the need to manage the order of operations, especially when dealing with cross-chart dependencies or when Kustomize needs to apply patches to resources generated by different Helm charts.