Skip to main content

13.1. Kustomize Basics - Fundamentals and Overlays

Mục lục


1. Kustomize giải quyết vấn đề gì?

Một deployment NGINX chạy ngon trên máy local. Sếp bảo đưa lên staging. Tuần sau, đưa nốt lên production. Cùng một ứng dụng, nhưng số replicas phải khác nhau — máy local yếu chỉ cần 1, staging cần 2-3, production cần 5-10 để chịu được traffic thật.

Trước khi đi tiếp, thống nhất hai từ sẽ xuất hiện liên tục trong toàn bộ tài liệu này:

  • Resource là một Kubernetes object được mô tả bằng YAML — Deployment, Service, ConfigMap, Secret đều là resource.
  • Manifest là nội dung YAML mô tả một hay nhiều resource, đúng dạng mà kubectl apply nhận vào.

Quay lại ba môi trường. Giải pháp "nhanh" nhất là tạo ba thư mục riêng biệt, copy nguyên config vào mỗi thư mục, rồi sửa replicas tương ứng. Nghe hợp lý. Nhưng khi project phát triển, thêm Service, ConfigMap, Secret vào hệ thống — lúc đó phải copy tất cả sang mọi thư mục. Sửa một dòng ở bản gốc phải nhớ sửa lại ở tất cả nơi khác. Với năm môi trường, rất dễ bỏ sót một chỗ nào đó. Kết quả: mismatching configs — các môi trường không còn đồng nhất, và debug trở nên mệt mỏi vì không ai biết chắc config nào mới là đúng.

💡 Hình dung: Giống việc chỉnh sửa ảnh bằng cách copy nguyên file gốc ra nhiều bản, mỗi bản chỉnh một chút — sau vài lần, không còn biết bản nào là "nguồn chân lý". Kustomize giống layer trong Photoshop: một layer nền duy nhất, rồi chồng lên đó các adjustment layer riêng cho từng môi trường. Sửa layer nền một lần, mọi bản xuất ra đều đổi theo.

Câu hỏi cốt lõi: làm sao có một bộ config duy nhất làm nguồn chân lý, nhưng vẫn tùy biến được cho từng môi trường mà không phải copy paste? Kustomize ra đời để trả lời đúng câu hỏi đó.


2. Base và Overlays — hai trụ cột của Kustomize

2.1. Base config

Base là thư mục chứa những phần giống nhau trên mọi môi trường — nền tảng chung mà mọi biến thể sẽ kế thừa. Nó đóng vai trò "giá trị mặc định", và giá trị mặc định thì luôn có thể ghi đè khi cần.

2.2. Overlays

Overlay là một thư mục khác, chỉ chứa những thay đổi cần thiết cho một môi trường cụ thể. Development muốn 1 replica? Overlay khai báo đúng 1. Staging cần 3? Overlay khai báo đúng 3. Production cần 10? Overlay khai báo đúng 10.

Điểm mấu chốt: overlay không phải bản sao của base. Nó không chứa lại toàn bộ Deployment, chỉ chứa delta — đúng phần khác biệt so với base. Lúc build, Kustomize đọc base trước, rồi áp delta của overlay lên trên, sinh ra manifest hoàn chỉnh.

Kustomize: Base + Overlays = Final Configbase/nginx-deployment.yaml (replicas: 1)nginx-service.yamloverlays/devreplicas: 1overlays/stagingreplicas: 3dev: replicas=1staging: replicas=3kustomize buildFinal manifest: base + overlay deltaChỉ khai báo delta, không copy nguyên config.

2.3. Cấu trúc thư mục chuẩn

k8s/
├── base/
│ ├── nginx-deployment.yaml
│ └── kustomization.yaml
└── overlays/
├── development/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── production/
└── kustomization.yaml

Workflow đi theo một chiều rõ ràng: base chứa shared config, overlay chứa environment-specific delta, Kustomize kết hợp cả hai thành final manifest, rồi manifest đó mới được apply vào cluster. Phần tiếp theo của tài liệu sẽ dựng dần từng mảnh của workflow này, bắt đầu từ chỗ đơn giản nhất — một thư mục duy nhất, chưa có overlay.

2.4. Kustomize vs Helm

