Skip to content

Helm 入门系列 05:吃透语法——Helm 模板语法实战教程(附案例)

约 2029 字大约 7 分钟

Helm 入门系列云原生Helm

2026-03-31

Helm 之所以能成为 Kubernetes 生态的“包管理器”,核心就是模板系统——它能把静态的 YAML 配置变成可动态调整、可复用的“配置模板”。不管是简单的参数替换,还是复杂的条件判断/循环,Helm 模板都能搞定。这篇文章就用最通俗的语言+实战案例,把 Helm 模板语法讲透,看完就能上手写模板。

一、模板基础:什么是“动作”?

Helm 用的是 Go 语言的文本模板引擎,核心规则就一个:所有逻辑和数据引用都要包在 {{ }},这个包裹的部分叫“动作(Action)”;{{ }} 之外的内容,会原封不动输出。

1.1 空格控制:别让多余空格坑了你

写 YAML 最怕多余空格导致格式错误,Helm 提供 - 符号清理空格,用法超简单:

  • {{- 表达式 }}:删左边空格
  • {{ 表达式 -}}:删右边空格
  • {{- 表达式 -}}:删两边空格

实战案例

# 模板代码
hello: {{- "world" -}}  # 两边空格全删
foo: {{ "bar" }} baz     # 不删空格

渲染后结果:

hello:world
foo: bar baz

⚠️ 注意:- 和表达式之间必须有空格(比如 {{- 123 }},别写成 {{-123 }},会被当成数字 -123)。

二、核心:模板能访问哪些数据?

Helm 渲染模板时,会传一个全局“数据对象”(用 . 表示,俗称“点”),里面包含了 Chart 配置、发布信息、集群信息等所有你需要的数据。核心有 6 类,逐个讲清楚:

2.1 .Values:最常用的配置入口

.Values 对应 values.yaml 文件里的配置,也包括 helm install 时用 --set/-f 传的自定义值,是模板动态配置的核心。

实战案例

# 第一步:在 values.yaml 定义配置
app:
  name: my-app
  port: 8080

# 第二步:模板中引用
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.app.name }}  # 输出 my-app
spec:
  ports:
  - port: {{ .Values.app.port }} # 输出 8080

2.2 .Release:发布版本的元数据

记录当前 Helm 发布的信息,比如发布名、命名空间、是安装还是升级。

