7.11 包管理与 Helm

一句话

Helm 是把 “一堆 YAML” 变成 “模板 + 默认值 + 一次可回滚的发布”;它的价值在 release 概念与 rollback,它的风险在 “模板里写编程”;团队变大后一定会问 “要不要 Kustomize”,答案取决于 “差异来自参数还是来自结构”。

三个概念:Chart / Values / Release

概念 是什么 存在哪
Chart 模板 + 默认值的打包(Chart.yaml + templates/ + values.yaml) 仓库 / OCI registry
Values 覆盖默认值的参数(环境差异、副本数、镜像 tag) 你的 values-<env>.yaml
Release 一次安装的实例,带版本历史 集群内的 Secret(sh.helm.release.v1.*)
web-chart/
├── Chart.yaml            # 名字、版本、appVersion
├── values.yaml           # 默认值(唯一真相,别在 template 里写死)
├── values.schema.json    # 可选:对 values 做 JSON Schema 校验
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── _helpers.tpl      # 命名与标签约定
│   └── NOTES.txt
└── crds/                 # CRD 只装不卸(见下)

三个必用命令 + 一个必知差异

helm template web ./web-chart -f values-prod.yaml   # 先把渲染结果看清楚,再安装
helm upgrade --install web ./web-chart -n prod -f values-prod.yaml --wait --timeout 5m
helm history web -n prod                            # 版本历史
helm rollback web 3 -n prod                         # 回到第 3 版
差异 说明
helm upgrade vs kubectl apply Helm 记录 release 与版本历史,能回滚;kubectl apply 只覆写当前状态
--wait 等资源就绪才返回;不加则 “命令成功 = 提交成功”,不是 “服务可用”
helm template 只渲染、不提交,是 review 与 diff 的基础
helm diff(插件) 安装前看 “会改什么”,比 helm upgrade 后回滚便宜

Helm vs Kustomize:按 “差异来源” 选

差异来源 更适合 原因
同一套结构,参数不同(副本数、镜像 tag、域名) Helm values 覆盖天然适配
同一套 YAML,按环境 “打补丁”(加 sidecar、改注解) Kustomize overlay/patch 不改原文件
需要发布历史与一键回滚 Helm Kustomize 没有 release 概念
需要被平台团队分发(chart 给多个团队) Helm(或 OCI 包) 版本化与依赖管理
需要 GitOps 的 “渲染后即真相” 两者皆可(Argo CD 都支持) 取决于团队习惯

两者不互斥:常见组合是 “Helm 出基础 chart,Kustomize 做最后一层环境补丁”,代价是多一层心智负担。

五个必须知道的边界

边界 说明
helm rollback 不回滚 PVC 与 CRD 数据与 CRD 不在 release 的删除范围内,别指望回滚能 “清库”
crds/ 目录下的 CRD 只安装不升级不删除 升级 CRD 要手工 kubectl apply(或用专门流程)
values.schema.json 不是自动生成的 不写就等于没有校验,拼错 key 会静默变默认值
模板函数里的 lookup 会让渲染结果依赖集群现状 helm template 与集群内渲染结果可能不同,无法确定性复现
release 存在 Secret 里 权限控制要按 Secret 做;历史版本多时 Secret 也会变多

反模式

反模式 后果
模板里写复杂 if/else 与字符串拼接 可读性崩塌,没人敢改;渲染结果只能靠 helm template 猜
在 template 里硬编码镜像 tag 环境差异无处覆盖,回滚只能改代码
用 --set 传几十个参数 参数散落在命令行,无法 review、无法复现
不写 values.schema.json 拼错 key 静默失效(用默认值),排查极难
用 helm upgrade 替代 review 上线后才发现渲染出 3 个意外资源
期望 helm uninstall 清掉 PVC 数据卷保留,下次安装挂到旧数据
release 名随环境随便起 命名混乱导致 upgrade 打到错误的 release

三条铁律

🔑 模板只做参数替换,不做业务逻辑;逻辑写进 values 或应用,不进 if。

🔑 上线前先 helm template / helm diff;看不见渲染结果就升级,等于盲改生产。

🔑 rollback 只回滚 K8s 资源;数据、CRD、外部依赖要单独设计回退。

自检

Q1 为什么 helm upgrade --install 之后 Pod 没起来,命令却返回成功?

▸ 答案:因为默认不等就绪:Helm 认为 “资源提交成功” 就是成功。加 --wait(配 --timeout)才会等 Pod Ready、PVC 绑定、Job 完成。这也解释了 “发布看起来成功,业务却是 502”——发布成功与可用是两件事,判据在 kubectl rollout status。

Q2 helm rollback 能把数据库恢复吗?

▸ 答案:不能。rollback 重建的是 K8s 资源清单(Deployment/Service/ConfigMap 等),不碰 PVC 里的数据,也不会撤销数据库迁移。要回滚数据,必须依赖备份/快照与 expand-contract 的发布流程。把 rollback 当 “一键恢复”,是发布事故里最常见的误判。

Q3 团队在 template 里用 lookup 读现有 ConfigMap 做判断,有什么隐患?

▸ 答案:lookup 让渲染结果依赖集群当前状态,于是 helm template(离线)与 helm upgrade(在线)可能渲染出不同结果,无法确定性复现,也让 GitOps 的 “仓库即真相” 失效。正确做法是把依赖作为显式 values 传入,渲染保持纯函数。

Q4 为什么 values.schema.json 值得写?

▸ 答案:因为 Helm 对 values 的默认行为是宽松合并:拼错的 key 不报错,直接生效为 “没设置”,于是用默认值跑起来,问题延后到运行期。写 schema 后,helm template/install/upgrade 会在渲染前校验类型与必填项,把 “配置错” 变成 “渲染失败”,在 CI 里就能拦住。

Q5 一个平台团队要把中间件分发给 40 个业务团队,选 Helm 还是 Kustomize?

▸ 答案:Helm(或 OCI 打包的 chart):需要版本化、可回滚、可参数化,且有 Chart.yaml 的版本语义给业务方引用。Kustomize 更适合 “业务团队自己的环境差异”,不适合作为分发单元(它没有版本与依赖概念)。常见组合:平台出 chart(含合理默认值与 schema),业务用 values 覆盖,环境补丁用 Kustomize 叠加。

跨篇相关:声明式与 release 的差别见 7.1 声明式 API 与控制器模式;Controller/CRD 与 chart 的职责边界见 7.10 Operator 与 CRD;制品的不可变与仓库见 54.1 不可变制品、54.2 镜像与仓库;部署编排与环境一致性见 54.3 部署编排、54.4 环境一致性;回滚的数据侧设计见 55.3 回滚设计。