Wat je aan het einde hebt
Een Kustomize-project met een gedeelde base en per-omgeving overlays (dev, staging, productie) die je met één kubectl apply -k commando kunt toepassen. Elke overlay past replicas, images, resource limits en configuratie aan zonder de base-manifests te wijzigen.
Vereisten
kubectlv1.21 of nieuwer (Kustomize-support werd bijgewerkt van v2.0.3 naar v4.0.5 in v1.21; oudere versies missen features die in deze gids worden gebruikt)- Een Kubernetes-cluster om tegen te deployen (elke distributie; een lokaal kind- of minikube-cluster werkt prima)
- Bekendheid met Kubernetes Deployments, Services, ConfigMaps en namespaces
- Optioneel: de standalone
kustomizebinary voor CI/CD-pipelines die nieuwere features nodig hebben dan wat jekubectlbundelt
Wanneer Kustomize gebruiken in plaats van Helm
Kustomize en Helm lossen verwante maar verschillende problemen op. Een kort besliskader:
| Scenario | Aanbevolen tool |
|---|---|
| Een upstream chart installeren (nginx-ingress, cert-manager) | Helm |
| Je app distribueren naar externe teams of klanten | Helm |
| Multi-environment configuratie voor een interne app | Kustomize |
| Conditionele YAML-structuur (if/else-logica in manifests) | Helm |
| Leesbare, auditeerbare YAML in Git zonder template-syntax | Kustomize |
| Team dat niet bekend is met Go templates | Kustomize |
De twee sluiten elkaar niet uit. Volwassen platformteams gebruiken Helm voor het consumeren van upstream charts en Kustomize om die deployments per omgeving aan te passen. Voor een uitgebreide gids over de Helm-kant, zie mijn artikel over Helm chart best practices.
De base- en overlay-mappenstructuur opzetten
De gangbare structuur scheidt gedeelde manifests (de base) van omgevingsspecifieke wijzigingen (overlays):
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── replica-patch.yaml
├── staging/
│ └── kustomization.yaml
└── prod/
├── kustomization.yaml
└── resource-limits-patch.yaml
De base aanmaken
De base is een op zichzelf staande, geldige Kustomization die geen weet heeft van de overlays die ernaar verwijzen:
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
labels:
- includeSelectors: true
includeTemplates: true
pairs:
app.kubernetes.io/name: "inventory-api"
# k8s/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-api
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: inventory-api
template:
metadata:
labels:
app.kubernetes.io/name: inventory-api
spec:
containers:
- name: app
image: registry.example.com/inventory-api:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
# k8s/base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: inventory-api
spec:
selector:
app.kubernetes.io/name: inventory-api
ports:
- port: 80
targetPort: 8080
Je kunt de base direct toepassen met kubectl apply -k k8s/base/. Dat is een bewust Kustomize-ontwerpprincipe: base-bestanden zijn altijd geldige Kubernetes-YAML, nooit templates.
Een overlay aanmaken
Elke overlay verwijst naar de base en legt omgevingsspecifieke wijzigingen eroverheen:
# k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
namePrefix: prod-
replicas:
- name: inventory-api
count: 5
images:
- name: registry.example.com/inventory-api
newTag: v1.4.2 # pin naar een release-tag in productie
patches:
- path: resource-limits-patch.yaml
target:
kind: Deployment
name: inventory-api
# k8s/overlays/prod/resource-limits-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-api
spec:
template:
spec:
containers:
- name: app
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
Een overlay toepassen
# Bekijk de gerenderde manifests
kubectl kustomize k8s/overlays/prod/
# Pas toe op het cluster
kubectl apply -k k8s/overlays/prod/
Verwachte output (verkort):
namespace/production configured
service/prod-inventory-api created
deployment.apps/prod-inventory-api created
Controleer of de Deployment is aangemaakt met het juiste aantal replicas:
kubectl get deployment prod-inventory-api -n production -o jsonpath='{.spec.replicas}'
# Verwacht: 5
Kies een patchstrategie
Kustomize ondersteunt twee patchmechanismen, beide toegankelijk via het uniforme patches-veld. De oudere velden patchesStrategicMerge en patchesJson6902 zijn deprecated in Kustomize v5.
Strategic merge patches
Een strategic merge patch ziet eruit als een gedeeltelijke kopie van de resource die je wijzigt. Kustomize merged het via Kubernetes merge-strategieën (lijsten van containers worden samengevoegd op naam, niet volledig vervangen).
Gebruik strategic merge als je velden toevoegt of wijzigt en het patchformaat intuïtief leesbaar is:
# Inline strategic merge patch in kustomization.yaml
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-api
spec:
template:
spec:
containers:
- name: app
env:
- name: LOG_LEVEL
value: "debug"
target:
kind: Deployment
name: inventory-api
JSON 6902 patches
RFC 6902 patches gebruiken expliciete operaties (add, remove, replace, move, copy, test) met JSON Pointer-paden. Verboseer, maar de enige nette manier om een veld te verwijderen of een lijst-item op index aan te spreken:
patches:
- patch: |-
- op: replace
path: /spec/template/spec/containers/0/resources/limits/memory
value: "512Mi"
- op: remove
path: /spec/template/spec/containers/0/livenessProbe
target:
group: apps
version: v1
kind: Deployment
name: inventory-api
Target selectors
Het target-blok ondersteunt regex en label-matching, waardoor één patch op meerdere resources kan worden toegepast:
target:
kind: Deployment
name: "inventory-.*" # regex
labelSelector: "tier=backend"
ConfigMaps en Secrets genereren met automatische rollouts
De configMapGenerator en secretGenerator maken resources aan vanuit bestanden, env-bestanden of literal waarden. De belangrijkste feature is het hash-achtervoegsel dat aan de gegenereerde naam wordt toegevoegd.
# k8s/overlays/prod/kustomization.yaml (toevoegen aan bestaand)
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=warn
- MAX_CONNECTIONS=50
envs:
- .env.prod # KEY=VALUE paren uit bestand
De gegenereerde ConfigMap krijgt een naam als app-config-9m5b4c7f. Wanneer de inhoud verandert, verandert de hash. Kustomize herschrijft elke referentie naar de ConfigMap in je Deployment-spec, waardoor de Deployment-spec zelf verandert en Kubernetes automatisch een rolling update start. Configuratiewijzigingen bereiken pods zonder handmatige kubectl rollout restart.
Verwijs in je base Deployment naar de generator-naam (zonder de hash):
# k8s/base/deployment.yaml (container spec)
envFrom:
- configMapRef:
name: app-config # Kustomize herschrijft dit naar app-config-<hash>
Als je een stabiele naam nodig hebt (bijvoorbeeld voor een DaemonSet die zijn eigen config-reload afhandelt), schakel het achtervoegsel dan uit:
configMapGenerator:
- name: stable-config
literals:
- KEY=value
options:
disableNameSuffixHash: true # pods herstarten NIET automatisch bij config-wijziging
Secrets: commit geen plaintext
secretGenerator werkt identiek aan configMapGenerator, maar produceert base64-encoded Secrets. Base64 is encoding, geen encryptie. Commit nooit literals: [password=mysecret] naar Git.
Voor productie-GitOps gebruik je een van deze aanpakken:
- SOPS + age-encryptie via de KSOPS-plugin: versleutelt alleen de waarden, keys blijven leesbaar voor diffs. Flux decodeert SOPS native.
- External Secrets Operator: synchroniseert secrets vanuit AWS Secrets Manager, HashiCorp Vault of Azure Key Vault naar Kubernetes Secrets. Je kustomization verwijst naar ExternalSecret-CRD's, niet naar echte waarden.
- Sealed Secrets: versleutel met de publieke sleutel van het cluster (Bitnami-controller). De SealedSecret-resource kan veilig gecommit worden.
Gebruik components voor cross-cutting features
Kustomize components (geïntroduceerd in v3.7.0) lossen het probleem op van N optionele features over M omgevingen. Zonder components kopieer je patchbestanden tussen overlays.
Een component gebruikt kind: Component en is niet zelfstandig deploybaar. Het werkt op de resources van welke parent Kustomization het ook includet:
# k8s/components/prometheus-metrics/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- service-monitor.yaml # voegt een ServiceMonitor toe
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory-api
spec:
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
target:
kind: Deployment
name: inventory-api
Neem het op in overlays die het nodig hebben:
# k8s/overlays/prod/kustomization.yaml
components:
- ../../components/prometheus-metrics
- ../../components/network-policy
# k8s/overlays/dev/kustomization.yaml
components:
- ../../components/prometheus-metrics
# geen network-policy in dev
Goede toepassingen voor components: Prometheus-scraping-annotaties, sidecar-injectie, HPA-configuratie, NetworkPolicy en feature flags via ConfigMap-patches.
Integratie met ArgoCD
ArgoCD heeft native Kustomize-ondersteuning. Verwijs een Application naar een map met kustomization.yaml en ArgoCD detecteert het automatisch en draait kustomize build voor het toepassen:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: inventory-api-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/my-org/inventory-api.git
targetRevision: HEAD
path: k8s/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
Voor multi-environment setups voorkomt een ApplicationSet herhaling per omgeving:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: inventory-api
spec:
generators:
- list:
elements:
- env: dev
cluster: https://dev.k8s.example.com
- env: staging
cluster: https://staging.k8s.example.com
- env: prod
cluster: https://prod.k8s.example.com
template:
metadata:
name: "inventory-api-{{env}}"
spec:
source:
repoURL: https://github.com/my-org/inventory-api.git
path: "k8s/overlays/{{env}}"
destination:
server: "{{cluster}}"
namespace: "{{env}}"
Let op: de Kustomize-versie die ArgoCD bundelt kan achterlopen op de standalone binary. Als je v5-features nodig hebt (het moderne labels- of replacements-veld), configureer dan een custom versie in argocd-cm.
Integratie met Flux
Flux gebruikt de Kustomization CRD van kustomize.toolkit.fluxcd.io om Kustomize-overlays in een Kubernetes-native workflow te verpakken:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: inventory-api
namespace: flux-system
spec:
interval: 1m
url: https://github.com/my-org/inventory-api.git
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: inventory-api-prod
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: inventory-api
path: "./k8s/overlays/prod"
prune: true
targetNamespace: production
wait: true
Flux voegt mogelijkheden toe bovenop gewone Kustomize. Twee die er het meest toe doen:
SOPS-decryptie zonder plugin: Flux' kustomize-controller decodeert SOPS-versleutelde secrets native:
spec:
decryption:
provider: sops
secretRef:
name: sops-age # Secret met je age private key
Post-build variabele-substitutie: lost ${VARIABLE}-tokens op in manifests nadat kustomize build is gedraaid. Dit is een Flux-feature, geen native Kustomize:
spec:
postBuild:
substitute:
ENVIRONMENT: production
REGION: eu-west-1
substituteFrom:
- kind: ConfigMap
name: cluster-vars
Afhankelijkheidsvolgorde zorgt dat infrastructuur vóór applicaties landt:
spec:
dependsOn:
- name: infra-prod
Het complete project verifiëren
Draai na het opzetten van de volledige structuur een dry-run diff voordat je toepast, om misconfiguraties te vangen:
# Render en vergelijk met de clusterstatus
kubectl diff -k k8s/overlays/prod/
Als de diff er goed uitziet, pas dan toe:
kubectl apply -k k8s/overlays/prod/
Bevestig dat alle resources zijn aangemaakt:
kubectl get all -n production -l app.kubernetes.io/name=inventory-api
Veelvoorkomende problemen
Immutable selector-fout bij opnieuw toepassen. Als je het verouderde commonLabels-veld hebt gebruikt, wijzigt dat label selectors, die Kubernetes na de eerste apply immutable maakt. Migreer naar het moderne labels-veld met includeSelectors: false, of verwijder en hermaak de Deployment.
Configuratiewijzigingen worden niet opgepikt door pods. Als je disableNameSuffixHash: true hebt gezet op een generator, verandert de Deployment-spec niet wanneer de ConfigMap-inhoud verandert. Verwijder de vlag om automatische rollouts te herstellen, of voeg kubectl rollout restart deployment/inventory-api toe aan je CI-pipeline.
Build faalt met "path not found." Kustomize lost alle paden op relatief aan het kustomization.yaml-bestand. Controleer dat ../../base daadwerkelijk naar de base-map verwijst vanuit de locatie van de overlay.
Deprecation-waarschuwingen in Kustomize v5. De velden patchesStrategicMerge, patchesJson6902, commonLabels en vars zijn deprecated. Gebruik in plaats daarvan patches, labels en replacements. Zie de migratiegids van Nick Janetakis.
kubectl-gebundelde Kustomize-versie komt niet overeen. De Kustomize-versie binnen kubectl kan ouder zijn dan de standalone binary. Draai kubectl version --client en kustomize version om te vergelijken. Installeer voor CI de standalone binary en gebruik kustomize build | kubectl apply -f -.
Compleet overlay-voorbeeld
Ter referentie: hier is de volledige prod overlay kustomization.yaml met alle features uit deze gids gecombineerd:
# k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
namePrefix: prod-
replicas:
- name: inventory-api
count: 5
images:
- name: registry.example.com/inventory-api
newTag: v1.4.2
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=warn
- MAX_CONNECTIONS=50
components:
- ../../components/prometheus-metrics
- ../../components/network-policy
patches:
- path: resource-limits-patch.yaml
target:
kind: Deployment
name: inventory-api