Chapter 03

Go 模板语法

深入掌握 Helm 模板引擎的核心语法:Values 值引用、内置对象、条件与循环控制流、模板复用,以及消除多余空白行的技巧。

模板分隔符与基本语法

Helm 使用 Go 的 text/template 包。所有模板指令写在双大括号 {{ }} 内。

# 基础输出
name: {{ .Values.appName }}

# 管道(Pipeline):将左侧的值传给右侧的函数
name: {{ .Values.appName | upper }}           # 转大写
name: {{ .Values.appName | quote }}           # 加引号
name: {{ .Values.appName | trunc 63 | trimSuffix "-" }}  # 截断+去尾横线

# 去除空白行:{{- 去左侧空白,-}} 去右侧空白
{{- if .Values.enabled }}
key: value
{{- end }}
{{- 的重要性

YAML 对缩进和空行极为敏感。{{- (注意空格)会消除该指令前的所有空白(包括换行)。 -}} 消除后面的空白。在条件判断和循环中正确使用 - 是避免生成无效 YAML 的关键。

内置对象

Helm 在渲染模板时注入了多个内置对象,通过 . 访问。

.Values
来自 values.yaml 和用户提供的 --set/--values 的所有配置值。例如 .Values.image.repository
.Release
Release 对象。包含 .Release.Name(Release 名称)、.Release.Namespace(命名空间)、.Release.IsInstall(首次安装)、.Release.IsUpgrade(升级)、.Release.Revision(版本号)。
.Chart
Chart.yaml 的内容。包含 .Chart.Name、.Chart.Version、.Chart.AppVersion、.Chart.Description 等。
.Files
访问 Chart 中的非模板文件(如配置文件)。常用方法:.Files.Get "config.ini"、.Files.AsConfig(转为 ConfigMap 格式)。
.Capabilities
集群能力信息。包含 .Capabilities.KubeVersion.GitVersion(K8s 版本)、.Capabilities.APIVersions.Has "apps/v1"(检查 API 是否可用)。
.Template
当前模板信息。包含 .Template.Name(模板文件路径)、.Template.BasePath(模板目录路径)。
# 常见内置对象用法
metadata:
  name: {{ .Release.Name }}-myapp
  namespace: {{ .Release.Namespace }}
  labels:
    app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
    helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
spec:
  containers:
  - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
    imagePullPolicy: {{ .Values.image.pullPolicy }}

控制流:if / else

# 基本 if/else
{{- if .Values.ingress.enabled }}
# 渲染 Ingress...
{{- end }}

# if/else if/else
type:
{{- if eq .Values.service.type "NodePort" }}
  NodePort
{{- else if eq .Values.service.type "LoadBalancer" }}
  LoadBalancer
{{- else }}
  ClusterIP
{{- end }}

# 逻辑运算符:and / or / not
{{- if and .Values.ingress.enabled .Values.ingress.tls }}
# 同时启用了 ingress 和 tls
{{- end }}

# 真值判断:以下值为 false(空字符串、0、nil、空 slice/map)
{{- if .Values.someValue }}   # someValue 非空/非零/非 nil 则执行

循环:range

# 遍历 slice(列表)
env:
{{- range .Values.envVars }}
  - name: {{ .name }}        # . 指向当前元素
    value: {{ .value | quote }}
{{- end }}

# 遍历 map(键值对),$key 和 $val 是变量绑定
annotations:
{{- range $key, $val := .Values.annotations }}
  {{ $key }}: {{ $val | quote }}
{{- end }}

# 使用 $index 获取索引
{{- range $index, $host := .Values.ingress.hosts }}
  # $index 从 0 开始
{{- end }}

with:作用域切换

# 不使用 with(繁琐)
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
pullPolicy: {{ .Values.image.pullPolicy }}

# 使用 with 切换作用域(. 变为 .Values.image)
{{- with .Values.image }}
image: {{ .repository }}:{{ .tag }}
pullPolicy: {{ .pullPolicy }}
{{- end }}

# with 块内需要访问父作用域,使用 $ 引用全局根对象
{{- with .Values.resources }}
resources:
  {{- toYaml . | nindent 2 }}
releaseName: {{ $.Release.Name }}  # $ 访问根对象
{{- end }}

define 与 include:模板复用

使用 define 定义命名模板,用 includetemplate 调用。

# 在 _helpers.tpl 中定义命名模板
{{- define "myapp.labels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

# 在其他模板文件中使用 include(支持管道,推荐)
metadata:
  labels:
    {{- include "myapp.labels" . | nindent 4 }}

# template 关键字(不支持管道,不推荐用于有缩进需求的场景)
{{ template "myapp.labels" . }}
include vs template
include 是 Helm 扩展的函数,返回字符串,可以接管道操作(如 | nindent 4)。template 是 Go 原生指令,直接输出内容,不支持管道。因此在需要控制缩进时必须使用 include
nindent vs indent
nindent N 在文本前添加换行后再缩进 N 个空格,indent N 只缩进不添加前置换行。在 YAML 中 nindent 更常用,确保标签块在正确的缩进层级。

实战:构建完整 Deployment 模板

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.port }}
              protocol: TCP
          {{- with .Values.resources }}
          resources:
            {{- toYaml . | nindent 12 }}
          {{- end }}
          {{- if .Values.envVars }}
          env:
            {{- range .Values.envVars }}
            - name: {{ .name | quote }}
              value: {{ .value | quote }}
            {{- end }}
          {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}

常用模板函数速查

函数用途示例
default提供默认值{{ .Values.tag | default "latest" }}
required必填参数,缺失时报错{{ required "image.repository is required" .Values.image.repository }}
quote加双引号{{ .Values.version | quote }}
toYaml结构体转 YAML 字符串{{ toYaml .Values.resources }}
toJson结构体转 JSON 字符串{{ toJson .Values.config }}
nindent添加换行+缩进{{ toYaml .Values.labels | nindent 4 }}
trunc截断字符串{{ .Values.name | trunc 63 }}
trimSuffix去尾字符{{ .Values.name | trimSuffix "-" }}
upper / lower大小写转换{{ .Values.env | upper }}
replace替换字符串{{ .Chart.Version | replace "+" "_" }}
printf格式化字符串{{ printf "%s-%s" .Chart.Name .Chart.Version }}
contains子字符串检测{{ contains "NodePort" .Values.service.type }}
sha256sum计算 SHA256(常用于配置热重启){{ include "myapp.config" . | sha256sum }}
b64encBase64 编码(Secret 用){{ .Values.password | b64enc }}
lookup从集群查询现有资源{{ (lookup "v1" "Secret" .Release.Namespace "my-secret").data }}
本章小结

Go 模板语法是 Helm 的核心。掌握 {{- -}} 空白控制、内置对象(.Values/.Release/.Chart)、if/range/with 控制流和 define/include 模板复用后,你就能编写任意复杂的 Chart 模板。记住:include 优于 template(支持管道),toYaml | nindent 是处理复杂结构(资源限制、节点选择器)的标准写法。