13 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-05-17 10:00 |
Helm 模板管理
概述
本文档系统讲解 Helm Chart 的编写与管理实践。内容覆盖:Chart 骨架、values.yaml 参数化、Go 模板语法、多环境值覆盖、Chart 依赖管理、Hooks 机制,以及与 ArgoCD 的集成方式。架构决策参见 ../03-CICD与GitOps。
为什么需要 Helm?
[!question] 每个服务几十个 YAML 文件,怎么管理?
一个典型的
order-service部署包含:Deployment、Service、Ingress、ConfigMap、HPA、NetworkPolicy、PDB……当你有 50 个服务,每个都要维护这么一堆模板——这是重复劳动的噩梦。
Helm 提供了两个核心抽象:
| 概念 | 类比 | 说明 |
|---|---|---|
| Chart | NPM 包 / Maven jar | 打包格式,定义"我要部署什么" |
| Release | npm install 的实例 | 运行实例,同一个 Chart 可以有多个 Release(staging/prod) |
# 创建 Chart 骨架
helm create order-service
# 目录结构一览
charts/order-service/
├── Chart.yaml # 元信息 (name, version, description)
├── values.yaml # 默认值 —— 你唯一需要经常改的文件
├── templates/ # Go 模板文件
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml
└── .helmignore # 类似 .gitignore
Helm Release 生命周期
flowchart LR
A["install"] --> B["running"]
C["upgrade"] --> B
D["rollback"] --> B
E["uninstall"] --> B
style A fill:#e1f5fe
style C fill:#fff3e0
style D fill:#fce4ec
style E fill:#f3e5f5
第一步:让配置全部参数化
values.yaml 中存放所有可变参数,遵循"一处定义、多处引用"原则:
# values.yaml
replicaCount: 2
image:
repository: registry.example.com/order-service
tag: "v1.2.3"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 8080
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 75
templates/deployment.yaml 中使用 {{ }} 引用:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {{ toYaml .Values.resources | nindent 12 }}
[!tip] 两个关键技巧
toYaml .X | nindent 12:将嵌套的对象序列化为 YAML 并缩进。如果直接写{{ .Values.resources }},输出会压缩成一行,Kubernetes 无法解析。-修剪空白:{{- if ...和{{- end }}中的-会消除模板前后多余的换行符,避免生成空行导致 YAML 格式错误。
Helm 内置对象速查
| Helm 对象 | 访问方式 | 用途 |
|---|---|---|
.Release |
.Release.Name, .Release.Namespace |
当前发布实例的信息 |
.Chart |
.Chart.Name, .Chart.Version |
Chart 本身的元信息 |
.Values |
.Values.replicaCount 等 |
用户传入的值(合并了 values.yaml + --set) |
.Capabilities |
.Capabilities.KubeVersion |
集群 API 版本信息 |
.Files |
.Files.Get "config.ini" |
读取同目录下的非模板文件 |
.Notes |
渲染后显示给用户的提示文本 | Post-install 使用说明 |
第二步:多环境值覆盖
通过 values 文件叠加实现不同环境的差异化:
# Staging 使用命令行临时覆盖
helm upgrade --install order-service ./charts/order-service \
--namespace=staging \
--set image.tag=v1.2.4-dev
# Production 叠加额外值文件
helm upgrade --install order-service ./charts/order-service \
--namespace=production \
--values charts/order-service/values.prod.yaml \
--set image.tag=v1.2.3
典型 values.prod.yaml(只写与默认值不同的部分):
replicaCount: 5
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "2"
memory: 2Gi
autoscaling:
minReplicas: 5
maxReplicas: 50
[!tip] Values 文件合并优先级(低 → 高)
values.yaml— 默认值values.prod.yaml— 环境变量覆盖文件--set image.tag=xxx— 命令行参数(最高优先级)
flowchart TD
A["values.yaml"] --> D["合并结果"]
B["values.prod.yaml"] --> D
C["--set image.tag=v1.2.3"] --> D
D --> E[".Values"]
[!warning] 常见陷阱:JSON Patch
当使用
--set修改嵌套字段时,Helm 使用 JSON Patch,意味着它会替换整个对象而非合并。例如--set resources.limits.cpu="1"会将memory字段丢弃。对于复杂嵌套,优先使用独立的 values 文件。
第三步:条件渲染与循环
条件渲染
用 Helm 的条件语法处理可选资源:
# templates/ingress.yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}
spec:
rules:
- host: {{ .Values.ingress.host | quote }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}
port:
number: {{ .Values.service.port }}
{{- end }}
[!note]
quote管道函数当值的类型不确定时(可能是字符串也可能是数字),加
| quote确保输出带引号,避免true/false被 YAML 解析为布尔值。
Range 循环生成多个资源
当需要批量创建多个 ServiceMonitor、ConfigMap 等时,range 非常有用:
# templates/servicemonitor.yaml
{{- range $key, $value := .Values.extraServices }}
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ $.Release.Name }}-{{ $key }}
spec:
selector:
matchLabels:
app: {{ $.Release.Name }}
endpoints:
- port: {{ $value.port }}
interval: {{ $value.interval | default "30s" }}
{{- end }}
对应的 values:
extraServices:
grpc-exporter:
port: grpc
interval: 15s
prometheus-path:
port: metrics
interval: 10s
[!example] 生成结果
上面这段模板会为每个
extraServices条目生成一个独立的 ServiceMonitor。注意$key绑定到键名(grpc-exporter),而$value绑定到对应对象。使用$.Release.Name(带美元前缀)是因为在 range 内部.已被重绑定。
第四步:复用模板片段
随着 Chart 膨胀,deployment.yaml 和 service.yaml 之间会产生大量重复逻辑。Helm 提供 _helpers.tpl 来统一管理可复用的模板片段。
_helpers.tpl 标准写法
{{/*
Common labels — 所有资源统一标注 */}}
{{- define "order.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}
{{/*
Selector labels — Deployment selector 必须使用固定值 */}}
{{- define "order.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
在模板中引用
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
{{- include "order.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "order.selectorLabels" . | nindent 6 }}
[!tip] include vs template
include "name" .:返回值,可通过| nindent继续管道处理 —— 适合注入 label、annotationtemplate "name" .:直接输出,不返回结果 —— 很少用,通常只在特殊情况下需要
Chart 依赖与仓库管理
大型项目往往不会从零编写所有模板,而是复用社区已有的 Chart。
声明子依赖
# Chart.yaml
apiVersion: v2
name: order-service
type: application
version: 1.2.0
appVersion: "1.2.3"
dependencies:
- name: postgresql
version: "15.0.0"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: "18.0.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
condition告诉 Helm:当 values 中对应路径为false时,跳过安装该子 Chart。- 子 Chart 的 values 通过
postgresql.xxx/redis.xxx命名空间传入。
常用命令
helm dependency update ./charts/order-service # 拉取 / 更新子依赖
helm dependency build ./charts/order-service # 仅从 Chart.lock 构建(无网络变更时使用)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo prometheus # 搜索公开 Chart
[!note] Chart.yaml 的 apiVersion
apiVersion: v1(Helm 2):依赖写在dependencies:数组里,语义不同apiVersion: v2(Helm 3):引入requirements.yaml的概念整合到 Chart.yaml 自身,推荐使用 v2
与 ArgoCD 集成
ArgoCD 原生支持将 Helm Chart 作为 Application source,自动解析 values.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service
spec:
source:
repoURL: https://github.com/team/k8s-manifests.git
targetRevision: main
path: charts/order-service
helm:
parameters:
- name: image.tag
value: v1.2.3
- name: replicaCount
value: "5"
[!tip] ArgoCD + Helm 进阶用法
helm.valueFrom:从 ConfigMap 或 Secret 取值,避免把敏感值暴露在 Git 中helm.fileParameters:从文件加载大段配置值,适合 CI/CD 流水线场景helm.ignoreMissingValueFiles: true:允许某些 values 文件按环境选择性存在
Hooks:在关键时机执行自定义逻辑
Helm Hooks 允许你在 Release 的生命周期事件中挂载自定义 Job 或 Pod:
# templates/migrate-db.yaml
{{- if .Values.dbMigration.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-db-migrate
annotations:
"hooks.helm.sh/hook": pre-upgrade,pre-install
"hooks.helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
spec:
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./migrate", "up"]
restartPolicy: Never
{{- end }}
| Hook 事件 | 触发时机 |
|---|---|
pre-install |
资源创建之前(如数据库迁移) |
post-install |
资源创建之后(如初始化数据) |
pre-upgrade |
升级操作之前 |
post-upgrade |
升级操作之后 |
pre-delete |
删除操作之前(如优雅停机通知) |
post-delete |
删除操作之后 |
调试与测试
可视化渲染结果
# 查看渲染后的纯 YAML(不做实际部署)
helm template my-release ./charts/order-service
# 指定命名空间和 values 文件
helm template my-release ./charts/order-service \
--namespace=staging \
-f values.staging.yaml
[!tip] 调试三件套
helm lint ./charts/order-service:静态检查,快速定位语法问题helm template:看渲染后的全量 YAML,逐段排查问题helm diff upgrade --install ...:配合 helm-diff 插件,对比升级前后的差异
Dry Run
# 模拟部署(服务端校验,但不会真正改变集群状态)
helm upgrade --install my-release ./charts/order-service \
--dry-run --debug
最佳实践
| 原则 | 具体做法 |
|---|---|
| 单一真相源 | 所有可配参数集中在 values.yaml,模板层只做引用不做硬编码 |
| Helper 集中管理 | 公共 labels / selectors / annotations 一律放在 _helpers.tpl |
| 条件最小化 | 能用 defaults 解决的就不加 if;减少分支能大幅降低测试复杂度 |
| 版本号分离 | Chart.yaml 的 version 管 Chart 本身迭代,appVersion 管应用版本 |
| 先 lint 再 push | CI 中加入 helm lint + helm template --strict 门禁 |
| 值文件只写差异 | values.prod.yaml 只覆盖与默认值不同的字段,保持可读性 |
何时用裸 YAML vs Helm vs Kustomize?
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| < 10 个服务 | Kustomize / 裸 YAML | 复杂度高于收益 |
| 10~50 个服务 | Helm | 模板复用价值明显 |
| > 50 个服务 | Helm + Kustomize overlays | Helm 管模板,Kustomize 管环境差异 |
[!note] Kustomize 的定位
Kustomize 不依赖 Go templating,更轻量。适合"基于同一套模板,按环境覆盖差异配置"的场景。
常见组合:Helm 生成基础模板,Kustomize overlay 处理 staging/prod 差异。
关联笔记
- 01-CICD基础与实践 — CI/CD Pipeline 的搭建与实践
- 02-GitOps与ArgoCD — GitOps 工作流与 ArgoCD
- 04-安全与发布策略 — 安全管理与发布决策
- ../03-CICD与GitOps — 参考手册与决策指南