8235 字
41 分钟
第 14 篇:Helm 与 Kustomize 多环境治理

第 14 篇:Helm 与 Kustomize 多环境治理#

0. 本篇定位#

这是容器技术路线的第 14 篇。上一篇我们已经把后端服务放进 Kubernetes,并能用 Deployment、Service、ConfigMap、Secret、Ingress 等对象描述一个可运行的部署模板。本篇继续往前走一步:当同一个服务要部署到 dev、test、staging、prod 多个环境时,如何避免 YAML 复制粘贴,如何让差异可审查,如何让发布结果可回滚。

本文重点不是背 helm installkubectl kustomize 的命令参数,而是建立一套工程判断:哪些内容应该放在 Chart、哪些内容应该放在 values、哪些内容适合用 Kustomize overlay patch、哪些内容必须交给密钥系统或 CI/CD 注入。读完后,你应该能解释一条后端发布链路中,模板输入、渲染结果、集群对象、release 记录和排障证据之间的关系。

本篇会把 Helm 与 Kustomize 放在同一张图里讨论,但不会把它们简单归为“二选一”。Helm 更擅长把一组可参数化的 Kubernetes 对象打包成可发布单元;Kustomize 更擅长在原生 YAML 上做环境级覆盖。真实团队常见的治理方式,是选一个作为主线,再用另一个补足边界,而不是把所有差异都塞进同一种机制。

1. 问题背景#

多环境治理最常见的失败方式,是把 dev、test、prod 写成三份看似相同、实际不断漂移的 YAML。今天测试环境改了镜像 tag,明天生产环境忘了同步资源限制,后天预发环境临时加了一个环境变量,几周之后团队已经无法回答“这三个环境到底差在哪里”。此时发布不再是声明期望状态,而变成了对历史手工操作的猜测。

Helm 和 Kustomize 解决的正是这类问题:把“相同部分”沉淀成可复用结构,把“不同部分”显式表达出来,把“发布前最终会应用什么 YAML”变成可以在 CI 中渲染、校验、diff 和归档的证据。对后端工程师来说,这比单次部署成功更重要,因为生产事故往往不是某个命令不会写,而是发布输入不透明、环境差异不可见、回滚时不知道应该回到哪个版本。

需要先建立一个边界意识:环境差异并不等于所有东西都可以按环境随意改。镜像 tag、replica 数量、资源限制、域名、探针阈值、开关配置、依赖地址、密钥引用方式,都可能按环境不同;但对象类型、端口契约、健康检查语义、权限模型、数据持久化策略,不应该在不同环境里无约束漂移。多环境治理的目标,是让差异被看见、被限制、被验证,而不是让差异变得更方便地失控。

2. 核心概念#

Helm 和 Kustomize 都作用在 Kubernetes manifest 生成阶段,但抽象方向不同。Helm 从“发布包”视角组织对象:Chart 里有模板、默认值、依赖和元数据;发布时输入 values,渲染出最终 YAML,并形成 release 记录。Kustomize 从“原生资源组合”视角组织对象:base 保存通用 YAML,overlay 通过 patch、namePrefix、images、configMapGenerator 等机制生成某个环境的最终 YAML。

对象主要职责不应该承担的职责排障时先看什么
Chart打包一组可发布的 Kubernetes 对象承担业务运行逻辑Chart.yamltemplates/、渲染结果
values给模板提供变量输入保存真实生产密钥实际传入的 values 层级
template render生成最终 manifest保证集群语义一定正确helm template 输出与 schema 校验
release记录一次安装或升级替代 Git 变更记录helm history/get values/get manifest
base保存可复用原生 YAML包含所有环境差异base 对象是否稳定、可读
overlay对 base 做环境覆盖承载复杂条件逻辑kubectl kustomize 最终输出
patch精确修改资源字段大面积重写资源结构target 是否命中、字段路径是否正确

2.1 Helm 的发布包视角#