Helm dùng Go template — nhét biến {{ .Values.replicaCount }} vào file YAML, rồi tách riêng file values.yaml chứa giá trị thực. Cách này mạnh, nhưng cú pháp template không phải YAML thuần, khó đọc, và phải học thêm một ngôn ngữ template nữa.

Kustomize đi ngược lại: dùng YAML thuần, không template. Không có biến kiểu {{ .Values.x }} — chỉ có cấu trúc YAML chuẩn. Config nào cần override thì khai báo đúng override đó. Đơn giản, dễ đọc, dễ debug, và validate được bằng bất kỳ YAML linter nào.

Tiêu chíKustomizeHelm
Cú phápYAML thuần, validGo template, không valid YAML thuần
Độ phức tạpĐơn giảnPhức tạp hơn
Tính năngCơ bảnNhiều tính năng nâng cao hơn
Package managerKhôngCó (giống yum/apt)

Khi nào dùng cái nào? Kustomize phù hợp khi chỉ cần override vài giá trị trên từng môi trường. Helm phù hợp khi cần templating phức tạp, conditional logic, hooks, hoặc muốn đóng gói "chart" như một package để người khác install. Hai công cụ này cũng không loại trừ nhau — rất nhiều team render chart bằng Helm rồi vá phần còn lại bằng Kustomize.


3. Cài đặt Kustomize

3.1. Yêu cầu

Trước khi cài, đảm bảo đã có Kubernetes cluster đang chạy và kubectl đã cấu hình kết nối tới cluster đó.

3.2. Script cài đặt chính thức

Kustomize cung cấp một script tự nhận diện hệ điều hành và tải đúng binary của bản release mới nhất. Đây vẫn là cách cài binary được ghi trong tài liệu chính thức:

curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash

Lưu ý một chi tiết nhỏ nhưng hay làm mất thời gian: script tải binary về thư mục đang đứng, không tự cài vào PATH. Muốn gọi kustomize từ bất kỳ đâu, bạn cần tự chuyển nó vào một thư mục nằm trong PATH:

sudo mv ./kustomize /usr/local/bin/

Script cũng nhận tham số nếu cần ghim một phiên bản cụ thể hoặc chọn thư mục đích — ví dụ install_kustomize.sh 5.8.1 /usr/local/bin.

3.3. Xác minh cài đặt

kustomize version

# Output minh họa
# v5.8.1

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Lệnh này in đúng một dòng semver của binary. Nếu không thấy output nào, nhiều khả năng terminal hiện tại chưa nhận PATH mới. Đóng terminal, mở terminal mới, rồi thử lại. Nếu vẫn lỗi, kiểm tra xem binary đã thực sự nằm trong một thư mục thuộc PATH chưa.

3.4. Hai bản Kustomize — standalone và bản nhúng trong kubectl

Có một chi tiết đủ sức làm mất cả buổi debug: trên máy đang có hai bản Kustomize, không phải một.

Bản thứ nhất là binary kustomize vừa cài ở trên. Bản thứ hai nằm sẵn bên trong kubectl — chính là thứ chạy khi gõ kubectl kustomize hoặc kubectl apply -k. Hai bản được release độc lập với nhau, và bản nhúng trong kubectl thường đi sau bản standalone một hoặc vài phiên bản.

kubectl in ra cả hai con số ngay ở lệnh version:

kubectl version --client

# Output minh họa
# Client Version: v1.34.1
# Kustomize Version: v5.7.1

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Hệ quả rất thực tế: một field mới có từ Kustomize v5.8 sẽ chạy ngon với kustomize build, nhưng kubectl apply -k trên một kubectl còn nhúng v5.7 lại báo lỗi kiểu "unknown field". Config không sai — chỉ là hai chương trình khác nhau đang đọc nó.

Khi gặp tình huống "lệnh này chạy được, lệnh kia báo lỗi", việc đầu tiên nên làm là so hai con số version đó. Và nếu muốn chắc chắn manifest được render đúng bởi phiên bản mình đã test, hãy dùng kustomize build <dir> | kubectl apply -f -: lúc đó kubectl chỉ còn nhiệm vụ gửi YAML lên cluster, toàn bộ phần render do binary standalone lo.


4. File kustomization.yaml

