Helm 是 Kubernetes 的包管理器,将一组 K8s 资源模板化为可复用的 Chart。掌握 Helm 不仅能简化部署,还能实现参数化配置、多环境管理和版本控制。
目录
- 1. Chart 结构详解
- 2. Values 多环境管理
- 3. Go Template 高级语法
- 4. Chart Hook
- 5. Chart 测试
- 6. 库 Chart 与子 Chart
- 7. Chart 仓库管理
- 8. Helm vs Kustomize
- 9. 生产环境最佳实践
1. Chart 结构详解
myapp/
├── Chart.yaml # Chart 元数据
├── values.yaml # 默认配置值
├── values-dev.yaml # 开发环境覆盖
├── values-prod.yaml # 生产环境覆盖
├── charts/ # 依赖的 Chart
│ └── postgresql-12.1.0.tgz
├── templates/ # K8s 资源模板
│ ├── _helpers.tpl # 命名辅助函数
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ └── NOTES.txt # 安装后提示信息
└── tests/ # Chart 测试
└── test-connection.yaml
Chart.yaml
apiVersion: v2 # v2 = Helm 3, v1 = Helm 2(已废弃)
name: myapp
description: 我的应用 Helm Chart
type: application # application 或 library
version: 1.2.3 # Chart 版本(语义化)
appVersion: "2.1.0" # 应用版本
kubeVersion: ">= 1.24.0" # 支持的 K8s 版本
home: https://example.com
sources:
- https://github.com/example/myapp
maintainers:
- name: Leeting Yan
email: dev@example.com
dependencies:
- name: postgresql
version: "~12.1.0" # ~ 允许 patch 更新
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
alias: db
- name: redis
version: "18.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
语义化版本约束:
| 约束 | 含义 | 示例 |
|---|---|---|
1.2.3 | 精确版本 | 仅 1.2.3 |
~1.2.3 | 允许 patch 更新 | 1.2.x,x >= 3 |
~1.2 | 允许 patch 和 minor | 1.x.x,x >= 2.0 |
^1.2.3 | 允许不破坏兼容的更新 | 1.x.x |
>= 1.2.3 | 大于等于 | 1.2.3 及以上 |
_helpers.tpl
{{/* 生成全名(包含 release 名) */}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/* 生成标准标签 */}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ include "myapp.chart" . }}
{{ include "myapp.selectorLabels" . }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/* 选择器标签 */}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
2. Values 多环境管理
默认 values.yaml
# values.yaml
replicaCount: 2
image:
repository: registry.example.com/myapp
tag: ""
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: false
className: nginx
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.example.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
# 数据库依赖配置
db:
enabled: true
auth:
username: appuser
password: ""
database: appdb
环境覆盖
# values-dev.yaml(开发环境)
replicaCount: 1
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
ingress:
enabled: true
hosts:
- host: api-dev.example.com
db:
auth:
password: "dev-password"
# values-prod.yaml(生产环境)
replicaCount: 3
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
ingress:
enabled: true
hosts:
- host: api.example.com
tls:
- secretName: api-tls-prod
db:
auth:
password: "" # 生产密码通过 --set 或外部 Secret 注入
安装命令
# 开发环境
helm install myapp-dev ./myapp -f values-dev.yaml
# 生产环境(密码通过命令行注入,不存于文件)
helm install myapp-prod ./myapp \
-f values-prod.yaml \
--set db.auth.password="$(vault read -field=password secret/db)"
# 升级
helm upgrade myapp-prod ./myapp -f values-prod.yaml
# 查看渲染后的 YAML(不实际部署)
helm template myapp ./myapp -f values-prod.yaml | less
# 对比升级差异
helm diff upgrade myapp-prod ./myapp -f values-prod.yaml
3. Go Template 高级语法
条件判断
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "myapp.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
循环与变量
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
# 数组处理
{{- range .Values.service.ports }}
- port: {{ .port }}
targetPort: {{ .targetPort | default .port }}
protocol: {{ .protocol | default "TCP" }}
name: {{ .name }}
{{- end }}
默认值与管道
# 默认值
image: {{ .Values.image.tag | default .Chart.AppVersion }}
# 缩进处理
{{- toYaml .Values.resources | nindent 10 }}
# 引用处理
{{ .Values.database.password | b64enc | quote }}
# 全局值访问
{{ $.Values.global.environment }}
内置函数
| 函数 | 作用 |
|---|---|
default | 设置默认值 |
quote | 加引号 |
upper / lower | 大小写转换 |
b64enc / b64dec | Base64 编码/解码 |
trim / trunc | 截断字符串 |
sha256sum | 计算哈希 |
toYaml / fromYaml | YAML 转换 |
nindent | 换行 + 缩进 |
required | 必填值校验 |
required 校验:
image:
tag: {{ required "image.tag is required" .Values.image.tag }}
若 image.tag 为空,Helm 渲染时报错:
Error: execution error at (myapp/templates/deployment.yaml:15:14): image.tag is required
4. Chart Hook
Hooks 在 Chart 生命周期的特定阶段执行:
| Hook | 触发时机 | 用途 |
|---|---|---|
pre-install | 安装前 | 数据库初始化、前置检查 |
post-install | 安装后 | 发送通知、健康检查 |
pre-delete | 删除前 | 备份数据、优雅关闭 |
post-delete | 删除后 | 清理外部资源 |
pre-upgrade | 升级前 | 数据库迁移、兼容性检查 |
post-upgrade | 升级后 | 验证新版本、清理旧版本 |
pre-rollback | 回滚前 | 备份当前状态 |
post-rollback | 回滚后 | 验证回滚结果 |
test | 测试时 | 连接测试、功能测试 |
Hook 示例:数据库迁移
# templates/hook-migration.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "myapp.fullname" . }}-db-migrate
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ include "myapp.fullname" . }}-db
key: url
权重:hook-weight 数值越小越早执行(默认 0)。可用于控制有依赖关系的 hooks 顺序。
删除策略:
before-hook-creation:创建新的前删除旧的hook-succeeded:成功完成后删除hook-failed:失败后删除- 多个策略用逗号分隔
5. Chart 测试
测试 Pod
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "myapp.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['--timeout=5', '-O-', '{{ include "myapp.fullname" . }}:{{ .Values.service.port }}/healthz']
restartPolicy: Never
执行测试:
helm test myapp-prod
Helm Unittest 框架
更强大的单元测试框架:
# tests/deployment_test.yaml
suite: test deployment
templates:
- deployment.yaml
tests:
- it: should have correct replica count
set:
replicaCount: 5
asserts:
- equal:
path: spec.replicas
value: 5
- it: should mount configmap
asserts:
- contains:
path: spec.template.spec.volumes
content:
name: config
configMap:
name: myapp-config
helm plugin install https://github.com/helm-unittest/helm-unittest.git
helm unittest ./myapp
6. 库 Chart 与子 Chart
库 Chart(Library Chart)
复用通用模板,不部署任何资源:
# Chart.yaml (library)
apiVersion: v2
name: common
type: library
version: 1.0.0
# templates/_deployment.tpl
{{- define "common.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.name }}
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: {{ .Values.name }}
template:
metadata:
labels:
app: {{ .Values.name }}
spec:
containers:
- name: app
image: {{ .Values.image }}:{{ .Values.tag }}
{{- end }}
在主 Chart 中使用:
# Chart.yaml
dependencies:
- name: common
version: "1.x.x"
repository: https://charts.example.com
---
# templates/deployment.yaml
{{- include "common.deployment" . }}
子 Chart(Subcharts)
父 Chart 自动包含 charts/ 目录下的依赖 Chart,通过 values.yaml 配置:
# values.yaml (父 Chart)
# 配置子 Chart postgresql
postgresql:
enabled: true
auth:
username: appuser
password: secret
database: appdb
primary:
persistence:
size: 10Gi
7. Chart 仓库管理
自建 Chart Museum
# 安装 ChartMuseum
helm repo add chartmuseum https://chartmuseum.github.io/charts
helm install chartmuseum chartmuseum/chartmuseum
# 推送 Chart
helm package ./myapp # 打包为 myapp-1.2.3.tgz
helm cm-push myapp-1.2.3.tgz my-repo
# 更新索引
helm repo update
OCI 注册表存储(推荐)
Helm 3.8+ 支持将 Chart 存储在 OCI 注册表中(如 Harbor、Docker Hub):
# 登录 OCI 注册表
helm registry login registry.example.com
# 保存 Chart 为 OCI 格式
helm package ./myapp
# 推送
helm push myapp-1.2.3.tgz oci://registry.example.com/charts
# 安装
helm install myapp oci://registry.example.com/charts/myapp --version 1.2.3
优势:
- 无需单独维护 Chart Museum 服务
- 复用已有的容器镜像仓库基础设施
- Harbor 支持签名验证(cosign)
8. Helm vs Kustomize
| 特性 | Helm | Kustomize |
|---|---|---|
| 核心能力 | 模板化 + 包管理 | 基于覆盖的 YAML 定制 |
| 学习曲线 | 中(Go Template) | 低(纯 YAML) |
| 复用方式 | Chart + Values | Base + Overlay |
| 依赖管理 | 内置 | 无 |
| 条件资源 | 支持(if/else) | 不支持(需手动注释) |
| 多环境 | values-{env}.yaml | overlays/{env}/ |
| 版本管理 | Chart 版本 | Git 版本 |
| 生态系统 | 丰富(ArtifactHub) | K8s 内置 |
选型建议:
- 需要包管理、依赖、版本控制 → Helm
- 只需要环境差异化配置 → Kustomize
- 两者可以结合:Helm 管理依赖,Kustomize 做最终环境覆盖
Helm + Kustomize 结合
# 1. Helm 渲染基础模板
helm template myapp ./myapp -f values-base.yaml > base.yaml
# 2. Kustomize 叠加环境配置
kustomize build overlays/prod/ > final.yaml
# 3. 部署
kubectl apply -f final.yaml
ArgoCD 和 Flux 都支持 Helm + Kustomize 的组合方式。
9. 生产环境最佳实践
版本与发布管理
# 语义化版本发布流程
# 1. 更新版本号
sed -i 's/version: .*/version: 1.3.0/' Chart.yaml
# 2. 更新 CHANGELOG
# 3. 打包
helm package ./myapp
# 4. 签名(可选)
helm package --sign --key 'mykey' ./myapp
# 5. 推送到仓库
helm cm-push myapp-1.3.0.tgz my-repo
# 6. 标记 Git
git tag -a helm-1.3.0 -m "Release Chart 1.3.0"
git push origin helm-1.3.0
安全加固
- Secrets 管理:绝不将真实密码放入 values.yaml,使用 Vault/Sealed Secrets
- 镜像签名:启用 cosign 验证镜像签名
- Chart 签名:用 PGP 签名 Chart,安装时验证
--verify - 最小权限:ServiceAccount 仅授予 Helm/Tiller 所需权限(Helm 3 无需 Tiller,已解决此问题)
CI/CD 集成
# GitHub Actions 示例
- name: Lint Chart
run: helm lint ./myapp
- name: Template Check
run: helm template myapp ./myapp -f values-prod.yaml > /dev/null
- name: Unit Test
run: helm unittest ./myapp
- name: Package
run: helm package ./myapp
- name: Push to Registry
run: |
helm registry login ${{ secrets.REGISTRY }} -u ${{ secrets.USER }} -p ${{ secrets.PASS }}
helm push myapp-*.tgz oci://${{ secrets.REGISTRY }}/charts
总结
| 主题 | 核心要点 |
|---|---|
| Chart 结构 | Chart.yaml + values.yaml + templates/ + charts/ |
| Values 管理 | 默认 + 环境覆盖文件,secret 通过命令行注入 |
| 模板语法 | Go Template + Sprig 函数,_helpers.tpl 复用 |
| Hooks | 生命周期钩子用于迁移、清理、验证 |
| 测试 | helm test + helm unittest 框架 |
| 库 Chart | type: library 复用通用模板 |
| 仓库 | OCI 注册表存储是现代推荐方案 |
| 与 Kustomize | Helm 管模板和依赖,Kustomize 管环境覆盖 |
Helm 不仅是一个部署工具,更是 K8s 应用交付的标准化基石。统一 Chart 规范、自动化测试、与 GitOps 的结合,是实现大规模集群应用管理的关键。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。