一个 Chart 可以理解成“后端服务部署包”。它通常包含 Chart.yamlvalues.yamltemplates/ 和可选的 charts/ 依赖目录。Chart.yaml 描述 Chart 名称、版本和应用版本;values.yaml 提供默认输入;templates/ 中的 Deployment、Service、Ingress、ConfigMap 等模板会读取 values 并渲染成 Kubernetes YAML。

Helm 的关键价值不是“少写 YAML”,而是把一组对象作为一个 release 管理。一次 helm upgrade --install 不只是 apply 多个文件,还会记录这次 release 使用的 Chart、values 和 manifest。出问题时可以用 helm history 看发布序列,用 helm get values 看实际输入,用 helm get manifest 看当时渲染结果,再结合 Kubernetes 事件和 Pod 日志判断问题发生在模板输入、渲染结果、集群准入还是应用运行阶段。

因此 Chart 的工程边界要尽量清晰:模板可以表达对象结构和少量条件分支,但不应该写成复杂程序;values 可以表达环境差异,但不应该保存密钥明文;helper template 可以复用 labels、name、annotations,但不应该隐藏关键字段,让评审者看不到最终对象关系。

2.2 Kustomize 的原生 YAML 视角#

Kustomize 的核心思想是“先有一份可读的原生 YAML,再按环境叠加差异”。base 目录里保存 Deployment、Service、ConfigMap 等通用对象;overlays/devoverlays/prod 等目录通过 kustomization.yaml 引用 base,并用 patch 修改副本数、镜像 tag、资源限制、Ingress host 等环境字段。

这种方式的优点是接近 Kubernetes 原生对象,评审者不需要先理解模板语法,就能看懂 base 的目标状态。它特别适合对象结构稳定、环境差异集中在少量字段上的场景。例如一个内部 API 服务在不同环境只改变镜像、replica、资源和域名,用 Kustomize overlay 会比写大量 Helm 条件语句更直观。

但 Kustomize 也有边界。patch 过多时,最终结果会变得难以预测;overlay 层级过深时,读者必须在多个目录之间跳转;如果把复杂业务条件塞进 patch,治理成本会迅速上升。Kustomize 最适合表达“在这份基础对象上,某个环境明确改了哪些字段”,不适合替代发布包管理、依赖管理和 release 历史。

2.3 环境差异的治理边界#

多环境治理的核心问题不是“差异能不能配置”,而是“差异是否有边界”。建议把字段分成四类:第一类是必须统一的契约字段,例如容器端口、Service selector、健康检查路径、权限模型;第二类是允许按环境变化的运行字段,例如 replica、resources、autoscaling、ingress host;第三类是由 CI/CD 注入的发布字段,例如 image tag、git sha、build number;第四类是不能进入普通 values 的敏感字段,例如数据库密码、第三方 token、证书私钥。

一个可靠的 values 分层通常长这样:Chart 内置 values.yaml 放安全默认值;仓库中保存 values-dev.yamlvalues-staging.yamlvalues-prod.yaml 表达环境差异;CI 使用 --set image.tag=$GIT_SHA 或临时 values 文件注入构建产物;密钥只传引用名,例如 existingSecret: order-service-db,真实密钥由 External Secrets、Sealed Secrets、SOPS 或云厂商密钥系统管理。

治理边界还要体现在评审规则上。不是所有 values 改动都应该同等看待:改 replica 和改数据库地址的风险不同,改镜像 tag 和改 Service selector 的风险不同,改 ConfigMap 和改 Secret 引用的验证方式也不同。好的模板结构会让这些差异在 PR diff 中清晰可见,而不是藏在一长串条件渲染逻辑里。

3. 运行机制#

从发布链路看,Helm 与 Kustomize 都属于“manifest 生成与发布前治理”层。它们的输出不是直接的业务行为,而是一组 Kubernetes 对象声明。真正的运行仍然由 Kubernetes 控制器完成:Deployment 创建 ReplicaSet,ReplicaSet 创建 Pod,Service 选择 endpoints,Ingress 或 Gateway 接入流量,ConfigMap 和 Secret 注入配置。

