Chapter 08

Helm:包管理与模板引擎

Helm 是 Kubernetes 的包管理器,将复杂应用的多个 YAML 打包为 Chart,实现一键安装、升级、回滚,并通过 values.yaml 实现多环境配置。

Helm 解决了什么问题

部署一个完整的应用(如 MySQL)需要创建 Deployment、Service、ConfigMap、Secret、PVC、ServiceAccount 等多个 YAML 文件,还需要按正确顺序 apply。Helm 将这些打包为一个 Chart,让你用一行命令完成整个应用的部署。

没有 Helm

  • 手动管理 10+ 个 YAML 文件
  • 不同环境复制修改 YAML
  • 升级时手动比较 diff
  • 回滚需要记住旧配置
  • 无法复用他人的配置

有了 Helm

  • 一条命令安装/升级/回滚
  • values.yaml 管理环境差异
  • 版本化的发布历史记录
  • Artifact Hub 上万现成 Chart
  • 模板化复用,DRY 原则

Helm 安装

# macOS
brew install helm

# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 验证
helm version

Chart 目录结构

Chart 标准目录结构

my-app/
├── Chart.yaml           Chart 元数据(名称、版本、描述)
├── values.yaml          默认配置值(最重要的文件)
├── charts/              依赖的子 Chart
├── templates/           YAML 模板目录
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── configmap.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl       模板辅助函数(下划线开头不渲染为资源)
│   └── NOTES.txt          安装成功后显示的提示信息
└── .helmignore           类似 .gitignore,打包时排除的文件

Chart.yaml

apiVersion: v2                    # Helm 3 使用 v2
name: my-app                      # Chart 名称
description: A simple web application
type: application                  # application 或 library
version: 0.1.0                    # Chart 版本(SemVer)
appVersion: "1.2.3"              # 应用版本(显示用,不影响部署)
dependencies:                     # 依赖的 Chart
- name: mysql
  version: 9.x.x
  repository: https://charts.bitnami.com/bitnami
  condition: mysql.enabled        # 可选条件,values 中控制是否启用

values.yaml

# 这是模板的默认值,用户可以通过 --values 或 --set 覆盖
replicaCount: 2

image:
  repository: nginx
  tag: "1.25"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false                 # 默认不创建 Ingress
  host: example.com

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

mysql:
  enabled: true
  auth:
    rootPassword: changeme
    database: myapp

模板语法

Helm 使用 Go 模板语言({{ }})将 values.yaml 中的值注入 YAML 模板。

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}     # 调用 _helpers.tpl 中的函数
  labels:
    {{- include "my-app.labels" . | nindent 4 }}  # 缩进 4 空格
spec:
  replicas: {{ .Values.replicaCount }}          # 注入 values
  selector:
    matchLabels:
      {{- include "my-app.selectorLabels" . | nindent 6 }}
  template:
    spec:
      containers:
      - name: {{ .Chart.Name }}                  # Chart 名称
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        resources:
          {{- toYaml .Values.resources | nindent 10 }}  # 对象直接转 YAML
{{- if .Values.ingress.enabled }}              # 条件判断
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "my-app.fullname" . }}
spec:
  rules:
  - host: {{ .Values.ingress.host }}
{{- end }}

Helm 常用命令

仓库管理

# 添加常用仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo update                     # 更新本地缓存

# 搜索 Chart
helm search repo mysql
helm search hub redis               # 从 Artifact Hub 搜索

# 查看 Chart 的 values
helm show values bitnami/mysql

安装与升级

# 安装(第一次部署)
helm install my-release bitnami/mysql \
  --namespace production \
  --create-namespace \
  --values prod-values.yaml \
  --set auth.rootPassword=myStrongPass

# 升级(更新配置或 Chart 版本)
helm upgrade my-release bitnami/mysql \
  --values prod-values.yaml

# install or upgrade(幂等,推荐在 CI 中使用)
helm upgrade --install my-release bitnami/mysql \
  --values prod-values.yaml

# 查看 Release 列表
helm list -A                          # 所有 namespace

# 查看 Release 状态
helm status my-release

# 查看发布历史
helm history my-release

# 回滚到上一版本
helm rollback my-release

# 回滚到指定版本
helm rollback my-release 2

