库图表——正因为无法安装才有用
一句话总结
library chart 是只包含 define、自己什么也不渲染的 Chart,是把标准标签和通用工作负载形状固定在一处的位置。
为什么需要它
第一次做 Chart 时,_helpers.tpl 总是以同一种形状诞生。helm create 放进来的 fullname、labels、selectorLabels 三个。问题从 Chart 变成十个时开始。这十个 _helpers.tpl 起初是复制品,但一个团队加了 app.kubernetes.io/component,另一个团队加了 team 标签,它们就变成了互不相同的生物。
为什么这是问题,看看使用标签的一方就清楚了。监控规则用 app.kubernetes.io/instance 选择对象,网络策略用 app.kubernetes.io/name 选择,成本仪表板用 team 来汇总。一个 Chart 里少了一行标签,只有那个工作负载会悄悄落到规则之外。在故障情况下会有“只有这个 Pod 没有指标”的报告,而原因是半年前复制品里漏掉的一行。
由此产生了一个要求:必须能一次改完标准标签。为此,这条规则必须在一个文件里,而这个文件必须能被多个 Chart 作为依赖带走。
工作原理
把 Chart.yaml 的 type 设为 library,Helm 就会把这个 Chart 排除在安装对象之外。真的尝试安装,会这样被拒绝。
Error: INSTALLATION FAILED: library charts are not installable
helm template 也因为同样的原因停下。不过这个 Chart 可以放进其他 Chart 的 dependencies,一放进去,其中所有的 define 就汇入父 Chart 的模板名称池(template name space)。父 Chart 用一行 include "platform-lib.deployment" . 就能整套拿到一个 Deployment。
模板名称在整个 Chart 中是一个名称池,这是关键,也是陷阱。子 Chart 定义的名称和父 Chart 定义的名称相同时,后读取的一方获胜,而父 Chart 是后读取的。如果不小心重叠,既没有错误也没有警告,悄悄地就用了别的模板。在 define 名称前面加上 Chart 名称的惯例,不是为了好看,而是为了防止这种冲突。
重新渲染 values 中字符串的 tpl
与 library 搭配经常使用的函数是 tpl。values 中写的字符串默认只是普通字符,即使里面有花括号也会原样输出。
note: "{{ .Release.Name }} in {{ .Release.Namespace }}"
如果用 {{ .Values.note }} 来插入这个值,花括号会原样输出。如果用 {{ tpl .Values.note . }} 来插入,它会在当场再被当作模板解释一次,放进 release 名称。让用户通过 values 传入自由格式配置的 Chart 就用这种方式——注解集合、sidecar 定义、配置文件正文之类。代价是 values 因此可以执行模板,所以不能把不可信的值交给 tpl。
什么时候用,什么时候不用
library chart 成为答案的条件出奇地窄。是在多个 Chart 必须遵守同一条规则,而且这条规则今后还会变的时候。标准标签正是如此——监控和策略按标签选择对象,所以规则必须只有一个,公司变大后,团队标签或成本中心标签会一个个增加。
相反,如果 Chart 只有两三个,规则也已固定,做 library 的成本就大于收益。依赖声明增加,又多出一条版本升级流程,新来的人要读模板,不是看 _helpers.tpl,而是得打开 charts/ 里的 tgz。这最后一点尤其被低估——渲染结果奇怪时,查找那个模板在哪个文件里,又远了一步。
所以比是否引入更先定下来的是版本升级流程。library 版本升上去之后,消费方 Chart 什么时候跟上?如果把 Chart.yaml 的版本范围像 0.1.0 这样固定,就要手工逐个升级消费方 Chart;如果像 ^0.1.0 这样放开,结果会随着运行 helm dependency update 的时间点而不同。让 CI 用 helm dependency build 只按锁定内容获取,后一种风险就消失了——锁文件在仓库里,用哪个版本构建的会留在记录里。
在现场相遇的样子
引入 library chart 时最常碰到的是更新时点。helm dependency update 会把那一刻的 library 打成 tgz 放进父 Chart 的 charts/。所以即使在 library 里改了一行标签,在消费方 Chart 重新获取依赖之前,仍然使用旧副本。如果使用内部仓库,就需要这样的流程:升级 library 版本,调整消费方 Chart 的 Chart.yaml 版本范围,让 CI 用 helm dependency build 按锁定内容获取。
而且不要往 library 里放太多东西。把整套 Deployment 通用化,起初很清爽,但每个 Chart 的不同需求一个个出现,if 就会堆积起来。实际工作中站得住的界线,通常是标签、名称规则、通用注解为止,工作负载主体由各个 Chart 自己拥有。通用化的收益和分支的代价在哪里反转,每个团队不同,所以 library 开始变大时,要停下来测量一下。
下一项实验要做什么
亲手做一个 library chart,确认它无法安装而被拒绝,并让两个应用 Chart 只换 values 就使用同一套 Deployment 模板和标签规则。用 tpl 让 values 中的模板字符串重新生效,最后在父 Chart 重新定义同名模板时,把两个 Chart 并排放置,确认哪一边会被渲染。