一条可治理的链路通常是:源码合并后构建镜像并生成不可变 tag;CI 选择目标环境的 values 或 overlay;渲染最终 manifest;执行 lint、schema 校验、策略校验和 diff;通过审批后执行 upgrade 或 apply;发布系统记录输入、输出、操作者、时间和结果;出现异常时从 release 记录、渲染 YAML、集群事件、Pod 状态和应用日志逐层追证。

3.1 从输入到最终 manifest#

Helm 的输入链路一般是 Chart + values layers + CLI overrides。当执行 helm templatehelm upgrade 时,Helm 会按优先级合并 values:Chart 默认值最低,多个 -f 文件按顺序覆盖,--set--set-file 的优先级更高。模板读取合并后的 .Values,再生成最终 manifest。

Kustomize 的输入链路一般是 base + overlay kustomization + patches/generators/images。overlay 引用 base 后,可以统一替换镜像、生成 ConfigMap、给资源加 label,也可以用 strategic merge patch 或 JSON patch 修改具体字段。最终输出必须通过 kubectl kustomize overlays/prodkustomize build overlays/prod 查看,而不能只看某个 patch 文件就判断结果。

这两条链路都要求团队保存“最终 manifest”这个证据。因为出问题时,单独看模板或 patch 都不够:模板可能渲染错,patch 可能没有命中,values 可能被 CI 覆盖,集群准入控制也可能拒绝某些字段。最终 manifest 是连接发布输入和集群状态的关键中间证据。

3.2 release 与期望状态#

Helm 的 release 是一个很重要的治理对象。它记录“某个命名空间里,某个 release 名称对应的一次部署历史”。当你执行 helm upgrade --install order-service ./chart -f values-prod.yaml,Helm 不只是把 YAML 交给 Kubernetes,还会记录 revision。后续可以用 helm rollback 回到某个 revision,也可以用 helm diff upgrade 在升级前查看变更。

但 release 不是万能回滚。它能回滚 Kubernetes manifest,却不能自动回滚数据库 schema、外部密钥版本、消息队列状态或业务数据。生产发布时必须明确哪些变化属于 Helm release 可覆盖范围,哪些变化需要额外的迁移脚本、审批单或人工确认。否则“回滚成功”可能只代表对象声明回去了,应用依赖的外部状态并没有回去。

Kustomize 本身不提供 release 历史,它通常依赖 GitOps 工具或 CI/CD 系统记录提交、渲染结果和 apply 结果。如果使用 Argo CD、Flux 这类工具,Git commit 与应用状态之间会形成另一种 release 证据链。无论工具如何选择,核心都是同一件事:必须能回答“当前集群状态来自哪次代码变更和哪份环境输入”。

3.3 渲染、校验与 diff 的闭环#

发布前闭环至少包含三类检查。第一类是渲染检查:Helm 用 helm linthelm template,Kustomize 用 kubectl kustomize,确保模板或 overlay 可以生成 YAML。第二类是 Kubernetes 语义检查:使用 kubeconformkubevalkubectl apply --dry-run=server 验证字段是否被当前集群 API 接受。第三类是变更检查:Helm 用 helm diff upgrade,Kustomize 或 GitOps 链路用 kubectl diff、Argo CD diff 或渲染产物 diff。

这三个步骤不能互相替代。模板能渲染,不代表字段对集群版本合法;schema 合法,不代表变更符合团队策略;策略通过,也不代表发布后应用一定健康。CI 的目标不是证明“不会出事故”,而是尽早拦住低级错误,并让高风险变更在发布前被看见。

对后端服务来说,diff 尤其重要。一次看似普通的 values 修改,可能实际改变 Deployment 的 selector、删除 Service port、替换 Secret 引用或触发全部 Pod 重建。如果发布前没有 diff,事故发生后团队只能在现场反推“到底变了什么”。如果 diff 被归档,排障可以从事实开始,而不是从记忆开始。

4. 最小可运行示例#

下面用一个订单服务说明两种组织方式。假设服务镜像是 registry.example.com/backend/order-service,容器监听 8080,生产环境需要 3 个副本、严格资源限制和独立域名。示例只覆盖核心结构,真实项目还需要 RBAC、HPA、PodDisruptionBudget、NetworkPolicy、监控注解和密钥系统。

