设计师周姐开了一家室内设计工作室。她画了一套“标准两居室”图纸:客厅、卧室、厨房、卫生间,每张图都画得一清二楚。开发商找她合作:同一个户型要卖给两个楼盘,东边楼盘厨房台面换大理石,西边楼盘门把手换成金色。周姐不想重画整本图纸,也不想在图纸上写满手写批注——她只在电脑里维护一份“改动说明”,出图时把原图与改动说明叠在一起,自动生成最终版图集。这套思路,正是 Kubernetes 配置工具 Kustomize 的核心理念:一套通用的基础资源,加上按环境叠层的差异记录,合成真正生效的最终配置。本文带你建立这套“图层思维”,学会管理多环境的集群配置。
学 Kustomize 是云原生工程师的基础能力。它让你告别复制粘贴 YAML 的噩梦,用声明式图层管理多环境配置,也是 ArgoCD 等 GitOps 工具链的核心一环,掌握它等于拿到了现代配置交付的入场券。
目录
可视化演示
展示Kustomize的Base/Overlay分层覆盖架构、Patches补丁机制与Helm对比,直观理解K8s声明式配置管理的核心原理。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Base与Overlay分层覆盖模式 Base目录存放通用资源定义,所有环境共享的基础配置 每个环境(dev/staging/prod)有独立overlay目录覆盖差异配置 最终配置 = Base + Overlay(dev/staging/prod) 声明式无模板引擎
Kustomize 引入
周姐的工作台上贴着三张便利贴:一张是“户型图集”,一张是“楼盘改动说明”,还有一张是“出图流程”。她不会一张张手绘图纸,而是把原图扫描进电脑,在改动说明里记差异,最后按下按钮,电脑自动合成新版图纸。Kustomize 做的事情,就是 Kubernetes 世界里的这台“合成打印机”。
在 K8s 集群中,每个服务都要写一堆 YAML 描述文件。开发环境、测试环境、生产环境的差异又让这些文件翻倍:单是 nginx 部署,你可能就要维护三份几乎相同的 deployment 文件。Kustomize 通过一个特殊的目录结构来解决这个问题,入口是每个目录中的 kustomization.yaml。
kustomization.yaml 是声明式配置的核心:它用“声明”代替“脚本指令”,明确列出要用哪些资源、要加哪些补丁。Kustomize 没有模板引擎——不会用 {{变量}} 语法做渲染,而是直接组合 YAML。你可以在 kustomization.yaml 中声明 resources、patches、transformers 等字段,然后用 kubectl apply -k 或 kustomize build 得到最终结果。
所以,只要把“图集”和“改动说明”整理好,周姐的“出图流水线”就永远稳定:同一套输入,永远生成同一套输出。这正是 Kustomize 被 GitOps 偏爱的原因——可预测、可审计、可重复。
- 输入目录 D目录中包含 kustomization.yaml 与各种 YAML 资源文件,这是构建的最小工作单元。
- 构建动作 BuildKustomize 按 kustomization.yaml 的声明加载 base 资源并应用 overlay 定制项,就像打印机把图集和改动说明叠印在一起。
- 最终配置 R_final构建输出是完整、可直接 apply 的资源清单,相当于交付给施工队的最终图纸。
Base 基础层
标准两居室户型图集是周姐的看家底本。无论哪个楼盘项目,她都先拿出这本图集:客厅和卧室的尺寸、厨房与卫生间的位置、水电管线的走向——一切都已定稿。图集本身不关心楼盘在哪个城市,也不关心买家预算,它就是“标准答案”。
在 Kustomize 中,base 目录就是这套“标准户型图集”。它是一组与环境无关的 K8s 资源文件,比如 Deployment、Service、ConfigMap。base 中定义了服务的通用镜像、端口、挂载点等,这些内容在 dev、staging、prod 之间保持一致。
base 目录也需要有自己的 kustomization.yaml,在其中用 resources 字段列出所有资源文件。这里的资源集合应尽量避免环境差异——不要出现特定环境的域名、实例数量或专属标签。这类差异应当放到 overlay 层去处理,让 base 保持“干净通用”。
一套底图,十个楼盘都能用。base 的质量直接决定后续所有 overlay 的体验:底图越干净,改动说明就越简单。
- 资源集合 B用花括号表示集合,base 是多个资源组合而成的整体。
- 单个资源 r_i其中每个 r_i 都是完整的 K8s 清单文件,例如 Deployment、Service,是“户型图集”中的一页图纸。
- 环境无关性约束base 中不出现环境专属配置,从而保证它能被所有 overlay 共用。
Overlay 覆盖层
开发商拿来周姐的底图,提出了新要求:“东边楼盘厨房台面换大理石,西边楼盘门把手换金色”。周姐不会去复印店把整本图纸重画,而是在电脑里建一个“楼盘专属改动层”,每次出图时把底图和改动层叠在一起。
overlay 目录是 base 上叠加的“差异层”。它有自己的 kustomization.yaml,里面用 resources 引用 base 目录的位置,再用 patches 或 transformers 描述要改什么。构建时,Kustomize 会先读取 base 的资源,再依次应用 overlay 上的定制内容。
多环境实践通常把 overlay 按环境组织:overlays/dev、overlays/prod。每个 overlay 都引用同一个 base,环境之间只差差异本身。overlay 还可以继续叠加,构建时按声明顺序进行资源合并。
底图不用动,overlay 画多少就改多少。周姐只需要给每个小区单独准备一张“透明贴纸”,贴纸撕掉,底图还是那张干净的底图。
- 基础资源集合 B这是上节定义过的 base 资源集合,相当于“底图”。
- 覆盖层信息 O代表 overlay 目录中声明的资源与补丁列表,相当于“改动通知单”。
- 渲染合并动作Kustomize 将 B 与 O 按规则合并——标量覆盖、字段保留、列表匹配,最终输出合成后的配置。
Patches 定制
如果开发商说“东边楼盘所有 132 户型的主卧飘窗要加宽 30 厘米”,周姐不需要去重画那页图纸——她只需要在改动说明里记录:定位到主卧飘窗,宽度 +0.3 米。这就是一个“补丁”:只改局部,不碰整体。
Kustomize 的 patches 字段就是干这个的。它提供了一种精确修改资源的方式,不用在 overlay 里重写整个 YAML 文件,而是通过 target 选择目标资源,再用补丁内容指定修改细节。
Patches 的常用类型有两种。第一种是 strategic merge patch:patch 中给出部分字段,Kustomize 按路径与 base 资源合并;字段标量被覆盖、新字段被添加、列表按 name 合并。第二种是 JSON patch:以 JSON 操作指令数组(如 add/replace/remove)精确定位路径,适合那些 strategic merge 无法表达的情形。kustomization.yaml 中可以使用 patches 字段配合 target 进行定位。
补丁是一种优雅的“微创手术”——不重建,只缝合。
- 目标选择 t_jtarget 通过 group/kind/name 等标识来选择要修改的资源。
- 补丁内容 d_j包含增量修改内容,只描述变化部分,不描述完整资源。
- 按序应用多个补丁按声明顺序执行,后一个补丁可以修改前一个的结果。
Transformers 变换
周姐同时接了好几个楼盘的订单,每个楼盘要发的风格不同:一号楼全部统一装金色门把手,二号楼全部换灰色踢脚线。“给所有房间统一换一种东西”的需求,如果逐页标注就太蠢了。
Kustomize 的 transformers 就是这种“全局微整形”。它在构建时对所有资源统一施加变换,不需要每个文件单独写补丁。常见变换有:namePrefix 给所有资源统一加前缀,commonLabels 加上公共标签,images 更新镜像,namespace 统一设置命名空间。
transformers 的威力在于“批量”与“一致”。例如 namePrefix 在多个环境有同名资源时(如 prod 和 dev 都叫 nginx)可以避免冲突;commonLabels 让跨环境筛选资源变得容易。变换是有顺序的,组合顺序决定最终结果。
给所有门装统一拉手,不需要一张张改图纸——写一次变换规则就够了。
- 原始配置集 R变换前的资源集合。
- 变换函数 τ每个 τ 代表一个 transformer,如 namePrefix、commonLabels、images 或 namespace。
- 复合运算(∘)函数组合,先执行 τ₁,再执行 τ₂……最后得到全量变换后的资源集合。
Helm 对比
还有一种常见的装修方式:买一套“预制家具包”,内含图纸、零件、安装说明书,按照说明书填写一些选项(沙发颜色、桌子尺寸)就能组装出成品。这是 Helm 的方式。周姐的“底图+改动说明”则是另一条路。
Helm 用模板 + Values 渲染 Chart,Kustomize 用 base + overlay 合成配置。两者目标相同——管理多变的 K8s 配置,但理念差异很大:Helm 模板引擎可以在 YAML 里写循环和条件;Kustomize 完全拒绝模板语法,保持 YAML 的原貌。
适用场景上,Helm 适合把整个应用打包交付给陌生用户(比如开源 redis chart),安装简单、版本明确。Kustomize 适合在已有的内部配置上进行增量定制,特别是“以我的 base 为核心”的场景。复杂度取舍上,Helm 学习成本高一些,Kustomize 更直观但实现不了复杂的编排逻辑。两者还可以组合:Helm 渲染出初始 YAML,再用 Kustomize overlay 做二次覆盖。
预制家具和现场定制各有拥趸;但真正聪明的甲方,往往会先买预制家具,再请周姐上门做局部改动。
- Helm 侧 Templ(V,T)T 是模板文件集合,V 是用户传入的 Values 参数;经过模板渲染得到资源。
- Kustomize 侧 Build(B,O)B 是 base 资源集合,O 是 overlay 定制信息;经过构建合成得到资源。
- 殊途同归两者最终都输出标准 YAML,可交给 kubectl apply 应用。
GitOps 工作流
周姐从土建开始便和所有开发商约定:所有设计图必须存进公司文档库,版本要可追溯,禁止在施工现场改图。开工时,施工队直接从文档库拉最新图纸施工。这就是装修界的“GitOps”。
GitOps 是一种以 Git 为唯一事实来源的部署方式。Kustomize 的配置(base + overlay)整体提交到 Git,CI/CD 或 ArgoCD 在部署时从 Git 拉取目录并执行 kustomize build,然后 apply 到集群。配置文件在 Git 中经历评审、合并、审计,这让多环境之间的差异清晰可见。
ArgoCD 内置 Kustomize 支持,可直接将 Application 的 path 指向 overlay 目录。Git 中的 overlay 更新后,ArgoCD 自动或手动同步,将构建结果应用到目标集群。因为 base 和 overlay 分开维护,环境之间的唯一差异点就是 overlay,这大大降低了环境配置漂移的风险。
周姐的文档库解放了所有项目:改动有人审批,出图自动完成,施工队永远照着最新版本做——而 GitOps 就把这种安全感带给了你的集群。
- Git 中的目标状态构建后的最终配置以 git commit 形式存在,代表“应该是什么样”。
- 同步动作 applyGitOps 工具从 Git 拉取变更并 apply 到集群,是“把图纸照进现实”。
- 状态收敛集群发生漂移后,GitOps 工具会检测偏差并自动纠正回目标状态。
AI 应用场景
多环境的模型推理服务部署
阶段:模型服务部署(MLOps)
解决的问题:某个 AI 推理服务需要部署到 dev、staging、prod 三个集群,环境间只差副本数、资源配额和镜像 tag。传统手工维护三份 YAML,改一处经常漏一处,导致环境漂移。Kustomize 用 base 定义通用部分,用三个 overlay 描述环境差异,一处修改全部生效。
具体案例:算法团队交付大模型推理服务(如 vLLM),base 定义 Deployment、Service、HPA,overlays/dev 只设 2 副本,overlays/prod 设置 10 副本并增加 GPU 配额;发布时执行 kubectl apply -k overlays/prod,配置差异一目了然。
ArgoCD 多集群同步中的 Kustomize
阶段:持续交付(GitOps)
解决的问题:多集群环境配置漂移、发布过程不可审计。把 Kustomize 的 base/overlay 源码提交进 Git,ArgoCD 监听仓库路径,overlay 更新后自动同步到目标集群。
具体案例:公司有北京、上海两个 K8s 集群,每套集群跑同一套 AI 平台组件。Git 仓库中 overlays/beijing 与 overlays/shanghai 各自定义资源限额,ArgoCD 分别监听两个路径,任何人提交修改便触发对应集群的自动同步与回滚。
第三方 Helm Chart 的二次定制
阶段:基础设施引入与治理
解决的问题:开源 Chart(如 ingress-nginx)默认配置无法直接满足内部多租户隔离要求,直接改 Chart 又难以跟随上游更新。用 Kustomize overlay 在 Chart 渲染结果上做增量定制,既保留上游兼容性,又满足内部规范。
具体案例:平台团队引入 ingress-nginx 后,用 Kustomize 给所有资源打上 team=platform 标签,并统一设置 namespace 与资源配额,生成的配置通过 GitOps 流程下发到集群。
Python 演示
import copyimport json def merge_dict(base, overlay): """递归合并字典:标量值以 overlay 为准""" result = copy.deepcopy(base) for key, value in overlay.items(): if key not in result: result[key] = copy.deepcopy(value) elif isinstance(result[key], dict) and isinstance(value, dict): result[key] = merge_dict(result[key], value) elif isinstance(result[key], list) and isinstance(value, list): result[key] = merge_list(result[key], value) else: result[key] = copy.deepcopy(value) return result def merge_list(base_list, overlay_list): """列表合并:按 name 键定位已有项,找不到则追加""" result = copy.deepcopy(base_list) for item in overlay_list: if isinstance(item, dict) and "name" in item: for i, exist in enumerate(result): if isinstance(exist, dict) and exist.get("name") == item["name"]: result[i] = merge_dict(exist, item) break else: result.append(copy.deepcopy(item)) else: result.append(copy.deepcopy(item)) return result BASE = { "apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "nginx", "labels": {"app": "nginx"}}, "spec": { "replicas": 3, "template": { "spec": { "containers": [{"name": "nginx", "image": "nginx:1.25"}] } } },} OVERLAY = { "metadata": {"labels": {"env": "prod"}}, "spec": { "replicas": 5, "template": { "spec": { "containers": [{"name": "nginx", "image": "nginx:1.26"}] } } },} if __name__ == "__main__": result = merge_dict(BASE, OVERLAY) print(json.dumps(result, indent=2, ensure_ascii=False))
result = copy.deepcopy(base)深拷贝 base,构建过程不修改原配置。elif isinstance(result[key], dict) and isinstance(value, dict):两个字典相遇时递归合并,这是 Kustomize 合并的基础。elif isinstance(result[key], list) and isinstance(value, list):两个列表相遇时走 merge_list,按 name 字段定位。result[i] = merge_dict(exist, item)找到同名列表项后递归合并,类似 strategic merge patch。else: result.append(copy.deepcopy(item))for-else 结构,未匹配到 name 时才追加新项。print(json.dumps(result, indent=2, ensure_ascii=False))输出合并结果,labels被叠加、replicas变成 5、镜像更新为 1.26。
常见误区
误区我听说 Kustomize 和 Helm 一样,可以把 YAML 做成模板,用变量渲染出不同配置。所以它应该有类似 `{{ .Values.replicas }}` 的语法。
正解Kustomize 根本没有模板语法,也不执行任何逻辑运算。它先将 base 和 overlay 的资源加载进来,再做合并与变换后输出。Kustomize 更接近“两张图叠印”,而不是“模具灌浆”。参数注入、循环、条件判断都不在 Kustomize 的范围内。
为何常见:因为两者都解决“多环境配置”问题,又都生成最终 YAML,初学者容易把渲染与合并混为一谈。
误区overlay 里写了 Deployment,就能把 base 里的 Deployment 整个覆盖掉,所以我只需要复制一份再改即可。
正解Kustomize 做的是资源合并,而不是替换。若 base 和 overlay 中有同 kind + name 的资源,构建时按“深合并”规则合成一个资源:标量取 overlay、新字段补全、列表按 name 合并。只有刻意使用 replace 类操作时才会整体覆盖。
为何常见:“overlay/覆盖”的命名让人直接联想到“替换”。
误区任何数组都能被 strategic merge 逐项合并,所以 patch 里写一小段就能改大数组。
正解strategic merge 只能对 K8s 资源中有特殊标记的列表做合并,通常要求列表项带 name 字段;否则列表会被整体替换。要更精细地控制路径,应该用 JSON patch 的 add/replace/remove 指令。
为何常见:“merge” 字样以及 K8s 中常见的 patch 场景,让用户以为它能合并一切列表。
误区既然 Kustomize 能做多环境配置,Helm 就没用了;或者反过来,用了 Helm 就不需要 Kustomize。
正解两者解决的问题不同:Helm 以 Chart 为单元打包和发布应用,Kustomize 以 base+overlay 为方法修改现有清单。常见做法是先用 Helm 渲染,再用 Kustomize 覆盖,各取所长。
为何常见:网上大量对比文章给人“竞争关系”的错觉。
误区运行一次 kustomize build,namePrefix 和 commonLabels 就会把我的 base YAML 文件改掉。
正解Kustomize 的构建过程不改动任何源文件,也不写回磁盘。它把内容全部在内存中完成变换,并将结果输出到标准输出。源仓库保持干净、可复用。
为何常见:因为 kustomize build 的输出看起来像是把改动“写进”了资源,容易误以为已经落盘。
面试题
Q1. 在 kubectl apply -k ./overlays/prod 中,-k 参数代表什么?
- A.将 kubeconfig 作为输入
- B.按 Kustomize 目录构建并应用← 正确答案
- C.仅保留关键命名空间
- D.使用 kubectl 内置加密
答案:B。-k 告诉 kubectl 用 Kustomize 构建该目录——它会读取 kustomization.yaml 并渲染出最终配置后再 apply;而 -f 直接使用原文件,不会执行 overlay 合并。
Q2. base 目录设计的核心原则是什么?
- A.只包含环境独立的通用资源← 正确答案
- B.必须写死所有环境差异
- C.只能存放 ConfigMap
- D.禁止创建自己的 kustomization.yaml
答案:A。base 是“标准户型图”,只放与环境无关的通用资源;环境差异全部交给 overlay 处理。如果把环境差异写进 base,就失去了共享复用的意义。
Q3. 以下哪个 transformer 可以为所有资源增加名称前缀?
- A.images
- B.namePrefix← 正确答案
- C.namespace
- D.commonAnnotations
答案:B。namePrefix 会统一给资源的 metadata.name 加上前缀,常用于隔离不同环境或租户的同名资源。images 只改镜像,namespace 只设置命名空间,commonAnnotations 只加注解。
Q4. strategic merge patch 与 JSON patch 的主要区别是什么?
- A.JSON patch 以操作指令数组寻址,strategic merge 按字段深度合并← 正确答案
- B.strategic merge 无法修改任何列表
- C.JSON patch 不需要提供 target
- D.两者的语法完全等价
答案:A。JSON patch 是 add/replace/remove 指令数组,需要精确路径;strategic merge 给出局部对象,Kustomize 按 merge key 与新对象做递归合并。两者语法完全不同,适合不同场景。
Q5. 下列哪些属于 Kustomize 的 transformers?
- A.commonLabels← 正确答案
- B.namePrefix← 正确答案
- C.images← 正确答案
- D.values
答案:ABC。commonLabels、namePrefix、images 都是常见的 Kustomize 变换器,它们会全局修改资源。values 是 Helm 的概念,不属于 Kustomize。
Q6. 当 base 和 overlay 定义了同一个资源时,kustomize build 的结果是什么?
- A.保留两个资源导致冲突
- B.深合并成为一个资源← 正确答案
- C.仅保留 overlay 版本
- D.报错拒绝构建
答案:B。Kustomize 会按 merge 规则将同名资源合并成一个:标量以 overlay 为准,新增字段被补全,列表按 name 匹配合并。既不是替换,也不是报错。
Q7. 关于 strategic merge 对列表的合并,以下说法正确的是?
- A.列表总是被整体替换
- B.带 name 字段的列表项按 name 合并,否则整体替换← 正确答案
- C.所有列表都强制拼接
- D.只有 map 才能被合并
答案:B。strategic merge 的列表合并依赖 merge key(通常是 name)。列表项有 name 时可以定位并合并;没有 name 时无法匹配,默认整体替换。这是 patch 新手常踩的坑。
Q8. 在 GitOps 多环境仓库中,以下哪种做法最能避免配置漂移?
- A.每个环境各存一份完整 YAML
- B.所有环境共用 base,差异只写在 overlay 中← 正确答案
- C.手动同步各环境目录
- D.直接在集群上修改资源
答案:B。共用 base + overlay 才能让环境间差异显式化。每个环境存独立 YAML 会随着迭代产生大量复制粘贴差异;手动同步和在线修改更无法审计,违背 GitOps 的声明式原则。
Q9. 相比 Helm,Kustomize 的优势有哪些?
- A.使用纯 YAML,无需学习模板语法← 正确答案
- B.原生支持 kubectl apply -k← 正确答案
- C.提供版本化 release 管理
- D.必须配套独立服务端才能运行
答案:AB。Kustomize 没有模板语法,保持 YAML 原貌;它内嵌于 kubectl,无需额外服务。C 是 Helm 的能力,D 描述错误——Kustomize 不需要独立服务端。
Q10. 关于 ArgoCD 使用 Kustomize 管理多环境配置,以下说法正确的有?
- A.可将 path 指向含 overlay 的目录,自动完成构建← 正确答案
- B.可使用 kustomize.buildOptions 附加额外变换配置← 正确答案
- C.必须提前提交构建后的 YAML 才能部署
- D.必须单独部署 Kustomize 服务端
答案:AB。ArgoCD 内置 Kustomize,可以直接从 Git 源码构建,无需提前提交渲染结果,也无需独立部署 Kustomize 服务。buildOptions 可传入额外参数,实现更灵活的构建过程。