4.1. File Kustomize tìm kiếm gì?

Khi chạy kustomize build, Kustomize tìm file có tên chính xáckustomization.yaml trong thư mục được chỉ định. File này phải được tạo thủ công và đặt đúng tên — đây là "điểm vào" để Kustomize biết cần quản lý những resource nào.

💡 Hình dung: kustomization.yaml giống một công thức nấu ăn. Nửa trên là danh sách nguyên liệu — món này cần những file YAML nào. Nửa dưới là phần hướng dẫn chế biến — nêm thêm gì, đổi gì trước khi dọn ra. Nguyên liệu trong tủ lạnh không bị đụng tới; chỉ có món ăn dọn ra đĩa là đã được nêm.

4.2. Hai phần cốt lõi

File kustomization.yaml chứa đúng hai thứ: danh sách resources cần quản lý, và các transformation áp dụng lên những resource đó.

Phần 1 — Resources:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- nginx-deployment.yaml
- nginx-service.yaml

Khai báo những file YAML nào Kustomize cần đọc và quản lý.

Phần 2 — Transformers:

Transformer là một khai báo bảo Kustomize sửa gì đó trên toàn bộ resource vừa import — thêm label, đổi namespace, gắn prefix vào tên. Kustomize đọc khai báo đó rồi tự sửa lúc build; file YAML gốc không bị chạm vào một dòng nào. Danh sách đầy đủ nằm ở mục 7.

labels:
- pairs:
company: KodeKloud

Kết quả: mọi Deployment và Service trong danh sách resources đều được thêm label company: KodeKloud, mà không ai phải mở từng file ra sửa tay.

4.3. Giải thích apiVersion và kind

Giống mọi Kubernetes resource khác, kustomization.yaml có thể khai báo apiVersionkind:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

kustomize.config.k8s.io/v1beta1 hiện vẫn là version chính thức của kind Kustomization. Về mặt kỹ thuật, Kustomize sẽ tự điền giá trị mặc định nếu không khai báo. Nhưng nên hard code hai dòng này: nhóm phát triển Kustomize đã dự kiến một API v1 trong tương lai, và những field đã deprecate ở v1beta1 sẽ bị loại hẳn khỏi v1. Ghi rõ apiVersion nghĩa là file của bạn nói rõ nó đang được viết theo luật của bản nào.

4.4. Build để xem kết quả

kustomize build k8s

Lệnh này in final config ra console — tất cả resources đã import, tất cả transformations đã apply. Quan trọng: kustomize build không apply vào cluster. Nó chỉ sinh ra manifest cuối cùng để xem trước.


5. Build và Apply — từ config đến cluster

💡 Hình dung: kustomize build là bản in thử đưa lên màn hình để soát lỗi. kubectl apply mới là lúc bấm nút cho máy in chạy thật. Soát bản in thử bao nhiêu lần cũng không tốn giấy; in nhầm ra production thì phải dọn.

5.1. Pipe sang kubectl apply

kustomize build chỉ hiển thị, cần pipe output sang kubectl apply để thực sự deploy:

kustomize build k8s | kubectl apply -f -

# Output minh họa
# deployment.apps/nginx-deployment created
# service/nginx-service created

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Giải thích từng phần: dấu | (pipe) chuyển output của lệnh bên trái thành input của lệnh bên phải. kustomize build k8s sinh final config. kubectl apply -f - đọc config từ stdin (-f - nghĩa là "đọc từ đầu vào chuẩn thay vì từ file") rồi apply vào cluster.

5.2. Cách ngắn gọn hơn

kubectl từ phiên bản 1.14 trở lên đã hỗ trợ Kustomize sẵn bên trong — chỉ cần flag -k:

kubectl apply -k k8s

-k nghĩa là "kustomize". kubectl tự chạy phần build rồi apply, tất cả trong một lệnh. Gọn hơn hẳn, và trong phòng thi CKA thì đây là cách nên dùng.

Đổi lại, phần build lúc này do bản Kustomize nhúng trong kubectl đảm nhiệm, chứ không phải binary standalone — đúng cái bẫy version đã nói ở mục 3.4. Với các field cơ bản trong tài liệu này thì không khác biệt gì; chỉ khi dùng field mới nhất mới cần để ý.