4.1 Helm Chart 的目录与 values 分层#

一个最小 Chart 可以这样组织:

charts/order-service/
Chart.yaml
values.yaml
values-dev.yaml
values-prod.yaml
templates/
deployment.yaml
service.yaml
ingress.yaml
configmap.yaml

values.yaml 放通用默认值,默认值要安全、保守、适合本地或测试环境:

image:
repository: registry.example.com/backend/order-service
tag: dev
replicaCount: 1
service:
port: 80
targetPort: 8080
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
config:
springProfilesActive: dev
logLevel: INFO
secret:
existingSecret: order-service-secret

values-prod.yaml 只表达生产差异:

replicaCount: 3
image:
tag: "2026-07-05.1"
ingress:
enabled: true
host: order.example.com
config:
springProfilesActive: prod
logLevel: WARN
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi

发布前应先渲染并检查最终 YAML:

Terminal window
helm lint charts/order-service
helm template order-service charts/order-service -f charts/order-service/values-prod.yaml
helm diff upgrade --install order-service charts/order-service -f charts/order-service/values-prod.yaml

注意密钥只出现引用名 existingSecret,不出现真实密码。真实密钥由集群内 Secret 或外部密钥控制器负责同步,Chart 只负责引用。

4.2 Kustomize base 与 overlay#

如果团队希望保持 YAML 原生可读,可以用 Kustomize:

k8s/order-service/
base/
deployment.yaml
service.yaml
kustomization.yaml
overlays/
dev/
kustomization.yaml
patch-deployment.yaml
prod/
kustomization.yaml
patch-deployment.yaml
ingress.yaml

base/kustomization.yaml 只声明通用资源:

resources:
- deployment.yaml
- service.yaml

overlays/prod/kustomization.yaml 引用 base,并表达生产差异:

resources:
- ../../base
- ingress.yaml
images:
- name: registry.example.com/backend/order-service
newTag: "2026-07-05.1"
patches:
- path: patch-deployment.yaml
target:
kind: Deployment
name: order-service

patch-deployment.yaml 只修改生产需要覆盖的字段:

apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
template:
spec:
containers:
- name: order-service
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi

检查时不能只看 patch 文件,而要看最终输出:

Terminal window
kubectl kustomize k8s/order-service/overlays/prod
kubectl apply --dry-run=server -k k8s/order-service/overlays/prod
kubectl diff -k k8s/order-service/overlays/prod

4.3 示例迁移到真实项目#

从示例迁移到真实项目时,第一件事是稳定命名和 labels。Deployment、Service、ServiceMonitor、NetworkPolicy、Ingress 的 selector 必须一致且可预测,否则某个 overlay 修改了 label 后,流量可能悄悄断开。Helm 中建议用 helper template 统一生成 app.kubernetes.io/nameapp.kubernetes.io/instanceapp.kubernetes.io/version;Kustomize 中建议通过 commonLabels 或显式 labels 统一管理,但要小心不要改变 selector 语义。

第二件事是明确配置热更新语义。ConfigMap 改了以后,Pod 不一定自动重启;Secret 改了以后,应用也未必重新读取。Helm 常见做法是在 Pod template annotation 中写入配置 checksum,让配置变化触发滚动更新;Kustomize 常见做法是使用 configMapGenerator 生成带 hash 的 ConfigMap 名称。无论哪种方式,都要和应用读取配置的方式一致。

第三件事是把发布证据沉淀下来。CI 至少归档这几类内容:使用的镜像 digest、values 或 overlay 输入、最终 manifest、diff 结果、dry-run 结果、发布命令输出。真正排障时,这些证据比“当时应该是这个版本”可靠得多。

5. 配置逐行拆解#

多环境治理中最容易出事故的字段,往往不是最复杂的字段,而是最普通、最常改的字段。镜像、资源、副本、域名、密钥引用和 patch target 都很简单,但它们直接影响发布结果、运行稳定性和安全边界。

5.1 镜像与版本字段#

