为什么需要 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 自动发布。
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(比如数据库迁移、缓存预热),配好了就能实现「先迁库、再发版」的安全顺序。钩子失败会中断发布,比在外部脚本里猜时序可靠得多。
版本回滚与发布策略
Helm 的发布历史让回滚成为一条命令的事:helm rollback 会把 release 恢复到某个历史修订号,Deployment、Service、ConfigMap 一起回到那个状态。生产事故里「先回滚止血、再定位根因」的标准动作,在 Helm 里几秒钟就能完成。
# 查看修订历史,找到上一个可用版本
helm history my-app
# REVISION 1 0.1.0 deployed
# REVISION 2 0.1.1 deployed ← 当前(有问题)
# REVISION 3 0.1.2 superseded ← 发错了
# 回滚到修订 2
helm rollback my-app 2
helm history my-app # 修订 4 出现,内容等于修订 2
回滚策略上记住三点:回滚也是一种发布(会产生新修订号,要继续走审批流程);回滚不等于修 bug(止血后要重放正确的修复版本);数据库 schema 不能靠 Helm 自动回滚(数据变更不可逆,迁移 hooks 要设计成可前滚、可补偿)。配合 Argo CD 等 GitOps 工具时,Helm 管「一次部署」,GitOps 管「期望状态与自动同步」,两者是互补关系。
多环境与 CI 集成
多环境部署是 Helm 的主场:一套 Chart,配上 values-dev.yaml、values-test.yaml、values-prod.yaml,用 -f 切换。values 里环境差异的典型项:镜像 tag、副本数、资源配额、域名、是否启用高可用组件。环境差异值建议显式列全,而不是用覆盖式的稀疏文件,避免「生产里混着开发残留参数」。
CI 里把 Helm 变成发布的一环:流水线构建出镜像 → 更新 values 里的 tag → helm upgrade --install。用 --install 让命令「有则升级、无则安装」,同一段脚本从环境初始化到增量发布都能复用。
# CI 发布步骤(GitHub Actions 示意)
- name: Deploy with Helm
run: |
helm upgrade --install my-app myrepo/my-app \
--version ${{ env.CHART_VERSION }} \
--namespace prod \
-f values-prod.yaml \
--set image.tag=${{ github.sha }} \
--atomic --timeout 5m
helm status my-app
--atomic 让升级失败时自动回滚到上一版本,配合超时时间,避免「半发布状态」挂着没人管。环境多了以后,可以用 Helmfile 这类编排工具把多套 release 声明成文件、一次 diff 一次应用,让「哪套环境该有什么」变成可审查的代码。
一套 Helm 交付清单
把全文收敛成一张能照着执行的清单:
Helm 应用交付清单
1. Chart 模板化 Deployment/Service/ConfigMap 全部走模板
2. values 分环境 dev/test/prod 各一份,显式列全差异项
3. lint + template 发版前本地渲染预览,拒绝「盲发」
4. 版本不可变 打包后不覆盖,升级发新版本号
5. 仓库管理 统一 OCI 仓库,权限跟着镜像体系走
6. hooks 编排 迁移/预热用 pre/post 钩子,失败即中断
7. --atomic 升级 失败自动回滚,配超时兜底
8. 发布历史可查 helm history 留痕,事故可溯源
9. 回滚走流程 回滚也是发布,止血后补正确版本
Helm 的意义不只是少敲几条命令,而是把「应用怎么部署」变成了可版本化、可审查、可回滚的代码资产。结合 GitOps、多环境 values 与 CI 发布门禁,一套 Chart 就能把分散的部署知识沉淀下来;后面的人接手时看到的不是一堆孤立的 YAML,而是一条清晰的交付路径。从最小的 Deployment 开始模板化,把第一条发布历史留起来,这就是值得长期投入的起点。