5.3. Xóa resources

Cú pháp tương tự, chỉ đổi apply thành delete:

kustomize build k8s | kubectl delete -f -
# Hoặc
kubectl delete -k k8s
Kustomize workflow: build → applykustomization.yaml+ YAML fileskustomize build→ Final manifestkubectl apply -f -→ ClusterPodRunningHoặc dùng shortcut: kubectl apply -k k8s(kubectl tự chạy kustomize build + apply)Lưu ý: kustomize build chỉ hiển thị manifest, không apply vào cluster.

Thói quen nên tập ngay từ đầu: build trước, đọc output, rồi mới apply. Nó tốn thêm đúng một lệnh, và đổi lại bạn không bao giờ phải đoán xem cluster vừa nhận được cái gì.


6. Multi-Directory — quản lý project lớn

6.1. Vấn đề khi project phình to

Khi YAML files tăng lên 20, 30, 50 file, không thể nhét tất cả vào một thư mục. Giả định có cấu trúc như sau:

k8s/
├── api/
│ ├── api-deployment.yaml
│ └── api-service.yaml
├── database/
│ ├── db-deployment.yaml
│ └── db-service.yaml
├── cache/
│ └── redis.yaml
└── kafka/
└── kafka.yaml

Cách làm thủ công là apply từng thư mục riêng:

kubectl apply -f k8s/api
kubectl apply -f k8s/database
kubectl apply -f k8s/cache
kubectl apply -f k8s/kafka

Bốn lệnh cho một lần deploy. Thêm một thư mục là thêm một lệnh phải nhớ, và không có cách nào build một phát để xem toàn bộ manifest cuối cùng.

💡 Hình dung: Một cuốn sách dày không bắt người đọc lật từng trang để tìm chương. Nó có mục lục ở đầu sách. kustomization.yaml ở thư mục gốc chính là mục lục đó — và khi sách quá dày, mỗi chương lại có mục lục riêng của nó.

6.2. Cách 1 — Root kustomization.yaml liệt kê từng file

Tạo root kustomization.yaml tại thư mục gốc k8s/, liệt kê đường dẫn từng file:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- api/api-deployment.yaml
- api/api-service.yaml
- database/db-deployment.yaml
- database/db-service.yaml
- cache/redis-config.yaml
- kafka/kafka.yaml

Bây giờ chỉ cần một lệnh cho tất cả:

kubectl apply -k k8s

6.3. Cách 2 — kustomization.yaml lồng nhau

Khi số lượng file tiếp tục tăng, danh sách resources trong root kustomization.yaml lại trở nên quá dài — chỉ là đổi chỗ vấn đề chứ chưa giải quyết. Cách tốt hơn: mỗi subdirectory tự có kustomization.yaml riêng, và root chỉ trỏ tới các subdirectory.

k8s/
├── api/
│ ├── api-deployment.yaml
│ ├── api-service.yaml
│ └── kustomization.yaml # Import files trong api/
├── database/
│ ├── db-deployment.yaml
│ ├── db-service.yaml
│ └── kustomization.yaml # Import files trong database/
└── kustomization.yaml # Import các subdirectory

Root kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- api
- database

kustomization.yaml trong api/:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- api-deployment.yaml
- api-service.yaml

Lưu ý: Khi chỉ định đường dẫn tới directory (không phải file), Kustomize tự động tìm file kustomization.yaml bên trong directory đó. Không cần viết rõ api/kustomization.yaml — chỉ cần api là đủ.

Lưu ý: Trong các config cũ, việc trỏ tới directory khác được viết bằng field riêng tên bases:. Field đó đã deprecate và nay gộp chung vào resources: — gặp bases: trong repo cũ thì đọc nó như resources: là đúng.

Lợi ích không chỉ nằm ở chỗ root gọn hơn. Mỗi subdirectory giờ là một đơn vị độc lập, build được riêng để kiểm tra (kustomize build k8s/api), và thêm một service mới chỉ là thêm một thư mục cùng một dòng trong root — không ai phải đọc lại toàn bộ danh sách file để chắc rằng mình không bỏ sót.


7. Common Transformers

7.1. Transformers là gì?