image.repository 应该表达镜像仓库位置,通常不建议按环境随意改变。开发、测试、生产如果使用不同 registry,也要通过清晰的环境 values 或 overlay 表达,并在 CI 中校验生产只允许来自可信仓库。否则最常见的事故就是生产部署了测试镜像,或者同一个 tag 在不同仓库指向不同内容。

image.tag 是发布链路中最关键的字段之一。生产环境应尽量使用不可变 tag 或直接记录 digest,避免 latest 这类可漂移标签。CI 注入 tag 时,要同时记录 Git commit、构建编号和镜像 digest。Helm 场景可以通过 --set image.tag=$GIT_SHA 注入;Kustomize 场景可以用 images.newTag 覆盖。无论哪种方式,发布单里都要能追到镜像构建记录。

appVersionimage.tag 不要混为一谈。appVersion 是 Chart 元数据,帮助人理解应用版本;真正决定 Pod 拉取哪个镜像的是 Deployment 中的 image 字段。排障时如果只看 Chart 版本,很容易误判实际运行的制品。

5.2 副本、资源与探针字段#

replicaCountspec.replicas 表达容量和可用性预期。开发环境一个副本通常够用,生产环境至少要结合负载、滚动发布策略、PodDisruptionBudget 和 HPA 判断。不要只把 replica 当成“环境差异字段”,它还关系到滚动更新期间是否有足够实例承接流量。

resources.requests 决定调度和容量预留,resources.limits 决定运行上限。后端 JVM 服务尤其要注意内存限制、堆大小和容器感知参数之间的关系。limits 过低会导致 OOMKilled,requests 过低会导致节点超卖和抖动。生产 values 里应该显式写资源,而不是继承开发默认值。

livenessProbereadinessProbestartupProbe 不是装饰字段。readiness 决定实例是否接流量,liveness 决定是否重启,startup 保护慢启动应用不被过早杀掉。Spring Boot 服务如果启动慢,应该配置 startupProbe 或合理的 initialDelay,而不是盲目拉大 liveness 阈值。

5.3 域名、配置与密钥字段#

ingress.host、Gateway route 或内部域名通常是环境差异字段,但它们也会影响证书、DNS、网关规则和回调地址。生产域名不应该通过临时命令手工注入,应该在 values 或 overlay 中可审查,并在发布前 diff 中明确出现。

ConfigMap 适合保存非敏感配置,例如日志级别、开关默认值、依赖服务地址。需要注意的是,配置值改变并不自动等于应用行为立即改变。你必须知道应用是启动时读取、定时刷新,还是通过配置中心动态订阅。模板只负责注入,不负责保证应用正确消费。

Secret 的边界要更严格。values 中可以保存 Secret 名称、key 名称、外部密钥路径或引用关系,但不应该保存真实生产密码。生产密钥泄漏到 Git 后,修复不是“删掉那一行”这么简单,而是要轮换密钥、清理历史、审计访问范围,并把 CI secret scanning 纳入门禁。

6. 后端工程场景#

后端服务的多环境治理,最终要落到团队协作场景:开发能在本地复现,测试能稳定验证,平台能审查变更,运维能定位事故,业务负责人能判断风险。Helm 与 Kustomize 只是工具,真正重要的是每个场景中输入、输出和责任边界是否清楚。

6.1 本地、测试与预发环境#

本地环境追求低成本和快速反馈,可以简化副本数、资源限制和外部依赖,但不应该改变应用启动契约。比如本地可以使用 mock 依赖或测试数据库,却不应该把健康检查路径、容器端口、Service selector 改成另一套逻辑。否则本地成功没有迁移价值。

测试环境追求可复现。每次测试发布应该能回答:使用哪个镜像、哪些 values 或 overlay、依赖服务地址是什么、数据库 schema 版本是什么。测试环境如果长期靠手工修配置,会让缺陷复现变得困难,也会让生产发布失去预演意义。

预发环境追求生产相似性。它不一定承载生产流量,但应尽量使用接近生产的资源、网关、密钥引用方式和发布流程。预发环境最重要的价值,是提前暴露模板渲染、准入策略、权限、网络和配置注入问题,而不是只验证业务接口能不能返回 200。

