LabHub
学习 学习路径 课程

Helm Chart 的制作与发布

用模板函数把 values 安全地插进去

在 LabHub 中继续学习

目标

使用 template function 将 values 安全写入 manifest,并亲手掌握值缺失、值必填、值为 block 时分别应使用什么工具。

为什么重要

Helm template 不是 YAML editor,而是字符串生成器。只有最终字符串生成后,YAML parser 才会读取它。因此,缩进错两格时,不一定直接报错,整个 block 可能消失或落到错误 parent 下。这正是 toYaml 展开 block 后必须接 nindent 的原因:indent 不会生成前置换行,第一行会直接粘到前一个 key 上。value 设计亦然。default 用于“可以缺省的值”,required 用于“缺失就不能部署的值”。若二者颠倒,要么以错误默认值悄然部署,要么 chart 变得无人可用。最后,value precedence 从低到高为 chart values.yaml、通过 -f 传入的文件(从左到右)、--set;map 会深度合并,list 则整体替换。

步骤

  1. /root/helm/tpl/labhub-api 创建 chart(可从 helm create labhub-api 开始)。values.yamlimage.repositorynginximage.tag"1.27"Chart.yamlappVersion"1.26"。然后用 helm template labhub-api /root/helm/tpl/labhub-api > /root/helm/tpl/out/base.yaml 保存 render。结果必须有 Deployment,container image 为 저장소:태그 形式,且不能残留花括号或 <no value>
  2. 将 container image tag 写成 {{ .Values.image.tag | default .Chart.AppVersion }},保存 helm template labhub-api /root/helm/tpl/labhub-api --set image.tag="" > /root/helm/tpl/out/default.yaml。即使 tag 为空,image 仍应类似 nginx:1.26,不得用 latest 作为默认值。
  3. values.yaml 中设置 ingress.enabled: trueingress.host: api.labhub.local,并在 /root/helm/tpl/labhub-api/templates/ingress.yaml 中用 required 包装 host(例如 {{ required "ingress.host 를 반드시 지정하세요" .Values.ingress.host }})。随后用 helm template labhub-api /root/helm/tpl/labhub-api --set ingress.host="" > /root/helm/tpl/out/required-error.txt 2>&1 故意触发失败并保存输出。文件中必须显示 render 失败消息及缺少的 key。
  4. values.yamlresources 设置为 limits.cpu: 500mlimits.memory: 512Mirequests.cpu: 100mrequests.memory: 128Mi,在 Deployment container 中以 {{- toYaml .Values.resources | nindent 12 }} 形式整体传入。重新 render /root/helm/tpl/out/base.yaml 后,必须显示 container 的 resources.limits.cpuresources.requests.memory
  5. values.yaml 中把 env 设计为map,定义 APP_MODE: serverLOG_LEVEL: infoTZ: Asia/Seoul。在 Deployment 中使用 range $k, $v := .Values.env 生成 container env list。/root/helm/tpl/out/base.yaml 中 container env 必须恰好 3 个,其中一个 name 为 LOG_LEVEL
  6. /root/helm/tpl/labhub-api/templates/_helpers.tpl 中用 define 创建 chart 标识字符串(name-version),并在 Deployment metadata.annotations 中添加 labhub.io/chart: {{ include "labhub-api.chart" . }}。再在 /root/helm/tpl/out/include-note.txt 中用一行说明 templateinclude 的区别:include 返回字符串,所以可接 pipeline 做后处理(缩进)。
  7. /root/helm/tpl/values-base.yaml 中设置 replicaCount: 2env.LOG_LEVEL: infoimage.tag: base;在 /root/helm/tpl/values-stage.yaml 中设置 replicaCount: 4env.LOG_LEVEL: debugimage.tag: stage。执行 helm template labhub-api /root/helm/tpl/labhub-api -f /root/helm/tpl/values-base.yaml -f /root/helm/tpl/values-stage.yaml --set image.tag=cli > /root/helm/tpl/out/merged.yaml。结果 replicas 为 4,LOG_LEVELdebug,image tag 为 cli。最后在 /root/helm/tpl/out/precedence.txt 中按从低到高逐行写 precedence:chart values.yaml-f values-base.yaml-f values-stage.yaml--set
  8. 添加 ConfigMap template,将 values.yamlconfig map 输出为 data,并在 Pod template metadata.annotations 中添加 checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}。Pod 级 spec.template.spec.securityContext.runAsNonRoot 必须为 true。将完整 render 保存到 /root/helm/tpl/out/final.yaml(至少 3 个 object、至少 3 个 container env、无 <no value>)。同时保存 helm lint /root/helm/tpl/labhub-api > /root/helm/tpl/out/lint.txt,且不能有 [ERROR]

参考

引用 values 进行 render