# 卸载
helm uninstall my-release
helm upgrade --install 的重要性

在 CI/CD 流水线中,始终使用 helm upgrade --install 而不是分别判断 install/upgrade。这样无论是首次部署还是更新,命令都能正确执行,保证流水线的幂等性。

创建自定义 Chart

# 创建 Chart 脚手架
helm create my-app

# 检查模板语法(不部署)
helm lint my-app/

# 渲染模板(查看生成的 YAML,不部署)
helm template my-release my-app/ \
  --values my-values.yaml

# 调试安装(--dry-run 只渲染,不实际部署)
helm install my-release my-app/ \
  --dry-run --debug

# 打包 Chart
helm package my-app/              # 生成 my-app-0.1.0.tgz

# 推送到 OCI 仓库(Helm 3.8+)
helm push my-app-0.1.0.tgz oci://registry.example.com/charts

多环境配置管理

# values-dev.yaml / values-staging.yaml / values-prod.yaml
# 每个环境只覆盖不同的部分

# 开发环境部署
helm upgrade --install my-app ./my-app \
  --namespace dev \
  -f values-dev.yaml

# 生产环境部署(基础值 + 生产覆盖值)
helm upgrade --install my-app ./my-app \
  --namespace production \
  -f values.yaml \
  -f values-prod.yaml \        # 后面的 -f 覆盖前面的
  --set image.tag=$CI_COMMIT_SHA  # --set 优先级最高
常见错误:values 中的密码暴露

不要将生产密码写入 values.yaml 并提交到 Git!使用 --set 从 CI/CD 环境变量注入,或配合 Helm Secrets 插件(结合 SOPS 加密)管理敏感 values。在 values.yaml 中只保留空占位符或非敏感默认值。

Chart 依赖管理

复杂应用往往由多个组件组成(如 app + redis + postgresql)。Helm 通过 Chart.yaml 中的 dependencies 字段声明依赖的子 Chart,实现整体打包部署。

# Chart.yaml 中声明依赖
apiVersion: v2
name: my-app
version: 1.0.0
dependencies:
- name: redis
  version: "19.x.x"                # 版本范围约束
  repository: https://charts.bitnami.com/bitnami
  condition: redis.enabled         # 可通过 values 动态启用/禁用
- name: postgresql
  version: "15.x.x"
  repository: https://charts.bitnami.com/bitnami
  alias: db                          # 别名,values 中用 db.xxx 引用
# 下载依赖(生成 charts/ 目录和 Chart.lock 文件)
helm dependency update ./my-app

# 覆盖子 Chart 的 values(格式:子chart名.键名)
helm install my-release ./my-app \
  --set redis.auth.enabled=false \
  --set db.primary.persistence.size=100Gi

# 查看已安装 Release 的实际 values(包含默认值)
helm get values my-release --all

Helm 模板高级技巧

Helm 模板使用 Go template 语法,常用的高级技巧:

# _helpers.tpl:定义可复用的命名模板
{{- define "my-app.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
{{- end }}

# 在其他模板中引用
metadata:
  labels:
    {{- include "my-app.labels" . | nindent 4 }}

# 使用 sha256sum 触发配置变更时的 Pod 重启
annotations:
  checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}

# range 循环生成多个资源
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
  http:
    paths:
    {{- range .paths }}
    - path: {{ .path }}
    {{- end }}
{{- end }}
helm upgrade 的陷阱:CRD 不会自动更新

Helm 在安装 Chart 时会创建 CRD(CustomResourceDefinition),但在 helm upgrade不会自动更新 CRD(这是 Helm 的有意设计,防止破坏性变更)。如果 Chart 升级包含 CRD schema 变更,需要手动 kubectl apply 更新 CRD 后再执行 helm upgrade。

本章小结

Helm 是 K8s 的包管理器,将多个 YAML 文件打包为可版本化、可参数化的 Chart。核心工作流:helm install 部署、helm upgrade 更新、helm rollback 回滚、helm uninstall 卸载。values.yaml 是 Helm 的配置入口,--set 可临时覆盖参数。dependencies 字段管理子 Chart 依赖。生产中的敏感 values 必须从 CI/CD 环境变量注入,不能硬编码到 Git。Chart 依赖需执行 helm dependency update 锁定版本。