6.2 CI/CD 中的渲染校验#

CI/CD 不应该直接执行 helm upgradekubectl apply,中间至少要有渲染、校验和 diff。一个简化流程可以是:构建镜像并推送;生成镜像 digest;渲染目标环境 manifest;执行 helm lintkubectl kustomize;执行 schema 校验和策略校验;生成 diff;人工或自动审批;发布;归档 release 证据。

策略校验可以覆盖团队约束,例如生产环境禁止 latest tag,必须设置 resources,禁止 privileged 容器,必须引用指定命名空间的 Secret,Ingress host 必须属于受控域名。工具可以是 OPA Gatekeeper、Kyverno、Conftest 或平台自研规则,关键是规则要在发布前暴露,而不是等集群拒绝或事故发生后再补。

CI 还要区分“构建时变量”和“运行时配置”。镜像 tag、Git SHA、构建时间属于构建产物元数据;数据库连接、业务开关、密钥引用属于运行配置。两者混在一起,会导致同一个镜像在不同环境行为不可解释,或者一次配置改动被迫重新构建镜像。

6.3 团队协作与审查责任#

开发同学需要负责应用契约:端口、健康检查、配置读取方式、优雅停机、日志输出和指标暴露。平台同学需要负责模板边界:Chart 或 base 的结构、默认值、资源策略、权限模型、发布门禁。测试同学需要负责环境输入可复现:测试用例对应哪个镜像和配置。运维或 SRE 需要负责观测与恢复:告警、容量、回滚和事故证据。

PR 审查时不要只看 YAML 是否语法正确,而要看变更属于哪类风险。改镜像 tag 要关联构建记录;改资源要关联容量依据;改 Secret 引用要关联密钥变更单;改 selector 要格外谨慎,因为它可能让 Service 找不到 Pod;改 ConfigMap 要确认是否会触发滚动更新。

团队越大,越需要把“可以改什么、谁来审、怎么验证”写成约定。否则 Helm 和 Kustomize 只是把手工差异换成了 Git 差异,并没有真正治理。

7. 常见错误与排障#

排障时不要从“我猜是哪一行 values 错了”开始,而要从证据链开始。先确定当前集群实际运行的对象,再反推它来自哪次发布、哪份 manifest、哪组 values 或 overlay、哪个镜像 digest。证据链越完整,定位越快。

7.1 Helm values 覆盖顺序错误#

典型现象是:生产发布后镜像 tag、replica 或配置值不是预期值。第一步看发布命令和 CI 日志,确认 -f 文件顺序和 --set 参数;第二步用 helm get values RELEASE -n NAMESPACE --all 查看 release 实际 values;第三步用 helm get manifest 查看最终 YAML;第四步对比 PR 中期望的 values。

常见根因包括:values-prod.yaml 被较后的文件覆盖,CI 的 --set image.tag 注入了旧变量,布尔值或数字被 --set 类型转换,或者某个模板使用了错误的 values 路径。修复时不要只改命令,要把 values 分层规则写进 CI 模板,并在发布前输出合并后的关键字段摘要。

预防方式是减少覆盖层数,并约定优先级。例如默认 values 只放通用安全值,环境 values 只放环境差异,CI 只注入构建产物字段。超过三层覆盖时,就应该警惕后续维护成本。

7.2 Kustomize patch 没有命中#

典型现象是:overlay 里明明写了 patch,但最终 Deployment 没变,或者改到了不该改的对象。第一步执行 kubectl kustomize overlays/prod 查看最终输出;第二步检查 patch target 的 kindnamenamespace 是否和 base 资源一致;第三步确认 patch 类型和字段路径是否适合当前对象结构。

Kustomize patch 的风险在于“文件存在不代表生效”。资源被 namePrefix 改名、namespace 不一致、patch target 写错、数组字段匹配失败,都可能导致 patch 未按预期工作。尤其是 containers 这类数组字段,如果 name 不匹配,就可能出现覆盖失败或结构异常。

