Kubernetes manifests can get messy fast, especially when managing multiple environments. Kustomize helps you keep things clean by letting you customize YAML files without copying or rewriting them. In this article, we’ll explore how Kustomize simplifies and streamlines Kubernetes configuration.
What does Kustomize do?#
let's say you already have a template for deployment, or you can generate sample deployment yaml from this command
1
| kubectl create deployment <your deployment name> --image dummy --dry-run=client -o yaml
|
this will generate a deployment yaml, then you can delete the following lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null # this
labels:
app: my-app
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
strategy: {} # this
template:
metadata:
creationTimestamp: null # this
labels:
app: my-app
spec:
containers:
- image: dummy
name: my-container
resources: {} # this
status: {} # this
|
Change image#
Create kustomization.yaml file
1
2
3
4
5
6
| resources:
- deployment.yaml
images:
- name: dummy # this must be match the image in the deployment.yaml
newName: my-registry/my-image
newTag: my-tag
|
The resources section lists the files or manifests to customize, and the images section updates the deployment image.
The deployment manifest will be updated, and the image name and tag will change according to the kustomization file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: my-registry/my-image:my-tag
name: my-container
|
Generators#
Kustomize also supports generators, such as secretGenerator and configMapGenerator, which creates resources from the input file(s).
nginx.conf
1
2
3
4
5
6
7
8
9
10
| events {}
http {
server {
listen 80;
location / {
return 200 "Hello from ConfigMap Nginx!\n";
}
}
}
|
kustomization.yaml
1
2
3
4
| configMapGenerator:
- name: nginx-config
files:
- nginx.conf
|
This will generate a ConfigMap using the data from nginx.conf.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| apiVersion: v1
data:
nginx.conf: |
events {}
http {
server {
listen 80;
location / {
return 200 "Hello from ConfigMap Nginx!\n";
}
}
}
kind: ConfigMap
metadata:
name: nginx-config-59k264tbg4
|
it's also work for secretGenerator
1
| echo "admin:admin123" > credential
|
kustomization.yaml
1
2
3
4
| secretGenerator:
- name: admin-secret
files:
- credential
|
this will generate Opaque type secret and base64 encoded
1
2
3
4
5
6
7
| apiVersion: v1
data:
credential: YWRtaW46YWRtaW4xMjMK
kind: Secret
metadata:
name: admin-secret-kh798fmhhc
type: Opaque
|
Patches#
Patches in Kustomize are used to modify or override specific fields in existing resources without changing the original files. For example, we can use a patch to add a ConfigMap volume and mount it into the base Deployment without modifying the original file.
cm-patch.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
volumes:
- name: nginx-config
configMap:
name: nginx-config # must be match with the name from configMapGenerator
containers:
- name: my-container
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
|
kustomization.yaml
1
2
3
4
5
6
7
8
9
10
11
12
| resources:
- deployment.yaml
images:
- name: dummy
newName: my-registry/my-image
newTag: my-tag
configMapGenerator:
- name: nginx-config
files:
- nginx.conf
patches:
- path: cm-patch.yaml
|
This will generate a Deployment manifest with the ConfigMap volume and mount already included.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| apiVersion: v1
data:
nginx.conf: |
events {}
http {
server {
listen 80;
location / {
return 200 "Hello from ConfigMap Dev Nginx!\n";
}
}
}
kind: ConfigMap
metadata:
name: nginx-config-59k264tbg4
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: my-registry/my-image:my-tag
name: my-container
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
name: nginx-config
subPath: nginx.conf
volumes:
- configMap:
name: nginx-config-59k264tbg4
name: nginx-config
|
Bases & Overlays#
Kustomize separates config into bases and overlays. Bases hold reusable resources, while overlays build on them with extra changes. Bases can be local or remote and don’t depend on overlays. Based on what we have done before, now we will use bases and overlays to make it more structured.
example:
1
2
3
4
5
6
7
8
9
10
11
| .
├── base
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ └── service.yaml
└── overlays
└── dev
├── kustomization.yaml
├── nginx.conf
└── patches
└── cm-patch.yaml
|
The base holds the original template manifests, and the overlays contains the changes or patches applied to them.
base/deployment.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: dummy
name: my-container
|
base/service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: ClusterIP
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: my-app
|
base/kustomization.yaml
1
2
3
| resources:
- deployment.yaml
- service.yaml
|
overlays/dev/nginx.conf
1
2
3
4
5
6
7
8
9
10
| events {}
http {
server {
listen 80;
location / {
return 200 "Hello from ConfigMap Nginx!\n";
}
}
}
|
overlays/dev/kustomization.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| namePrefix: dev-
resources:
- ../../base # reffering to base kustomization
images:
- name: dummy
newName: nginx
newTag: alpine
configMapGenerator:
- name: nginx-config
files:
- nginx.conf
patches:
- path: patches/cm-patch.yaml
- path: patches/nodeport.yaml
|
overlays/dev/patches/cm-patch.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
volumes:
- name: nginx-config
configMap:
name: nginx-config
containers:
- name: my-container
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
|
overlays/dev/patches/nodeport.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 31232
type: NodePort
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| apiVersion: v1
data:
nginx.conf: |
events {}
http {
server {
listen 80;
location / {
return 200 "Hello from ConfigMap Nginx!\n";
}
}
}
kind: ConfigMap
metadata:
name: dev-nginx-config-mb9bhc6c66
---
apiVersion: v1
kind: Service
metadata:
name: dev-my-app
spec:
ports:
- nodePort: 32323
port: 80
protocol: TCP
targetPort: 80
selector:
app: my-app
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: dev-my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: nginx:alpine
name: my-container
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
name: nginx-config
subPath: nginx.conf
volumes:
- configMap:
name: dev-nginx-config-mb9bhc6c66
name: nginx-config
|
You can also apply directly with the following command
1
2
| kubectl apply -k ./ # apply
kubectl get -k ./ # get component
|
1
2
3
4
5
6
7
8
| NAME DATA AGE
configmap/dev-nginx-config-mb9bhc6c66 1 6s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dev-my-app NodePort 10.43.194.245 <none> 80:32323/TCP 6s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/dev-my-app 1/1 1 1 6s
|
Summary#
Kustomize is a simple yet powerful way to tweak your Kubernetes YAMLs without touching the original files. With features like overlays, image overrides, and auto-generated ConfigMaps or Secrets, it makes managing different environments a breeze. It’s a favorite in CI/CD pipelines because it keeps things clean, organized, and easy to maintain even as your app grows.