早期一直在使用helm或者kubectl原生yaml结合CI和ArgoCD部署应用,最近在给一个金融大客户部署私有化服务的时候,发现客户的环境比较复杂,涉及多个国家多套环境,比如Test、UAT、Prod,差不多有三十个环境,每次修改的values和yaml文件快吐了,这次打算用Kustomize来改造现有的部署应用试试。
Kustomize介绍
什么是Kustomize
Kustomize是Kubernetes原生的配置管理工具,通过模板化的方式管理YAML资源:
核心特性:
- 无模板语法,纯YAML
- 声明式配置管理
- 支持多环境管理
- GitOps友好
- 已集成kubectl 1.14+
与Helm对比:
| 特性 |
Kustomize |
Helm |
| 模板语法 |
无(纯YAML) |
Go Template |
| 学习曲线 |
低 |
中高 |
| 多环境管理 |
Base + Overlay |
Values文件 |
| 包管理 |
无 |
Chart Repository |
| 版本管理 |
Git |
Chart版本 |
| 适合场景 |
应用开发者 |
应用分发者 |
基本概念
kustomization.yaml
├── bases/ # 基础配置(复用)
├── overlays/ # 环境覆盖
│ ├── dev/
│ ├── staging/
│ └── prod/
├── patches/ # 配置补丁
└── resources/ # 静态资源
Kustomize与Helm区别
设计理念
Kustomize设计理念:
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
|
# 声明式配置管理
# 不使用模板,而是通过叠加(Overlay)方式修改配置
# Base: 基础配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
template:
spec:
containers:
- name: app
image: my-app:latest
# Overlay: 环境差异
# overlays/production/kustomization.yaml
bases:
- ../../base
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 10
|
Helm设计理念:
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
|
# 模板化配置管理
# 使用Go Template模板
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
# values.yaml (不同环境不同values)
replicaCount: 1
image:
repository: my-app
tag: latest
# values-production.yaml
replicaCount: 10
image:
repository: my-app
tag: v1.2.3
|
功能对比
| 功能 |
Kustomize |
Helm |
| 模板 |
无模板,纯YAML |
Go Template |
| 学习曲线 |
低,只需会YAML |
中高,需学模板语法 |
| 复用性 |
Base + Overlay |
Chart + Values |
| 调试 |
kubectl kustomize |
helm template –dry-run |
| 版本管理 |
Git版本控制 |
Chart版本号 |
| 环境管理 |
Overlay目录 |
多个Values文件 |
| 包分发 |
无(用Git) |
Chart仓库 |
| CI/CD集成 |
原生kubectl |
需安装helm |
| Rollback |
kubectl rollout |
helm rollback |
| 依赖管理 |
无直接支持 |
Chart dependencies |
适用场景
选择Kustomize的场景:
- 应用开发者管理自己的K8s资源
- 需要GitOps工作流
- 多环境配置差异大
- 不想学习模板语言
- 团队熟悉YAML和kubectl
选择Helm的场景:
- 应用分发者(软件供应商)
- 需要包管理和版本控制
- 配置相对固定
- 复杂的应用依赖关系
- 快速部署第三方应用
Kustomize部署K8s应用
项目结构
my-app/
├── base/ # 基础配置
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ ├── serviceaccount.yaml
│ └── kustomization.yaml
├── overlays/ # 环境配置
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ ├── deployment-replica.yaml
│ │ ├── deployment-resources.yaml
│ │ └── configmap-env.yaml
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── deployment-replica.yaml
│ └── production/
│ ├── kustomization.yaml
│ ├── deployment-replica.yaml
│ ├── deployment-resources.yaml
│ └── configmap-prod.yaml
└── scripts/
└── deploy.sh
Base配置创建
base/deployment.yaml
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
version: v1.0.0
spec:
replicas: 1
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: my-app
version: v1.0.0
spec:
serviceAccountName: my-app
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: my-app
image: my-app:latest
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: JAVA_OPTS
value: "-Xms512m -Xmx512m"
- name: SPRING_PROFILES_ACTIVE
value: "prod"
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: config
mountPath: /config
- name: logs
mountPath: /logs
volumes:
- name: config
configMap:
name: my-app-config
- name: logs
emptyDir: {}
|
base/service.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: my-app
|
base/configmap.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
data:
application.yml: |
server:
port: 8080
spring:
application:
name: my-app
logging:
level:
root: INFO
|
base/serviceaccount.yaml
1
2
3
4
|
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
|
base/kustomization.yaml
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
|
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 引用的资源文件
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
- serviceaccount.yaml
# 通用标签(所有资源添加)
commonLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: my-app
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/version: v1.0.0
# 通用注解
commonAnnotations:
createdBy: kustomize
env: base
# 名称前缀
# namePrefix: dev-
# 名称后缀
# nameSuffix: -v1
|
Overlay环境配置
overlays/production/kustomization.yaml
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
|
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 引用base
namespace: production
bases:
- ../../base
# 副本数覆盖
replicas:
- name: my-app
count: 10
# 镜像覆盖
images:
- name: my-app
newName: harbor.example.com/prod/my-app
newTag: v1.2.3
# ConfigMap生成器
configMapGenerator:
- name: my-app-config
behavior: replace
files:
- application.yml=application-production.yml
# Secret生成器
secretGenerator:
- name: my-app-secret
behavior: create
envs:
- .env.production
# 资源补丁
patchesStrategicMerge:
- deployment-resources.yaml
- deployment-probes.yaml
- pvc.yaml
# 命名空间
namespace: production
# 通用标签覆盖
commonLabels:
environment: production
tier: backend
# 通用注解覆盖
commonAnnotations:
env: production
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
|
overlays/production/deployment-resources.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
|
overlays/production/pvc.yaml
1
2
3
4
5
6
7
8
9
10
11
|
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-logs
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
|
overlays/production/application-production.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
server:
port: 8080
spring:
application:
name: my-app
datasource:
url: jdbc:mysql://mysql-prod.svc:3306/mydb
username: prod_user
password: ${DB_PASSWORD}
logging:
level:
root: INFO
com.example: DEBUG
file:
path: /logs
|
overlays/production/.env.production
1
2
3
|
DB_PASSWORD=prod_secure_password_123
API_KEY=prod_api_key_xyz
REDIS_PASSWORD=prod_redis_pass
|
部署命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 构建配置(不应用,查看最终YAML)
kubectl kustomize overlays/production
# 保存渲染结果
kubectl kustomize overlays/production > rendered-prod.yaml
# 查看差异
kubectl diff -k overlays/production
# 直接应用
kubectl apply -k overlays/production
# 删除资源
kubectl delete -k overlays/production
# 查看资源
kubectl get all -l app.kubernetes.io/name=my-app -n production
|
统一部署管理配置
1. Namespace管理
方式一:在Overlay中指定
1
2
|
# overlays/production/kustomization.yaml
namespace: production
|
方式二:作为资源管理
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# overlays/production/kustomization.yaml
resources:
- ../../base
- namespace.yaml
# overlays/production/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
name: production
|
2. Secret管理
方案一:环境变量文件
1
2
3
4
5
6
|
# overlays/production/kustomization.yaml
secretGenerator:
- name: my-app-secret
type: Opaque
envs:
- .env.production
|
方案二:字面值
1
2
3
4
5
6
|
secretGenerator:
- name: my-app-secret
type: Opaque
literals:
- DB_PASSWORD=prod123
- API_KEY=xyz789
|
方案三:命令生成
1
2
3
4
5
6
|
secretGenerator:
- name: tls-cert
type: kubernetes.io/tls
files:
- tls.crt=tls.crt
- tls.key=tls.key
|
方案四:从现有Secret引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# overlays/production/kustomization.yaml
generatorOptions:
disableNameSuffixHash: true
resources:
- secret.yaml
# overlays/production/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: my-app-secret
type: Opaque
stringData:
DB_PASSWORD: prod123
|
方案五:Sealed Secrets(推荐生产使用)
1
2
3
4
5
6
|
# 本地生成sealed secret
kubeseal -f secret.yaml -w sealed-secret.yaml
# 在kustomization中引用
resources:
- sealed-secret.yaml
|
3. PVC管理
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
|
# overlays/production/kustomization.yaml
resources:
- ../../base
- pvc-logs.yaml
- pvc-data.yaml
# overlays/production/pvc-logs.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-logs
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd # 使用SSD存储类
resources:
requests:
storage: 50Gi
# overlays/production/pvc-data.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-data
spec:
accessModes:
- ReadWriteMany # 多节点共享
storageClassName: nfs-storage
resources:
requests:
storage: 100Gi
|
4. 镜像版本管理
方式一:直接指定
1
2
3
4
|
images:
- name: my-app
newName: harbor.example.com/prod/my-app
newTag: v1.2.3
|
方式二:使用SHA256
1
2
3
4
|
images:
- name: my-app
newName: harbor.example.com/prod/my-app
digest: sha256:abc123def456... # 最安全的方式
|
方式三:多个镜像
1
2
3
4
5
6
7
8
9
10
|
images:
- name: nginx
newName: harbor.example.com/library/nginx
newTag: 1.21-alpine
- name: redis
newName: harbor.example.com/library/redis
newTag: 6.2-alpine
- name: my-app
newName: harbor.example.com/prod/my-app
newTag: v1.2.3
|
方式四:CI/CD动态更新
1
2
3
4
5
|
# 在CI Pipeline中
export IMAGE_TAG=$(git rev-parse --short HEAD)
cd overlays/production
kustomize edit set image my-app=harbor.example.com/prod/my-app:$IMAGE_TAG
kubectl apply -k .
|
5. 节点亲和性配置
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
|
# overlays/production/deployment-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
affinity:
# 节点亲和性
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- c5.2xlarge
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-west-1a
# Pod亲和性
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
|
在kustomization中引用:
1
2
|
patchesStrategicMerge:
- deployment-affinity.yaml
|
6. 污点和容忍配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# overlays/production/deployment-taints.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
# 容忍专用节点污点
tolerations:
- key: workload
operator: Equal
value: dedicated
effect: NoSchedule
- key: node.kubernetes.io/not-ready
operator: Exists
effect: NoExecute
tolerationSeconds: 300
# 容许GPU节点
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
|
7. 副本数量管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 方式一:replicas字段(推荐)
replicas:
- name: my-app
count: 10
- name: my-app-sidecar
count: 2
# 方式二:Patch方式
# overlays/production/deployment-replica.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 10
|
8. ConfigMap管理
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
|
# 方式一:configMapGenerator(推荐)
configMapGenerator:
# 从文件生成
- name: my-app-config
behavior: replace
files:
- application.yml=application-production.yml
- logback.xml=logback-production.xml
# 从字面值生成
- name: my-app-env
behavior: create
literals:
- SPRING_PROFILES_ACTIVE=production
- LOG_LEVEL=INFO
- JAVA_OPTS=-Xms2g -Xmx4g
# 从env文件生成
- name: my-app-file-env
behavior: create
envs:
- .env.production
# 方式二:直接引用资源
resources:
- configmap.yaml
|
完整生产环境示例
overlays/production/kustomization.yaml
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
59
60
61
62
63
64
65
66
67
|
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 命名空间
namespace: production
# 引用base
bases:
- ../../base
# 额外资源
resources:
- namespace.yaml
- pvc-logs.yaml
- pvc-data.yaml
- hpa.yaml
- service-monitor.yaml
- network-policy.yaml
# 副本数
replicas:
- name: my-app
count: 10
# 镜像版本
images:
- name: my-app
newName: harbor.example.com/prod/my-app
newTag: v1.2.3
# ConfigMap
configMapGenerator:
- name: my-app-config
behavior: replace
files:
- application.yml=config/application-production.yml
- name: my-app-env
behavior: merge
literals:
- ENV=production
- LOG_LEVEL=INFO
# Secret
secretGenerator:
- name: my-app-secret
behavior: create
envs:
- .env.production
# Patch
patchesStrategicMerge:
- deployment-resources.yaml
- deployment-affinity.yaml
- deployment-taints.yaml
- deployment-probes.yaml
# 通用标签
commonLabels:
environment: production
tier: backend
team: platform
# 通用注解
commonAnnotations:
env: production
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
|
CI/CD集成
GitLab CI示例
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
|
# .gitlab-ci.yml
stages:
- build
- deploy
variables:
REGISTRY: harbor.example.com
APP_NAME: my-app
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $REGISTRY/$APP_NAME:$CI_COMMIT_SHA .
- docker push $REGISTRY/$APP_NAME:$CI_COMMIT_SHA
only:
- main
- develop
deploy:dev:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: dev
script:
- cd overlays/dev
- kustomize edit set image $REGISTRY/$APP_NAME=$REGISTRY/$APP_NAME:$CI_COMMIT_SHA
- kubectl apply -k .
- kubectl rollout status deployment/my-app -n dev
only:
- develop
deploy:prod:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: production
script:
- cd overlays/production
- kustomize edit set image $REGISTRY/$APP_NAME=$REGISTRY/$APP_NAME:$CI_COMMIT_SHA
- kubectl apply -k .
- kubectl rollout status deployment/my-app -n production
when: manual
only:
- main
|
ArgoCD集成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# argocd/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-production
namespace: argocd
spec:
project: production
source:
repoURL: https://github.com/example/k8s-manifests.git
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
|
总结
Kustomize最佳实践
-
目录结构规范
- base存放通用配置
- overlays管理环境差异
- 清晰的命名约定
-
版本管理
- 使用Git管理所有配置
- 镜像使用SHA256或明确的tag
- 不要使用latest
-
Secret管理
- 生产环境使用Sealed Secrets
- 不要将敏感信息提交到Git
- 使用External Secrets Operator集成Vault
-
组件复用
- 抽取通用配置为组件
- 建立Kustomize组件库
- 避免重复配置
-
CI/CD集成
- 自动化镜像tag更新
- 使用kubectl diff预览变更
- 配置验证通过后再应用
Kustomize vs Helm选择
| 场景 |
推荐方案 |
原因 |
| 微服务配置管理 |
Kustomize |
灵活性高,Git友好 |
| 第三方应用部署 |
Helm |
生态丰富,一键部署 |
| 复杂模板需求 |
Helm |
Go Template功能强大 |
| 简单配置 |
Kustomize |
无需学习模板 |
| GitOps |
Kustomize |
天然支持 |
学习路径
- 基础语法:base + overlay结构
- Patch策略:StrategicMerge vs JSON6902
- 组件复用:提取通用配置
- CI/CD集成:自动化部署流程
- GitOps实践:与ArgoCD/Flux结合
Kustomize是云原生时代配置管理的理想工具,掌握它能显著提升Kubernetes应用管理效率,在实际部署场景下,对于新项目不同环境差异化很小,可以结合VibeCoding工具生成一键部署脚本,快速交付新环境。