预防方式是把最终输出纳入 CI,并对关键字段做断言。例如生产 overlay 渲染后必须有 replicas: 3,镜像 tag 必须等于当前构建号,resources 不能为空,Ingress host 必须是生产域名。只看 patch 文件不能证明最终状态。

7.3 发布后应用不健康#

如果发布成功但 Pod 不健康,先区分是 Kubernetes 对象问题还是应用运行问题。对象层看 kubectl describe pod、事件、镜像拉取、资源限制、挂载、探针失败原因;应用层看容器日志、启动参数、配置读取、依赖连接、数据库迁移和 JVM 内存。不要一上来就回滚,也不要一上来就改 values。

Helm 场景要同时看 release 和 Kubernetes 状态:helm status 只能告诉你发布状态,不等于业务健康。Kustomize 或 GitOps 场景也一样,同步成功只代表对象已应用,不代表服务可用。真正的发布完成标准应该包括 rollout 完成、readiness 通过、关键接口探测通过、错误率和延迟没有异常。

如果回滚,必须明确回滚对象范围。Helm rollback 可以回到上一版 manifest,但如果本次发布同时执行了数据库迁移、外部配置切换或密钥轮换,单纯回滚 manifest 可能不够。事故记录中要写清楚“回滚了什么,没有回滚什么”。

8. 生产化边界#

生产化不是把 dev values 改成 prod values,而是给模板、环境差异、密钥、发布和回滚补上边界。边界越清楚,事故时越少靠经验猜。

8.1 values 分层与默认值策略#

默认 values 应该安全、可运行、低风险。它可以让新人快速启动服务,但不能假装自己就是生产配置。生产环境必须显式覆盖关键字段,例如镜像 tag、resources、replica、ingress、密钥引用、探针阈值。不要让生产继承“为了本地方便”的默认值。

环境 values 应该短而明确。它只描述环境差异,不复制整个对象。一个 values-prod.yaml 如果和 values-dev.yaml 大部分内容相同,说明抽象边界可能错了。重复越多,漂移越容易发生。

对关键字段可以建立 schema。Helm 支持 values.schema.json,可以限制字段类型、必填项、枚举值和格式。比如生产必须设置 resources.requestsimage.tag 不能是 latestingress.host 必须符合域名格式。schema 不能替代业务审查,但能拦住大量低级错误。

8.2 密钥、权限与合规边界#

密钥边界要从一开始就设计好。普通 values、ConfigMap、overlay patch 都不应该保存真实生产密钥。推荐做法是让模板只引用 Secret 名称,真实密钥由 External Secrets、Vault、云 KMS、Sealed Secrets 或 SOPS 管理,并通过 RBAC 控制谁能读取。

权限边界同样重要。Chart 不应该默认创建过大的 ClusterRole,ServiceAccount 也不应该复用 default。生产模板要明确每个服务需要哪些 API 权限,不需要访问 Kubernetes API 的普通后端服务,通常不应拥有额外权限。

合规边界包括审计和轮换。谁改了密钥引用,什么时候改的,是否触发轮换,是否影响旧 Pod,是否需要重启应用,这些都应该能追踪。密钥泄漏后的处理流程,也要比普通配置错误更严格。

8.3 回滚、审计与证据归档#

生产发布必须归档输入和输出。输入包括 Chart 版本、values 文件、overlay 目录、CI 注入变量、镜像 digest;输出包括最终 manifest、diff、dry-run 结果、release revision、rollout 状态。没有这些证据,回滚和复盘都会变成猜测。

回滚策略要提前演练。Helm 场景要确认哪些 revision 可回滚,回滚是否会触发 Pod 重建,是否需要同步回滚 ConfigMap 或 Secret;Kustomize/GitOps 场景要确认回退 Git commit 后控制器如何同步,是否存在手工漂移。

审计不只是满足流程,也是降低事故恢复时间。一个清晰的发布记录可以快速回答:本次变更是否只改了镜像,是否改了资源,是否改了密钥引用,是否改了网络入口。越快回答这些问题,越快定位事故边界。

9. 什么时候用,什么时候不用#

