CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Helm 与 GitOps 入门/课时/模板与 values
RawBlame
课时目录12 节

Helm 与 GitOps 入门

  1. 01为什么需要 Helm
  2. 02安装 Helm 与仓库
  3. 03Chart 目录结构
  4. 04模板与 values
  5. 05install upgrade rollback
  6. 06依赖与子 Chart
  7. 07GitOps 是什么
  8. 08仓库布局与清单
  9. 09Argo CD 入门
  10. 10Flux 概览
  11. 11发布策略
  12. 12综合练习

模板与 values

Go template 语法、引用 .Values、条件与循环。

35 分钟

模板与 values

这一课你将能做到

  1. 用 .Values、if/with/range、include+nindent 写出可渲染的 Deployment/Service 模板,关键行能解释为什么这么写
  2. 用 helm template(不需要集群)对比同一 Chart 在 dev/prod 两份 values 下的输出差异,说清 -f 与 --set 的覆盖顺序
  3. 遇到「模板渲染出无效 YAML」时,用 --debug 与缩进规则定位并修复

为什么要学

Chart 的灵魂是 Go template + YAML:Helm 在安装/升级前把默认 values、各层 -f 文件、--set 参数合并成一份 .Values,再驱动模板渲染出最终清单交给 API。写错缩进、漏引号、条件分支吐出半截映射,都会变成「看起来像 Helm 的错,其实是 YAML 坏了」。本课练的是 可读、可调试 的模板——够用且清晰,而不是堆砌过滤器炫技。学完后你应能不看答案写出 backend 的 Deployment/Service,并用 helm template 自检。

先建立图像