/root/helm/tpl/labhub-api 创建 chart(可从 helm create labhub-api 开始)。values.yamlimage.repositorynginximage.tag"1.27"Chart.yamlappVersion"1.26"。然后用 helm template labhub-api /root/helm/tpl/labhub-api > /root/helm/tpl/out/base.yaml 保存 render。结果必须有 Deployment,container image 为 저장소:태그 形式,且不能残留花括号或 <no value>

image 由 repository 与 tag 两个 values 组合。若 render 结果残留花括号或 '',说明引用的 key 不存在于 values。

用 default 填补空值

将 container image tag 写成 {{ .Values.image.tag | default .Chart.AppVersion }},保存 helm template labhub-api /root/helm/tpl/labhub-api --set image.tag="" > /root/helm/tpl/out/default.yaml。即使 tag 为空,image 仍应类似 nginx:1.26,不得用 latest 作为默认值。

即使 tag 为空,render 后 image 也必须带 tag。若从 Chart.yaml 获取替代值,chart 与 app version 会自然保持一致;latest 不是答案。

用 required 强制必填值

values.yaml 中设置 ingress.enabled: trueingress.host: api.labhub.local,并在 /root/helm/tpl/labhub-api/templates/ingress.yaml 中用 required 包装 host(例如 {{ required "ingress.host 를 반드시 지정하세요" .Values.ingress.host }})。随后用 helm template labhub-api /root/helm/tpl/labhub-api --set ingress.host="" > /root/helm/tpl/out/required-error.txt 2>&1 故意触发失败并保存输出。文件中必须显示 render 失败消息及缺少的 key。

必填 value 为空时,render 本身必须失败。错误消息中应写明缺少哪个 key;失败输出写到标准错误,保存时必须一并捕获。

使用 toYaml 与 nindent 传递 block

values.yamlresources 设置为 limits.cpu: 500mlimits.memory: 512Mirequests.cpu: 100mrequests.memory: 128Mi,在 Deployment container 中以 {{- toYaml .Values.resources | nindent 12 }} 形式整体传入。重新 render /root/helm/tpl/out/base.yaml 后,必须显示 container 的 resources.limits.cpuresources.requests.memory

像 resource limit 这样整体传递的 block 不应逐行编写。两个缩进 function 的区别在于是否需要前置换行。

用 range 展开环境变量

values.yaml 中把 env 设计为map,定义 APP_MODE: serverLOG_LEVEL: infoTZ: Asia/Seoul。在 Deployment 中使用 range $k, $v := .Values.env 生成 container env list。/root/helm/tpl/out/base.yaml 中 container env 必须恰好 3 个,其中一个 name 为 LOG_LEVEL

用 key 与 value 两个 variable 遍历 values map。将值加引号更安全,最终必须恰好生成三个。

定义 named template 并 include

/root/helm/tpl/labhub-api/templates/_helpers.tpl 中用 define 创建 chart 标识字符串(name-version),并在 Deployment metadata.annotations 中添加 labhub.io/chart: {{ include "labhub-api.chart" . }}。再在 /root/helm/tpl/out/include-note.txt 中用一行说明 templateinclude 的区别:include 返回字符串,所以可接 pipeline 做后处理(缩进)。

把 define 创建的片段插入 annotation。请记录两种调用方式中哪一种可接 pipeline,以及其原因。

确认 value precedence

/root/helm/tpl/values-base.yaml 中设置 replicaCount: 2env.LOG_LEVEL: infoimage.tag: base;在 /root/helm/tpl/values-stage.yaml 中设置 replicaCount: 4env.LOG_LEVEL: debugimage.tag: stage。执行 helm template labhub-api /root/helm/tpl/labhub-api -f /root/helm/tpl/values-base.yaml -f /root/helm/tpl/values-stage.yaml --set image.tag=cli > /root/helm/tpl/out/merged.yaml。结果 replicas 为 4,LOG_LEVELdebug,image tag 为 cli。最后在 /root/helm/tpl/out/precedence.txt 中按从低到高逐行写 precedence:chart values.yaml-f values-base.yaml-f values-stage.yaml--set

同时使用两个 values 文件和 command-line option。values 文件的传入顺序有意义;根据结果按从低到高写出 precedence。

添加 config checksum 与 security context

添加 ConfigMap template,将 values.yamlconfig map 输出为 data,并在 Pod template metadata.annotations 中添加 checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}。Pod 级 spec.template.spec.securityContext.runAsNonRoot 必须为 true。将完整 render 保存到 /root/helm/tpl/out/final.yaml(至少 3 个 object、至少 3 个 container env、无 <no value>)。同时保存 helm lint /root/helm/tpl/labhub-api > /root/helm/tpl/out/lint.txt,且不能有 [ERROR]

为避免 ConfigMap 内容变化后 Pod 不更新,可把 render 后配置文件的 hash 放入 Pod annotation。同时填写 Pod 级 security setting。