工具选型不要从偏好开始,而要从问题形态开始。你要管理的是一个可复用发布包,还是一组原生 YAML 的环境差异?你需要 release 历史和依赖管理,还是需要清晰 patch 和 GitOps 同步?答案不同,工具主线就不同。

9.1 优先选择 Helm 的场景#

当服务需要作为一个可复用发布单元交付时,优先考虑 Helm。例如多个团队复用同一套服务模板,需要通过 values 打开或关闭 Ingress、HPA、ServiceMonitor、PDB;或者服务依赖第三方组件,需要管理子 Chart;或者发布系统需要清晰的 release revision、rollback 和 diff。

Helm 也适合平台团队提供标准化 Chart。平台把通用能力封装进模板,业务团队只填写 values。这样可以减少重复 YAML,也能把资源限制、labels、探针、监控注解、Pod 安全上下文等规范沉淀下来。

不适合 Helm 的情况是:模板条件越来越多,values 层级越来越深,评审者必须运行渲染才能理解任何字段。此时要么拆 Chart,要么把部分环境覆盖交给 Kustomize 或 GitOps,而不是继续在模板里堆逻辑。

9.2 优先选择 Kustomize 的场景#

当团队希望保留 Kubernetes 原生 YAML,并且环境差异主要是少数字段覆盖时,优先考虑 Kustomize。它适合 GitOps 场景,也适合需要让开发、平台、测试都能直接读懂最终对象结构的团队。

Kustomize 对“基础对象稳定、环境差异明确”的服务很友好。例如 base 中定义 Deployment 和 Service,dev overlay 降低资源和副本,prod overlay 增加副本、资源、Ingress 和 NetworkPolicy。评审时可以直接看 patch 改了哪些字段。

不适合 Kustomize 的情况是:需要复杂参数化、依赖管理、发布包版本、Chart 仓库分发或大量可选组件。patch 如果膨胀到几十个文件,也会让最终状态难以理解。此时应考虑 Helm 作为主线,或重新拆分 base。

9.3 混用与反模式#

Helm 和 Kustomize 可以混用,但要有清晰边界。一种常见方式是 Helm 负责生成标准服务 manifest,Kustomize 负责在 GitOps 仓库中对不同集群做最后一层覆盖。另一种方式是平台提供 Helm Chart,业务仓库只维护 values,环境仓库负责保存最终发布输入。

混用的反模式是两边都在改同一个字段。比如 Helm values 设置 replica,Kustomize overlay 又 patch replica;Helm 模板生成 Ingress,overlay 又替换 host;CI 再用命令行覆盖 image tag。这样会让优先级难以解释,事故时也很难追踪真实来源。

最终判断标准很简单:团队能否在发布前看见最终 manifest,能否在事故时追到输入来源,能否在 PR 中看懂环境差异,能否把密钥和敏感配置排除在普通模板之外。如果做不到,说明问题不在工具命令,而在治理边界还没设计好。

10. 下一篇衔接#

本篇解决的是 Helm 与 Kustomize 在多环境治理中的职责边界:Chart、values、templates、release、base、overlay、patch 如何协作,环境差异如何分层,密钥为什么不能混入普通配置,CI 为什么必须做渲染、校验和 diff。

下一篇会进入“容器日志、监控与排障证据链”。如果说本篇关注的是发布前和发布时的期望状态,那么下一篇关注的就是发布后的实际状态:日志如何定位应用问题,指标如何发现资源和流量问题,事件如何连接 Kubernetes 对象变化,trace 如何串起一次请求的上下游。

读完本篇后,你可以用三个问题自测:第一,能否说清一个生产 values 改动最终影响了哪些 Kubernetes 字段;第二,能否从 release 或 Git commit 追到当前集群对象的来源;第三,能否解释一个 Secret 引用、ConfigMap 更新或 Kustomize patch 失败时应该看哪些证据。如果能回答,就可以进入下一篇。

11. 官方参考#

第 14 篇:Helm 与 Kustomize 多环境治理
https://jupiter-ws.cn/posts/backend/container/14_helm_kustomize_multi_environment/
作者
Jupiter
发布于
2026-07-05
许可协议
CC BY-NC-SA 4.0