TT Lab
开始
学习 学习路径 课程

Helm Chart 的制作与发布

三方合并——什么被还原,什么会留下

在 TT Lab 中继续学习

一句话总结

Helm 3 的升级用旧清单、新清单、集群实物这三者制作补丁,所以 Chart 声明的字段会被改回去,而 Chart 不知道的字段则会保留下来。

为什么需要它

故障应对通常这样收场。流量涌来,用 kubectl scale deploy/api --replicas=20 紧急扩容,之后为了查找原因,又用 kubectl label deploy/api owner=ops 之类加了个标记。事态平息几天后,一个毫不相干的功能被部署了。第二天早上,副本数又回到了 1,而标签仍然留着。

如果不理解这种不对称,Helm 就成了“偶尔会抹掉我的修改的工具”。实际上只是一条非常明确的规则。

用三个状态制作补丁

Helm 2 只比较旧清单和新清单两者。所以在集群里由人改过的值不在比较对象之内,两者没有差异就不会发出任何补丁,人改的值就保留了下来。Helm 3 在这里又加入了一个集群的实物。

情形 旧清单 新清单 实物 结果
手动修改副本数 1 1 5 被改回 1
手动添加标签 无 无 有 原样保留
在 Chart 中去掉标签 有 无 有 被删除

第一行就是与 Helm 2 不同的地方。如果 Chart 声明了 replicas,那个字段的主人就是 Chart,实物不同就按声明改回来。第二行,Chart 不知道这个字段,所以不会包含在补丁里。第三行,旧清单里有而新清单里没有,会被理解为“删掉”。

由此得出一个实务结论——由自动伸缩器管理的 replicas 不应在 Chart 中声明。 如果声明了,每次部署 Helm 都会把自动伸缩器定的值改回去,自动伸缩器又把它调上去,形成拉锯战。正统的做法是在 Chart 中去掉 replicas,交给 HPA。

为什么值不会延续

第二个陷阱在值这一边,而不是对象。

helm upgrade api ./api --set replicas=4 --set logLevel=debug   # 리비전 2
helm upgrade api ./api --set replicas=6                        # 리비전 3

revision 3 的用户值只有 {replicas: 6}。logLevel 消失,回到 Chart 默认值。这不是 bug,而是默认行为——升级是用“这次给出的值”重新计算 release。可以用 helm get values api 确认。

有一个例外,这也是最容易让人混淆的。如果一个值也不给(既没有 --set 也没有 -f),Helm 会原样使用上一个 release 的用户值。所以只运行 helm upgrade api ./api 时,上次的值会保持,而一旦给出哪怕一个 --set,其余的就全部掉落。“去掉值就会回到默认值吧”这种直觉,在这里恰恰反着运转。要确保丢掉上次的值,就明确写上 --reset-values。

沿用选项有三个,名字相近容易混淆。

选项 作用
--reuse-values 原样沿用上次 release 的值,只叠加这次的 --set
--reset-values 丢掉上次的值,只在 Chart 默认值之上叠加这次的(与默认行为相同)
--reset-then-reuse-values 先回到 Chart 默认值,再叠加上次的用户值,然后叠加这次的

--reuse-values 与 --reset-then-reuse-values 的区别,在 Chart 默认值变了的时候显现。前者原样沿用上次 release 算好的值,所以 Chart 的新默认值被掩盖;后者先铺上新的默认值,再叠加用户明确指定的,所以新默认值会生效。升级 Chart 时默认值也一起升级的情况下,这个差别会决定部署结果。

--force 不是补丁而是替换

--force 不是打补丁,而是替换对象。它是遇到像 Service 的 clusterIP 这样无法用补丁修改的不可变字段时使用的逃生舱,但因为是替换,该对象会暂时消失再重新出现。如果是 Deployment,Pod 会全部重建;如果是 Service,路由会暂时中断。

替换还会附带一件事。因为是用新清单整个换掉,平时升级中能保留下来的“Chart 不知道的字段”也会消失。 手动加上的 owner 标签,在普通升级中会原样保留,但在 --force 之后就没有了。“升级不生效,先 force 一下”是生产中最昂贵的习惯之一。

在现场相遇的样子

最常见的事故是部署脚本里漏掉了一个 --set。值有八个左右时,有人删掉一行,只有那个值悄悄回到默认值。不会报错,部署显示成功。这个问题的正统解法不是沿用选项,而是把值作为文件放进仓库。只要一行 -f values-prod.yaml,用什么部署的就会留在提交记录里,经过代码评审,下一个人也能读到。--reuse-values 走向相反的方向——方便,但值只留在 release 里,在仓库任何地方都看不到。

部署前想看看会变什么,有 helm upgrade --dry-run=server。它把渲染结果发给真正的 API 服务器做验证,但不创建 release。不过这是“验证”,不是“看差异”。想按行查看有什么变化,需要 helm-diff 之类的插件,而这个实验环境里没有。

下一项实验要做什么

先部署一个 Deployment,在集群里手动修改副本数和标签,然后运行一次完全没动 Chart 的升级,确认什么被改回去、什么保留下来。看到在 Chart 里去掉标签时集群里也会被删掉,接着转到值这一边,通过 release 记录比较默认升级会丢弃之前的用户值,以及三个沿用选项的区别。最后把服务端预览和 --force 各用一次,把规则整理成报告。