为什么需要 Helm

Kubernetes 的 YAML 本身没有「变量」概念:同一个应用,开发、测试、生产三套环境,往往要维护三份几乎相同的 Deployment、Service、ConfigMap,改个镜像 tag 或副本数得同时动好几个文件。更麻烦的是,这些 YAML 散落在不同仓库和团队手里,新人接手要翻半天才知道某份配置是谁维护的。

Helm 是 Kubernetes 官方的应用包管理器,核心思路是把一组相关 YAML 打包成 Chart(应用包):不变的部分写成模板,变化的部分抽成 values 参数;用一条命令完成安装、升级、回滚;安装后的每次发布被称为一个 release,Helm 会记录发布历史,出问题可以一键回退。它解决的三个核心痛点:模板化(一份 Chart 到处部署)、可复用(打包进仓库大家共享)、可回滚(发布有历史可追溯)。

# 没有 Helm:三个环境三份 YAML,改一处要同步三处
deploy-dev.yaml    # 副本数 1,镜像 app:dev
deploy-test.yaml   # 副本数 2,镜像 app:test
deploy-prod.yaml   # 副本数 5,镜像 app:1.4.0

# 有 Helm:一份 Chart + 三份 values
helm install app-prod ./app-chart -f values-prod.yaml
helm install app-test ./app-chart -f values-test.yaml
本文核心知识点
  • Chart应用包结构:模板 + values + 元信息。
  • Release一次安装即一个版本,可升级可回滚。
  • 仓库Chart 分发中心,支持 HTTP 与 OCI。
  • CI 集成流水线里 helm upgrade 自动发布。
Helm 应用交付配图:中央舵轮向下分发光线连接一排排小方块应用卡片、右侧圆柱仓库飘出卷轴清单的蓝色扁平科技插画
Helm 像船舵一样掌控应用分发:一处定义,处处部署。

Chart 结构与应用模板

一个 Chart 就是一个目录,标准结构里最重要的是 Chart.yaml(元信息:名称、版本、依赖)、values.yaml(默认参数)、templates/(模板文件,渲染时会把 values 填进去)。模板用 Go template 语法,配合 helpers.tpl 里定义的公共函数(如资源名、标签)保持一致性。

my-app/
├── Chart.yaml          # name: my-app, version: 0.1.0
├── values.yaml         # 默认值:replicaCount、image.tag 等
└── templates/
    ├── deployment.yaml # 模板化 Deployment
    ├── service.yaml
    ├── configmap.yaml
    └── _helpers.tpl    # 公共模板函数

模板里用 . 取参数、include 复用函数、quote 给字符串加引号。写好后可以用 helm lint 校验格式,用 helm template 本地渲染出最终 YAML 预览,不用真部署就能看到结果,这是排错最快的路径。

# templates/deployment.yaml(片段)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "my-app.name" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}

# 本地渲染预览
helm lint ./my-app
helm template my-app ./my-app

仓库管理:Chart 从发布到拉取

模板在本地没意义,打包进仓库才能被团队和 CI 复用。Helm 仓库本质上是一个静态 HTTP 目录,里面放着 .tgz 的 Chart 包和一份 index.yaml 索引;新一代做法是用 OCI 仓库(把 Chart 当容器镜像推送,Harbor 等仓库原生支持),权限与镜像统一管理。打包用 helm package,会在目录里生成 my-app-0.1.0.tgz。

# 打包并推送到 OCI 仓库
helm package ./my-app
helm push my-app-0.1.0.tgz oci://registry.example.com/charts

# 客户端添加仓库并搜索
helm repo add myrepo oci://registry.example.com/charts
helm search repo myrepo/my-app

# 按版本查看与安装
helm show values myrepo/my-app --version 0.1.0
helm install my-app myrepo/my-app --version 0.1.0

仓库管理要注意版本不可变:一个 Chart 版本号打包后就不要覆盖重传,升级内容要发新版本号,否则「同一个版本在不同人手里内容不一样」,溯源和回滚都会乱套。发版前用 helm lint 校验,再配合签名(Chart 也支持 provenance 签名)确认来源。

部署与升级:release 生命周期

helm install 一次安装产生一个 release(发布实例),名字默认随机、也可以显式指定;之后每次 helm upgrade 都是对这个 release 的一次修订。升级时可以用 --set 覆盖单个参数、用 -f 指定 values 文件,也可以用 --reuse-values 沿用上次的值只改个别项——但要注意它可能把不该保留的旧值也带过去,生产里更推荐每次显式给全量 values。

# 安装(生成 release:my-app)
helm install my-app myrepo/my-app --version 0.1.0 -f values-prod.yaml

# 升级(release 修订号 +1)
helm upgrade my-app myrepo/my-app --version 0.1.1 -f values-prod.yaml

# 查看发布状态与历史
helm list
helm status my-app
helm history my-app

升级还有一个常被忽略的环节:hooks。Chart 可以在升级前后挂钩子执行 Job(比如数据库迁移、缓存预热),配好了就能实现「先迁库、再发版」的安全顺序。钩子失败会中断发布,比在外部脚本里猜时序可靠得多。