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 可以查看模板中所有变量的实际值,快速定位渲染错误。
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,避免将错误的资源清单提交到集群。
通过本章的学习,你已经掌握了从零创建Chart、编写Deployment和Service模板,以及使用模板函数与管道进行动态渲染的核心技能。这些是后续构建复杂Chart和集成网络拓扑的基础。