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 回滚设计。