Nhắc lại từ mục 4.2: transformer là cách Kustomize thực hiện thay đổi tự động trên tất cả resources đã import. Thay vì mở từng file YAML và chỉnh tay, chỉ cần khai báo transformer trong kustomization.yaml — Kustomize apply nó vào lúc build, và file gốc vẫn nguyên vẹn.

💡 Hình dung: Transformer giống con dấu của phòng hành chính. Hồ sơ dày bao nhiêu trang cũng vậy, không ai ngồi viết tay dòng "đã duyệt" lên từng trang — chỉ cần một con dấu đóng qua cả tập. Và vì bản gốc trong tủ hồ sơ không bị đóng dấu, lần sau cần một con dấu khác thì vẫn lấy đúng bản gốc đó ra dùng.

7.2. Các loại Common Transformers

TransformerTác dụng
labelsThêm label chung vào mọi resource, tùy chọn ghi cả vào selector
namePrefixThêm prefix vào tên mọi resource
nameSuffixThêm suffix vào tên mọi resource
namespaceĐặt mọi resource vào một namespace
commonAnnotationsThêm annotation chung vào mọi resource

7.3. labels — và cái bẫy selector

Label là cặp key-value gắn vào metadata.labels của resource, dùng để nhóm và lọc. Nhưng trong Kubernetes, label còn một vai trò thứ hai nguy hiểm hơn nhiều: làm selector. Deployment dựa vào spec.selector.matchLabels để biết Pod nào là Pod của nó. Service dựa vào spec.selector để biết gửi traffic tới Pod nào. Nói cách khác, cùng một cặp key-value, chỗ này chỉ là ghi chú, chỗ kia là dây nối giữ hệ thống đứng vững.

Khai báo cơ bản:

labels:
- pairs:
department: engineering

Sau khi build, mọi Deployment và Service đều có department: engineering trong metadata.labels. Selector không bị đụng tới — đó là hành vi mặc định, tương đương includeSelectors: false.

Muốn label chui vào cả selector và cả pod template, phải bật cờ một cách có chủ đích:

labels:
- pairs:
app: nginx
includeSelectors: true
includeTemplates: true

Vì sao mặc định lại là false? Vì spec.selector của một Deployment là immutable — đã tạo rồi thì Kubernetes không cho sửa. Thêm một label vào selector của Deployment đang chạy nghĩa là lần apply tiếp theo sẽ bị từ chối, và cách duy nhất đi tiếp là xóa Deployment rồi tạo lại, tức là downtime.

Quy tắc thực dụng rút ra từ đó:

  • Label mang tính mô tả — department, team, owner, cost-center — cứ để mặc định, đừng động vào selector.
  • Label định danh ứng dụng — app — chỉ bật includeSelectors: true khi bạn đặt nó từ đầu, trên workload chưa từng deploy lần nào.

Lưu ý: Field cũ commonLabels vẫn chạy được trong kustomize.config.k8s.io/v1beta1 nhưng đã bị deprecate từ Kustomize v5.0.0 và sẽ không có mặt trong API v1. Nó tương đương labels kèm includeSelectors: true, nên gặp config cũ viết commonLabels thì hiểu ngay rằng selector đang bị ghi đè — và lệnh kustomize edit fix sẽ tự chuyển nó sang cú pháp mới.

7.4. namespace

Đặt mọi resource vào một namespace cụ thể:

namespace: debugging

Mọi resource, sau khi build, đều có metadata.namespace: debugging. Đây là transformer hữu dụng nhất khi cần dựng nguyên một bản sao của hệ thống sang namespace khác để thử nghiệm.

7.5. namePrefix và nameSuffix

namePrefix: KodeKloud-
nameSuffix: -web

Tên resource sẽ thành KodeKloud-api-deployment-web. Kustomize không chỉ đổi metadata.name mà còn cập nhật luôn các chỗ đang tham chiếu tới tên đó, nên Service vẫn trỏ đúng vào Deployment sau khi đổi tên.

Thường dùng trong subdirectory kustomization.yaml để phân biệt resources theo folder — api/ thêm -web, database/ thêm -storage.

7.6. commonAnnotations

commonAnnotations:
logging: verbose

Mọi resource sẽ có annotation logging: verbose trong metadata.

