4. 编写第一个Helm Chart:从零创建Chart、定义Deployment和Service模板、使用模板函数与管道

本章是Helm实战的起点。你将亲手创建一个完整的Chart,理解其目录结构,并编写Deployment和Service模板。同时,我们将深入Helm模板语言的核心——模板函数与管道,掌握如何动态生成Kubernetes资源清单。

4.1 Chart目录结构与创建

一个标准的Helm Chart遵循固定的目录布局。使用 helm create 命令可以快速生成脚手架。

创建命令:
helm create my-first-chart

执行后,会在当前目录生成 my-first-chart/ 文件夹。

文件/目录 用途
Chart.yaml Chart的元数据文件,包含名称、版本、描述等。
values.yaml 默认配置值文件,用户可通过此文件覆盖模板中的变量。
templates/ 存放Kubernetes资源模板文件(Go模板语法)。
templates/_helpers.tpl 定义可复用的模板片段和命名模板。
charts/ 存放子Chart依赖(如未使用则为空)。
.helmignore 打包时忽略的文件列表,类似 .gitignore。
最佳实践: 保持 templates/ 目录扁平化,避免嵌套过深。每个资源类型建议单独一个文件,例如 deployment.yaml、service.yaml。

4.2 定义Deployment模板

我们将编写一个Nginx Deployment模板,使用 values.yaml 中的变量来控制副本数、镜像标签和资源限制。

步骤1:编辑 values.yaml

replicaCount: 3
image:
  repository: nginx
  tag: "1.25.3"
  pullPolicy: IfNotPresent
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

步骤2:创建 templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-first-chart.fullname" . }}
  labels:
    {{- include "my-first-chart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "my-first-chart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-first-chart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: 80
              name: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
注意: include 函数用于引入 _helpers.tpl 中定义的命名模板。 nindent 管道用于控制缩进,确保生成的YAML格式正确。

4.3 定义Service模板

Service模板用于暴露Deployment的端口。我们将创建一个ClusterIP类型的Service。

创建 templates/service.yaml

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

在 values.yaml 中添加Service配置:

service:
  type: ClusterIP
  port: 80

4.4 模板函数与管道核心用法

Helm模板语言基于Go模板,并扩展了大量函数。管道(|)允许你将前一个命令的输出作为下一个命令的输入,实现链式处理。

函数/管道 作用 示例
quote 将字符串用双引号包裹 {{ .Values.name | quote }}
upper 将字符串转为大写 {{ .Values.env | upper }}
default 如果值为空,则使用默认值 {{ .Values.port | default "8080" }}
toYaml 将对象转换为YAML格式 {{ .Values.resources | toYaml }}
nindent 换行并添加指定数量的空格缩进 {{ .Values.labels | toYaml | nindent 4 }}
include 引入命名模板并返回渲染结果 {{ include "mychart.labels" . }}
管道链式处理示例:
# 将标签对象转为YAML,然后缩进8个空格
labels:
  {{- .Values.podLabels | toYaml | nindent 8 }}

4.5 使用模板函数控制条件与循环

除了管道,Helm还支持条件判断和循环,用于生成动态资源。

  • 条件判断(if-else): 根据 values.yaml 中的布尔值决定是否生成某段YAML。
  • 循环(range): 遍历列表或字典,生成重复的资源块。

条件判断示例:

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
...
{{- end }}

循环示例(遍历端口列表):

ports:
{{- range .Values.service.ports }}
  - port: {{ .port }}
    targetPort: {{ .targetPort }}
{{- end }}

4.6 验证与调试Chart

编写完模板后,使用以下命令验证语法和渲染结果:

命令 用途
helm lint my-first-chart/ 检查Chart语法和最佳实践问题。
helm template my-first-chart/ 在本地渲染模板,输出最终的YAML清单。
helm install --dry-run --debug my-release my-first-chart/ 模拟安装,显示渲染后的资源及调试信息。
调试技巧: 使用 helm template 配合 --debug 可以查看模板中所有变量的实际值,快速定位渲染错误。
Helm Chart 模板渲染流程 values.yaml 用户配置值 templates/*.yaml Go模板文件 _helpers.tpl 命名模板定义 Helm引擎 模板函数 + 管道 条件/循环解析 Kubernetes YAML清单 输出示例 helm template mychart/ helm install --dry-run

4.7 实战:完整Chart示例

以下是一个完整的 values.yaml 和对应的Deployment、Service模板组合,展示如何通过模板函数实现灵活配置。

完整 values.yaml:

replicaCount: 2
image:
  repository: nginx
  tag: "1.25.3"
  pullPolicy: IfNotPresent
service:
  type: ClusterIP
  port: 80
  targetPort: 80
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi
ingress:
  enabled: false
  host: example.com
env:
  - name: NGINX_HOST
    value: "localhost"
  - name: NGINX_PORT
    value: "80"

Deployment模板(使用循环和条件):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-nginx
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-nginx
    spec:
      containers:
        - name: nginx
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          env:
            {{- range .Values.env }}
            - name: {{ .name }}
              value: {{ .value | quote }}
            {{- end }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
关键点:
  • 使用 .Release.Name 确保资源名称唯一。
  • range 循环遍历 env 列表,动态注入环境变量。
  • toYaml 将 resources 对象直接转换为YAML块。

4.8 常见错误与调试

错误现象 原因 解决方案
YAML缩进错误 模板中缩进不一致,或 nindent 使用不当 使用 helm template --debug 查看渲染结果,检查缩进
变量未定义 values.yaml 中缺少对应键,或拼写错误 在 values.yaml 中定义默认值,或使用 default 函数
命名模板未找到 _helpers.tpl 中未定义,或 include 名称错误 检查 _helpers.tpl 中的 define 名称是否匹配
管道链式调用失败 前一个函数输出类型与后一个函数输入不匹配 使用 toYaml 后必须接 nindent 或 indent
重要: 始终在 helm install 之前执行 helm lint 和 helm template,避免将错误的资源清单提交到集群。
模板函数与管道数据流 原始值 .Values.env upper 转大写 quote 加双引号 渲染结果 "PRODUCTION" 管道链式调用示例 {{ .Values.env | upper | quote }} 输入: production → 输出: "PRODUCTION" toYaml + nindent 组合 {{ .Values.resources | toYaml | nindent 12 }} 将对象转为YAML并缩进12个空格 default 函数 {{ .Values.port | default "8080" }} 如果port未定义,则使用8080

通过本章的学习,你已经掌握了从零创建Chart、编写Deployment和Service模板,以及使用模板函数与管道进行动态渲染的核心技能。这些是后续构建复杂Chart和集成网络拓扑的基础。