常用属性说明
.Release.Name发布名(比如你执行 helm install demo ./mychart 里的 demo
.Release.Namespace发布到哪个命名空间
.Release.IsInstall是不是首次安装(布尔值)

实战案例:给资源加“发布名前缀”,避免重名

metadata:
  name: {{ .Release.Name }}-{{ .Values.app.name }}  # 输出 demo-my-app
  namespace: {{ .Release.Namespace }}                # 输出发布的命名空间

2.3 .Chart:Chart 自身的元数据

对应 Chart.yaml 里的信息(比如 Chart 名、版本),属性名和 Chart.yaml 一致,只是首字母大写。

实战案例:给资源加标准化标签

labels:
  app.kubernetes.io/name: {{ .Chart.Name }}       # Chart 名
  app.kubernetes.io/version: {{ .Chart.AppVersion }} # 应用版本

2.4 .Capabilities:适配不同 Kubernetes 集群

用来判断集群能力(比如 K8s 版本、是否支持某个 API),避免模板在不同集群报错。

实战案例:兼容不同版本的 Deployment API

{{ if .Capabilities.APIVersions.Has "apps/v1/Deployment" }}
apiVersion: apps/v1  # 高版本集群用这个
{{ else }}
apiVersion: apps/v1beta2  # 低版本兼容
{{ end }}
kind: Deployment

2.5 .Files:读取 Chart 里的自定义文件

可以读取 Chart 里的非模板文件(比如配置文件、证书),不用把内容硬编码到模板里。

实战案例:把 config/ 目录的文件打包成 Secret

apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-config
data:
  {{- .Files.Glob "config/**" | .Files.AsSecrets | nindent 2 }}

2.6 $:全局变量“救星”

循环、with 语句里,. 的作用域会变,用 $ 能直接访问全局数据。

实战案例:循环中取发布名

# values.yaml
apps:
  - name: web
  - name: db

# 模板代码
{{ range .Values.apps }}
name: {{ .name }}-{{ $.Release.Name }}  # $ 指向全局,取 Release.Name
{{ end }}

三、数据处理:管道 + 常用函数

模板的“数据加工”全靠管道(|)——把前一个结果传给后一个函数,像 Unix 管道一样。先讲几个最常用的函数,结合管道实战:

3.1 常用函数速查(附案例)

函数作用实战案例
default 兜底值 变量变量为空时用兜底值{{ .Values.env | default "prod" }}(env 没配置就输出 prod)
toYaml把对象转成 YAML 字符串见下文示例
nindent n缩进 n 个空格,开头换行见下文示例
quote给字符串加引号(避免 YAML 解析坑){{ .Values.id | quote }}(id 是 e2e1f3 时,避免被解析为科学计数法)
eq/ne/gt/lt等于/不等于/大于/小于{{ if eq .Values.env "prod" }}(判断环境是不是生产)

3.2 管道实战:把配置转成 YAML 并缩进

# values.yaml
podSecurityContext:
  runAsUser: 1000
  runAsGroup: 3000

# 模板代码
securityContext:
  {{- .Values.podSecurityContext | toYaml | nindent 2 }}

渲染结果:

securityContext:
  runAsUser: 1000
  runAsGroup: 3000

3.3 兜底 + 加引号:避免配置为空

# 模板代码
character: {{ .Values.character | default "Sylvester" | quote }}

如果 .Values.character 没配置,渲染结果:character: "Sylvester"

四、控制结构:条件、循环、作用域

模板的“逻辑控制”靠 3 个核心结构,用实战案例讲明白:

4.1 if/else:条件判断

判断某个条件是否成立,决定要不要渲染某段配置。

实战案例:是否启用 Ingress

{{ if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-ingress
spec:
  rules:
  - host: {{ .Values.ingress.host }}
{{ else }}
# 注释:Ingress 未启用
{{ end }}

4.2 with:切换作用域 + 空值判断

with 有两个作用:① 变量为空时跳过代码块;② 变量非空时,把 . 指向这个变量(简化代码)。

实战案例:渲染注解

# values.yaml
annotations:
  author: "张三"
  version: "1.0"

# 模板代码
metadata:
  {{- with .Values.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}  # . 现在指向 .Values.annotations
  {{- end }}

4.3 range:循环遍历(数组/字典)

最常用的循环,分两种场景:

场景1:遍历数组(切换作用域)

# values.yaml
ports:
  - name: http
    port: 80
  - name: https
    port: 443

# 模板代码
ports:
{{ range .Values.ports }}
- name: {{ .name }}
  port: {{ .port }}
{{ end }}

场景2:遍历字典(定义变量)

# values.yaml
labels:
  app: my-app
  env: prod

# 模板代码
labels:
{{ range $key, $value := .Values.labels }}
  {{ $key }}: {{ $value | quote }}
{{ end }}

五、复用神器:命名模板

写模板最怕重复代码(比如每个资源都要生成全名、加标签),命名模板就是“代码片段复用”,核心规则:

  • 定义:用 define,通常放在 templates/_helpers.tpl(文件名以 _ 开头,不会被直接渲染);
  • 调用:用 include(推荐,支持管道)或 template

5.1 定义命名模板:生成资源全名

# templates/_helpers.tpl
{{/* 注释:生成资源全名,最长63字符,去掉末尾的横杠 */}}
{{- define "mychart.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

5.2 调用命名模板:简化主模板

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}  # 传入全局数据,调用模板

5.3 复杂逻辑封装:拼接镜像地址

# templates/_helpers.tpl
{{/* 注释:镜像地址优先级:digest > tag > appVersion */}}
{{- define "mychart.image" -}}
{{- $image := .Values.image.repository -}}
{{- if .Values.image.digest -}}
{{- printf "%s@%s" $image .Values.image.digest -}}
{{- else if .Values.image.tag -}}
{{- printf "%s:%s" $image .Values.image.tag -}}
{{- else -}}
{{- printf "%s:%s" $image .Chart.AppVersion -}}
{{- end -}}
{{- end -}}

# 主模板调用
spec:
  containers:
  - name: {{ .Chart.Name }}
    image: {{ include "mychart.image" . }}

六、模板调试 + 可维护性最佳实践

写模板难免出错,这 3 个方法能快速定位问题;同时遵守最佳实践,让模板更易维护。

6.1 调试三板斧

1. 试运行(--dry-run):模拟渲染,不部署

helm install demo ./mychart --dry-run

如果有语法错误,会直接提示“哪一行错了、错在哪”。

2. 查看已部署的清单:对比配置

helm get manifest demo  # 查看 demo 发布的最终渲染清单

3. 校验 Chart:自动找问题

helm lint ./mychart  # 检测语法、YAML 格式、资源名非法等问题

6.2 可维护性最佳实践

  1. 一个资源一个文件:Deployment 放 deployment.yaml,Service 放 service.yaml,别把所有资源写在一个文件里;
  2. 命名模板集中管理:所有复用的模板都放 templates/_helpers.tpl,方便查找;
  3. 遵循 K8s 标签规范:用 app.kubernetes.io/ 前缀的标签(比如 app.kubernetes.io/name),标准化更易维护。

七、总结

Helm 模板的核心就是“数据 + 逻辑”:

  1. .Values/.Release/.Chart 等数据对象获取配置和元数据;
  2. 用管道 + 函数加工数据,用 if/with/range 控制逻辑;
  3. 用命名模板复用代码,用 --dry-run/helm lint 调试问题。

掌握这些语法,你就能从“复制粘贴 YAML”升级到“写可复用、可动态配置的 Helm Chart”。