Ở đây có một điểm dễ nhầm. Sau khi biết commonLabels đã bị thay bằng labels, phản xạ tự nhiên là đổi luôn commonAnnotations thành annotations. Đừng. Kustomization API không có field tên annotations; commonAnnotations vẫn là cách viết hiện hành và không bị deprecate. Lý do rất đơn giản: annotation chưa bao giờ được dùng làm selector, nên nó không có cái bẫy immutable đã bàn ở mục 7.3, và vì thế cũng không cần một field mới để tách hành vi ra.

7.7. Phạm vi áp dụng — root vs subdirectory

Vị trí khai báoPhạm vi
Root kustomization.yamlÁp dụng cho tất cả resources (global)
Subdirectory kustomization.yamlChỉ áp dụng cho resources được import trong directory đó

Nếu đặt labels ở root, tất cả resources — kể cả trong subdirectories — đều được label. Nếu đặt trong api/kustomization.yaml, chỉ resources trong api/ được label. Đây cũng là lý do nên đẩy những gì mang tính toàn hệ thống (namespace, label tổ chức) lên root, và giữ lại ở subdirectory những gì chỉ đúng với riêng nhóm resource đó.


8. ConfigMap và Secret Generators

8.1. Vấn đề: sửa ConfigMap mà Pod không thèm restart

Tình huống rất quen: ứng dụng đọc LOG_LEVEL từ một ConfigMap. Cần bật debug, bạn sửa LOG_LEVEL: info thành LOG_LEVEL: debug, chạy kubectl apply, rồi ngồi đợi log debug hiện ra.

Nó không hiện ra.

ConfigMap trong cluster đúng là đã đổi. Nhưng Pod thì không. Deployment không có cách nào biết rằng nội dung một ConfigMap nó tham chiếu vừa thay đổi — spec của Deployment vẫn y nguyên từng ký tự, nên với Kubernetes thì chẳng có gì cần làm cả. Biến môi trường được nạp vào container lúc container khởi động, và container đó vẫn đang chạy với giá trị cũ.

Cách làm đúng theo khuyến nghị của Kubernetes là: tạo một ConfigMap tên mới, rồi sửa Deployment để trỏ sang tên mới đó. Chính việc sửa tên trong spec Deployment mới là thứ kích hoạt rolling update — Kubernetes thấy spec đổi, nên thay Pod cũ bằng Pod mới, và Pod mới đọc config mới.

Làm tay thì đúng nhưng cực kỳ phiền: mỗi lần đổi một dòng config là phải nghĩ ra tên mới, sửa ConfigMap, sửa Deployment, và nhớ dọn ConfigMap cũ. Generators sinh ra để tự động hóa đúng chuỗi thao tác đó.

8.2. configMapGenerator

Generator là loại khai báo bảo Kustomize tạo ra một resource hoàn toàn mới lúc build, thay vì đọc nó từ file có sẵn. Khác với transformer (sửa cái đã có), generator sinh cái chưa có.

configMapGenerator nhận dữ liệu từ ba nguồn:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- nginx-deployment.yaml

configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=info
- APP_MODE=production
  • literals — cặp key-value viết thẳng trong kustomization.yaml, ngăn cách bằng dấu =.
  • files — mỗi file thành một key, tên file là key, nội dung file là value.
  • envs — đọc một file .env, mỗi dòng KEY=value thành một entry riêng.

Ví dụ hai dạng còn lại:

configMapGenerator:
- name: nginx-conf
files:
- nginx.conf
- name: app-env
envs:
- app.env

8.3. Name suffix hash — và vì sao nó mới là điểm mấu chốt

Build thử xem Kustomize sinh ra cái gì:

kustomize build k8s

# Output minh họa
# apiVersion: v1
# data:
# APP_MODE: production
# LOG_LEVEL: info
# kind: ConfigMap
# metadata:
# name: app-config-7hd2t4k9gm

(Output minh họa — cấu trúc và tên field đúng theo lệnh thật, số liệu cụ thể chỉ mang tính ví dụ.)

Tên không phải app-config mà là app-config-7hd2t4k9gm. Cái đuôi đó là hash băm từ chính nội dung của ConfigMap.