values.yaml(默认) + values-dev.yaml(-f) + --set(可选)
        \            |                /
         ▼           ▼               ▼
   合并后的 .Values(越靠后优先级越高)
                 +
   .Release / .Chart / .Capabilities / .Files
                 │
                 ▼
       templates/*.yaml(Go template 引擎)
                 │
                 ▼
        纯 YAML 清单 → 交给 API,或仅 helm template 预览
内置对象人话常用字段
.Values合并后的旋钮表.Values.image.tag、.Values.replicaCount
.Release这次安装实例.Release.Name、.Release.Namespace
.Chart包元数据.Chart.Name、.Chart.Version
.Capabilities集群能力探测.Capabilities.KubeVersion(进阶)

人话:模板里写的是「填空题」;helm template 是交卷前自己先誊一遍答案,不用集群就能检查。

跟着做

步骤 1:准备三份 values

values.yaml(Chart 默认):

replicaCount: 1
image:
  repository: registry.example/coderoadmap-backend
  tag: "1.0.0"
service:
  port: 8080
db:
  host: mysql.coderoadmap-dev.svc
  passwordSecretName: backend-db-secret
# 空对象 = 可选块;模板里 with 条件渲染
resources: {}

values-dev.yaml:

replicaCount: 1
image:
  tag: "1.0.0-dev"

values-prod.yaml:

replicaCount: 2
image:
  tag: "1.0.0"
db:
  host: mysql.prod.internal
resources:
  limits:
    cpu: "500m"
    memory: 512Mi

步骤 2:写 Deployment 模板

templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  # helpers 统一命名,避免手写拼错
  name: {{ include "coderoadmap-backend.fullname" . }}
  labels:
    {{- include "coderoadmap-backend.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "coderoadmap-backend.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "coderoadmap-backend.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: api
          # 仓库与 tag 分字段;升级通常只改 tag
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}
          env:
            - name: SPRING_DATA_REDIS_HOST
              value: redis.coderoadmap.svc
            - name: DB_HOST
              value: {{ .Values.db.host | quote }}
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.db.passwordSecretName }}
                  key: password
          {{- with .Values.resources }}
          # with:仅当 resources 非空时才渲染整个 resources 块
          resources:
            {{- toYaml . | nindent 12 }}
          {{- end }}

步骤 3:补 Service 模板

templates/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "coderoadmap-backend.fullname" . }}
  labels:
    {{- include "coderoadmap-backend.labels" . | nindent 4 }}
spec:
  type: ClusterIP
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
  selector:
    {{- include "coderoadmap-backend.selectorLabels" . | nindent 4 }}

确认 selector 与 Pod 标签来自 同一个 selectorLabels helper——不一致 Service 就选不中 Pod。

步骤 4:写 helpers 最小集

templates/_helpers.tpl:

{{- define "coderoadmap-backend.fullname" -}}
{{- printf "%s-backend" .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "coderoadmap-backend.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

{{- define "coderoadmap-backend.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

注意 include 必须传上下文 .,否则 helper 里访问不到 .Release/.Chart。

步骤 5:渲染并对比环境

helm template myrel . -f values-dev.yaml  > dev.yaml
helm template myrel . -f values-prod.yaml > prod.yaml

打开对比:dev 副本 1、tag 1.0.0-dev、无 resources 块;prod 副本 2、有 resources 限额、db.host 指向内网。再验证覆盖顺序:

helm template myrel . -f values-prod.yaml --set image.tag=9.9.9 | grep "image:"
# 输出 9.9.9 —— --set 优先级高于 -f 文件

结论写进笔记:优先级 默认 values < 先传的 -f < 后传的 -f < --set;多层 -f 时后者覆盖前者。

步骤 6:故意制造错误再修复

把 resources 块的缩进打乱(或删掉 nindent 前的 -),然后:

helm template myrel . -f values-prod.yaml --debug

读报错信息定位行号 → 修复 → 重新渲染通过。体会口诀:「渲染报错先怀疑 YAML 结构与空白,再怀疑函数与数据。」

讲清楚要点

  1. 因为渲染结果必须是合法 YAML,所以空白控制与模板逻辑同等重要——{{- 与 -}} 吃掉紧邻的空白/换行;indent/nindent 把 helper 输出按层级缩进(nindent 先换行再缩进,几乎总是比 indent 更安全)。
  2. 因为字符串里可能含 :、{ 等特殊字符,所以镜像名、env 值、注解值要用引号包起来——image: "{{ ... }}"、| quote 都是为此;不加引号时 YAML 会把冒号解析成映射导致结构损坏。
  3. 因为多环境只是 values 不同,所以禁止在模板里写死 if eq .Release.Name "prod" 这类环境魔数——Release 名是命名问题不是配置项;环境差异一律显式放进 values,模板保持环境无关。
  4. 因为 helm template 不碰集群,所以 CI 与本地评审应先 template/lint——在「装进集群」之前就能发现绝大多数结构错误;它发现不了的只有运行时问题(镜像不存在、探针路径错误)。
  5. 因为 helpers 统一命名与标签,所以改资源名只改 define 一处——fullname/labels/selectorLabels 三件套是骨架里的标准件。
  6. 因为 --set 表达复杂结构很别扭(逗号分隔、索引语法),且优先级最高,所以只用于临时试验——长期配置以 values 文件为准,GitOps 场景更是只信文件。

你可能会懵的地方

  • {{ }} 和 {{- -}} 差在哪? 功能相同;加 - 会剥掉紧邻的空白与换行。YAML 对缩进和空行敏感,不用 - 经常渲染出「幽灵空行」导致结构错位,所以实操里几乎总是写 {{- ... -}}。

  • if / with / range 怎么选? if 判断真假(空字符串、0、nil、空 map 都为假);with 把 . 切换到指定对象(块内直接 .cpu 而不是 .Values.resources.cpu),对象为空则整块跳过;range 遍历列表或 map。资源可选块的标准写法就是 {{- with .Values.resources }} ... {{- end }}。

  • 为什么到处是 nindent? include 返回的是不带缩进的字符串,直接放进 YAML 会破坏层级。nindent 4 = 先输出一个换行再缩进 4 格,让标签块精确落位到 labels: 下面两级。

  • toYaml 是干嘛的? 把 values 里的对象(如 resources: {limits: {...}})原样序列化成 YAML 文本。用户在 values 里怎么写结构,模板就怎么输出——比逐字段手拼灵活得多,是透传任意结构(resources、nodeSelector、tolerations)的标准手法。

  • 模板里能写多复杂的逻辑? 引擎能力很强,但工程共识是 模板越薄越好:分支与默认值尽量设计进 values 结构里;模板过厚=难 lint、难评审、难在 GitOps 里 diff。看到三层嵌套 if 的 Chart 先怀疑设计。

错法对照

错法问题正解
image: {{ .Values.image.repository }}:{{ .Values.image.tag }} 不加引号tag 含特殊字符或以数字开头时 YAML 解析歧义整串加引号 image: "{{ ... }}"
Service selector 手写、Pod 标签用 helper两处易漂移,选不中 Pod两边引用同一 selectorLabels
用 .Release.Name 判断环境写死分支难测试、难复用values 显式参数
条件分支只包住值不包住 key输出半截 key 导致非法 YAML整个键值块放进 if/with
只 --set 调通、从不固化 values 文件同事与 CI 无法复现结果写回 -f 文件
helper 里漏传上下文 .helper 内取不到 .Releaseinclude "x" . 始终传点

卡点急救

  1. 现象:error converting YAML to JSON 或渲染产物缩进错乱——九成是空白问题:检查该行是否该用 {{-、helper 输出是否用了 nindent、块缩进格数是否与 YAML 层级一致;用 helm template --debug 看 Meta 行报的文件与行号。
  2. 现象:env 值渲染成 map[...] 或直接报错——你把对象当字符串用了;用 toYaml+nindent 整块透传,或 | quote 处理标量,并检查 values 里该键的真实类型。
  3. 现象:同一个 Chart dev 渲染正常、prod 报错——差异几乎必在 prod values 的结构(多了 resources、缩进错了);helm template myrel . -f values-prod.yaml --debug 单独渲染 prod 定位,别在模板里瞎找。

面试常问

  • Helm 模板的内置对象有哪些?平时最常用哪几个? .Values(合并后的用户配置)、.Release(安装实例名/命名空间/修订号)、.Chart(包元数据)、.Capabilities(集群 API 版本探测)、.Files(包内文件访问)、.Template(当前模板信息)。日常 90% 是前三者:镜像与副本读 .Values,命名与标签用 .Release+.Chart。

  • values 的合并优先级顺序? 从低到高:Chart 默认 values.yaml → 父 Chart(子 Chart 场景)→ 按顺序传入的每个 -f 文件(后者覆盖前者)→ --set/--set-string/--values 等命令行参数。--set 优先级最高但表达复杂结构困难,生产以 values 文件为准。

  • 如何在不连接集群的情况下验证 Chart? helm lint(语法与最佳实践)+ helm template --debug(渲染预览,可叠加各环境 -f 对比输出差异)+ helm install --dry-run --debug(连集群校验但不落资源,含 admission 校验)。CI 流水线中至少跑前两个。

自测三问

  1. .Values、.Release、.Chart 各提供什么?升级镜像 tag 应改哪一层的哪个值?
  2. 为什么 CI 里必须跑 helm template/helm lint?它们发现不了哪类问题?
  3. 写出「仅当 .Values.resources 非空才渲染 resources 块」的模板写法,并解释为什么要用 with 而不是 if。

小结一句话

values 驱动 Go template 生成合法 YAML:空白与引号决定合法性,helpers 决定一致性,helm template 多环境对比是渲染正确性的第一道闸——多环境一套 Chart 从这里开始成立。

延伸

  • Helm — Chart Template Guide
  • Helm — Values Files
  • 下一课:把渲染好的 Chart 真正装进集群——install/upgrade/rollback 与 Release 生命周期。
加载本节练习…
← 上一课Chart 目录结构下一课 →install upgrade rollback