Nhiều người gặp lần đầu sẽ thấy khó chịu và đi tìm cách tắt nó. Nhưng cái đuôi đó chính là toàn bộ lý do generator tồn tại. Đổi LOG_LEVEL=info thành LOG_LEVEL=debug, nội dung đổi, hash đổi, tên ConfigMap đổi theo — và Kustomize tự động sửa mọi chỗ đang tham chiếu tên đó, kể cả envFrom.configMapRef.name trong Deployment. Spec Deployment đổi, nên Kubernetes chạy rolling update. Đúng chuỗi thao tác thủ công ở mục 8.1, nhưng được làm hộ, và không bao giờ quên.

configMapGenerator: name suffix hashkustomization.yaml (v1)literals: LOG_LEVEL=infoname: app-configkustomization.yaml (v2)literals: LOG_LEVEL=debugname: app-configđổi 1 giá trịConfigMap generatedapp-config-7hd2t4k9gmConfigMap generatedapp-config-b56gm24f8cDeployment trỏ sang tên ConfigMap mới→ Pod rolling restart tự độngMuốn tên cố định: generatorOptions.disableNameSuffixHash: true.

8.4. secretGenerator

Cùng một cơ chế, áp dụng cho Secret:

secretGenerator:
- name: db-credentials
literals:
- DB_USER=admin
- DB_PASSWORD=s3cr3t
type: Opaque

Kustomize tự base64-encode giá trị khi sinh ra Secret, nên không cần chạy echo -n ... | base64 bằng tay nữa. secretGenerator cũng nhận filesenvs giống configMapGenerator, cộng thêm field type để khai báo loại Secret — ví dụ kubernetes.io/tls cho cặp cert và key.

Lưu ý: base64 là encoding, không phải mã hóa. Ai đọc được file kustomization.yaml là đọc được password. Đừng commit literals chứa secret thật lên Git — dùng files trỏ tới file nằm ngoài repo, hoặc một công cụ quản lý secret riêng.

8.5. generatorOptions và disableNameSuffixHash

Đôi khi tên có hash thật sự gây vướng: một ConfigMap được tham chiếu từ chỗ Kustomize không nhìn thấy (một CRD lạ, một script bên ngoài, một Ingress annotation viết tay). Lúc đó tắt hash bằng generatorOptions:

generatorOptions:
disableNameSuffixHash: true
labels:
generated-by: kustomize

Khai báo này áp cho mọi generator trong cùng file. Ngoài disableNameSuffixHash, generatorOptions còn nhận labels, annotations, và immutable để đánh dấu ConfigMap/Secret sinh ra là bất biến.

Cũng có thể tắt cho riêng một generator bằng field options của chính nó — nhưng nếu generatorOptions ở cấp file đã đặt disableNameSuffixHash: true thì khai báo cục bộ không lật ngược lại được:

configMapGenerator:
- name: static-config
literals:
- REGION=ap-southeast-1
options:
disableNameSuffixHash: true

💡 Hình dung: Cái hash giống số phiên bản in trên bìa một tập tài liệu nội bộ. Sửa một dòng bên trong là bìa đổi số, và ai đang cầm bản cũ buộc phải đi lấy bản mới — không ai vô tình làm việc với nội dung đã lỗi thời. Tắt disableNameSuffixHash nghĩa là bỏ số phiên bản đi: bìa lúc nào cũng như nhau, nên người cầm bản cũ chẳng có lý do gì phải đổi.

Vì vậy quy tắc là: mặc định cứ để hash bật. Chỉ tắt khi có một tham chiếu bên ngoài thật sự cần tên cố định — và khi đã tắt, nhớ rằng mình vừa tự nhận lại trách nhiệm restart Pod mỗi lần đổi config, bằng kubectl rollout restart deployment/<tên>.


Nguồn tham khảo

Nguồn gốc: Khóa "Certified Kubernetes Administrator (CKA)" — bài "Kustomize Basics" (vì sao cần Kustomize; base và overlays; cài đặt Kustomize; file kustomization.yaml; build và apply; quản lý project nhiều thư mục; common transformers), nền tảng KodeKloud. Giảng viên: Mumshad Mannambeth.

Repo ghi chú, link tài liệu, và đáp án các practice question của toàn bộ khóa học: kodekloudhub/certified-kubernetes-administrator-course.

Fact-check: