Helm
本文范围
这篇只讲 Helm 在 K8S 应用交付里的用法:Chart 结构、Chart.yaml、values.yaml、模板、_helpers.tpl、ConfigMap/Secret、Release 生命周期、lint、template、dry-run、升级、回滚、依赖 Chart、Chart 仓库、OCI 仓库、多环境 values 和生产发布检查。
不把 Helm 写成 K8S 教程。Deployment、Service、Ingress、RBAC、镜像仓库、Jenkins 流水线只讲到 Helm 发布会接触到的边界。
前置条件
读这篇之前,建议你已经能用 kubectl 访问一个测试集群,并且能看懂最基础的 Deployment、Service、Ingress YAML。
| 项目 | 要求 |
|---|---|
| Kubernetes | 目标集群可访问,目标 namespace 已规划 |
| Helm | 本地或 CI 机器安装 Helm CLI;生产流水线固定 Helm 版本 |
| 权限 | 发布账号能创建、更新、删除目标 namespace 内的资源 |
| 仓库 | Chart 包、依赖 Chart、业务镜像都能从内网仓库拉取 |
| 校验 | 发布前必须跑 helm lint、helm template、dry-run 和回滚演练 |
截至 2026-07-10,Helm 4 是当前稳定主线,Helm 3 处于维护收尾阶段。生产老项目如果仍在 Helm 3,不要直接替换 CI 里的二进制,先在测试集群验证模板渲染、依赖下载、升级和回滚。
先把场景说清楚
假设我们有一个 Java 服务叫 mall-api,要部署到 K8S。最开始可能就几份 YAML:Deployment、Service、Ingress、ConfigMap。开发环境复制一份,测试环境复制一份,生产环境再复制一份。
一开始看起来很快,后面问题就来了:镜像地址不一样、域名不一样、副本数不一样、JVM 参数不一样、资源限制不一样。再发布几次,谁也说不清生产到底用的是哪份 YAML,某次升级失败以后,也不知道怎么稳稳回到上一版。
Helm 就是用来解决这个问题的。它不替你理解 K8S 基础对象,也不替你设计微服务治理体系,它做的是三件很实在的事:
- 把一组 K8S YAML 做成一个可复用的
Chart。 - 把环境差异放到
values.yaml里管理。 - 把每次安装、升级、回滚记录成一个
Release。
先检查本机和集群,不通就不要急着写 Chart:
kubectl version
kubectl cluster-info
kubectl get nodes
helm version正常情况下,kubectl get nodes 能看到节点列表,helm version 能看到 Helm 客户端版本。如果 kubectl 连不上集群,Helm 后面的命令也会失败;如果 helm version 都没有输出,先安装 Helm CLI。
这里要注意:Helm 最终还是调用 K8S API 创建资源。镜像拉不下来、命名空间不存在、发布账号没权限、节点资源不足,这些问题 Helm 都会暴露出来,但不会自动替你修好。
Helm 到底管什么
可以把 Helm 理解成 K8S 应用的安装包管理器。Linux 里我们用 yum install nginx 或 apt install nginx,K8S 里可以用 Helm 安装一整套应用资源。
几个名字先记住,后面所有命令都围绕它们转:
| 名称 | 通俗理解 | 实战里关注什么 |
|---|---|---|
Chart | 一套 K8S 模板包 | 模板是否可复用,版本是否可追溯 |
values.yaml | 安装参数 | 不同环境差异是否收敛到这里 |
templates/ | K8S YAML 模板目录 | 渲染后是不是合法 YAML、合法 K8S 资源 |
Release | Chart 安装到集群后的实例 | install、upgrade、rollback、uninstall 都操作它 |
Repository | Chart 仓库 | 内网是否可访问,依赖是否能离线闭环 |
一句话记:Chart 管模板,values 管差异,Release 管生命周期,Repo 管分发。
下面这张图适合放在 Chart 设计评审里。评审时不要只问“模板能不能跑”,要从 Chart.yaml、默认 values、环境 values、templates、built-in objects 一路看到最终 manifest、Release 元数据和 K8S 对象;上线前则用 helm lint、helm template、dry-run 和 diff 把变更范围先摊开。
这张图能帮助你把 Helm 问题拆开:helm template 失败,多半是模板或 values;dry-run 失败,可能是权限、API 版本或 CRD;发布后 Pod 不 Ready,就要离开 Helm,继续用 kubectl describe pod、kubectl get events、Service endpoints 和 Ingress/Gateway 状态看 K8S 层。
安装 Helm
截至 2026-07-11,Helm 官方 Release 页面显示 v4.2.3 是当前 Helm 4 最新补丁版本。下面示例使用这个版本,真正上线前请重新检查 Helm release 页面和团队兼容矩阵。
这里用二进制安装最稳,生产流水线也建议固定版本,不要每次临时拉最新版。
HELM_VERSION=v4.2.3
curl -LO "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz"
curl -LO "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz.sha256sum"
sha256sum -c "helm-${HELM_VERSION}-linux-amd64.tar.gz.sha256sum"
tar -zxvf "helm-${HELM_VERSION}-linux-amd64.tar.gz"
sudo mv linux-amd64/helm /usr/local/bin/helm
helm version这几条命令解决两个问题:第一,固定 Helm 版本;第二,下载后做 checksum 校验,避免包损坏或被替换。最后 helm version 应该能看到客户端版本。
如果你们还在 Helm 3,可以固定到团队验证过的版本,例如:
HELM_VERSION=v3.21.1Helm 4 里常用参数有变化,后面的生产命令我会优先写 Helm 4 写法。如果你的环境还是 Helm 3,把 --rollback-on-failure 换成 --atomic,把 --force-replace 换成 --force。
内网环境一般不能指望现场能稳定访问公网。比较稳的做法是:在联网构建机下载 Helm 二进制、checksum、Chart 包和依赖包,然后上传到企业制品库或内网文件服务器。生产 Runner 从内网下载,并打印:
helm version
kubectl version发布记录里有这两个版本,后面排查才知道当时到底用的哪套工具。
创建第一个 Chart
先不要一上来就改生产 Chart,先用 helm create 生成一个脚手架,看清楚目录。
helm create mall-api
tree mall-api -L 2一般会看到这样的结构:
mall-api/
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
ingress.yaml
serviceaccount.yaml
_helpers.tpl
tests/
charts/
.helmignore这些文件分别干什么:
| 文件 | 作用 |
|---|---|
Chart.yaml | Chart 元数据,比如名称、Chart 版本、应用版本、依赖 |
values.yaml | 默认参数,模板里通过 .Values 读取 |
templates/ | 真正的 K8S YAML 模板 |
_helpers.tpl | 放统一命名、label、selector 的公共模板 |
charts/ | 依赖 Chart 下载后一般会放这里 |
.helmignore | 打包 Chart 时忽略本地文件 |
先渲染一下,不连集群也可以看结果:
helm template mall-api ./mall-api > rendered.yaml打开 rendered.yaml,应该能看到 Deployment、Service、ServiceAccount 等资源。这里不是为了马上部署,而是先确认 Helm 能把模板和 values 合成普通 K8S YAML。
再跑一遍 lint:
helm lint ./mall-api正常会看到类似 1 chart(s) linted, 0 chart(s) failed。如果这里失败,先别安装到集群。常见原因是 values.yaml 类型不对、模板缩进错、Chart 元数据缺字段。
Chart.yaml 怎么写
Chart.yaml 可以先保持简单:
apiVersion: v2
name: mall-api
description: Mall API service chart
type: application
version: 0.1.0
appVersion: "1.0.0"这里要注意两个版本:
version是 Chart 版本,代表模板和默认参数的版本。appVersion是应用版本,通常对应镜像 tag 或业务版本。
一般需要这样配合:
Chart version: 0.1.0
appVersion: 1.0.0
image tag: 1.0.0如果只改镜像 tag,不改 Chart 版本,短期看没问题,排查时会很难受。因为你只能看到应用换了,模板有没有换、默认 values 有没有换,不一定能从发布记录里看出来。
生产建议:每次模板结构、默认 values、依赖 Chart 有变化,都升级 Chart.yaml 的 version。只换应用镜像,也要在发布记录里明确记录镜像 tag 或 digest。
values.yaml 管环境差异
values.yaml 不要写成一锅粥。先按应用运行时最常变的东西分组:镜像、副本、Service、Ingress、资源、环境变量、探针。
replicaCount: 2
image:
repository: registry.example.com/backend/mall-api
tag: "1.0.0"
pullPolicy: IfNotPresent
imagePullSecrets: []
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
className: nginx
host: mall-api.example.com
path: /
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
env:
SPRING_PROFILES_ACTIVE: prod
JAVA_TOOL_OPTIONS: "-XX:MaxRAMPercentage=75"
config:
LOG_LEVEL: INFO
FEATURE_NEW_ORDER_FLOW: "false"
probes:
readiness:
path: /actuator/health/readiness
initialDelaySeconds: 20
periodSeconds: 10
liveness:
path: /actuator/health/liveness
initialDelaySeconds: 60
periodSeconds: 20这里有几个实战经验:
- 字符串就加引号,尤其是
"false"、"true"、版本号、镜像 tag。 - 开发默认值可以低一点,生产值单独放文件。
- 敏感信息不要直接写进公开仓库里的 values。
- 不要把一整段 YAML 塞到一个字符串里,模板会非常难排查。
多环境一般这样拆:
values.yaml
values-dev.yaml
values-test.yaml
values-prod.yaml开发环境安装:
helm upgrade --install mall-api ./mall-api \
-n mall-dev \
--create-namespace \
-f values.yaml \
-f values-dev.yaml生产环境安装:
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
--create-namespace \
-f values.yaml \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure \
--history-max 20这条命令做了几件事:没有 Release 就安装,有 Release 就升级;命名空间不存在就创建;生产 values 覆盖默认 values;等待资源就绪;失败时回滚;最多保留 20 条历史。
如果是 Helm 3,把最后一段改成:
--wait \
--timeout 5m \
--atomic \
--history-max 20安装后看结果:
helm list -n mall-prod
helm status mall-api -n mall-prod
helm get values mall-api -n mall-prod --all
kubectl get pods -n mall-prod -l app.kubernetes.io/instance=mall-api -o widehelm list 应该能看到 mall-api,状态一般是 deployed。helm get values --all 可以看到最终生效的 values。Pod 如果不是 Running 或 Ready,继续用 kubectl describe pod 看事件。
values 覆盖顺序
Helm values 有覆盖顺序,排查参数不生效时一定要看这个:
- Chart 自带的
values.yaml。 - 父 Chart 传给子 Chart 的 values。
- 命令行多个
-f文件,后面的覆盖前面的。 --set、--set-string、--set-json覆盖文件里的值。
例如:
helm upgrade --install mall-api ./mall-api \
-n mall-test \
-f values.yaml \
-f values-test.yaml \
--set image.tag=1.0.1最后镜像 tag 以 --set image.tag=1.0.1 为准。
这里要注意:--set 适合临时改一个小参数,比如镜像 tag、开关、replica 数。大段配置不要塞进 --set,否则审查、回滚、复盘都很难。生产更推荐把差异写进 values 文件,再把文件纳入版本管理。
templates 怎么写
Helm 模板就是把 values、Release 信息、Chart 信息填进 K8S YAML。先看一个 Deployment 模板:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mall-api.fullname" . }}
labels:
{{- include "mall-api.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "mall-api.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "mall-api.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}常用对象就这几个:
| 写法 | 含义 |
|---|---|
.Values.image.tag | 读取 values 里的参数 |
.Release.Name | 当前 Release 名称 |
.Release.Namespace | 当前 Release 所在 namespace |
.Release.Revision | 当前发布 revision |
.Chart.Name | Chart 名称 |
.Chart.Version | Chart 版本 |
.Chart.AppVersion | 应用版本 |
.Capabilities.APIVersions.Has | 判断集群是否支持某个 API |
最容易出问题的是缩进。比如 map/list 一般这样写:
resources:
{{- toYaml .Values.resources | nindent 2 }}toYaml 负责把对象转成 YAML,nindent 负责换行并缩进。如果缩进错了,helm template 可能能输出内容,但 kubectl apply 会报 YAML 结构错误。
检查模板:
helm template mall-api ./mall-api -n mall-prod -f values-prod.yaml --debug > rendered.yaml
kubectl apply --dry-run=client -f rendered.yaml第一条命令看 Helm 渲染结果,第二条命令让 kubectl 检查这个 YAML 基本是否合法。这里如果失败,先看 rendered.yaml 对应位置,不要盲目改 values。
如果模板里用了 Ingress、CRD 或集群能力判断,再跑服务端 dry-run:
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml \
--dry-run=server \
--debug \
--hide-secret服务端 dry-run 会连接集群,可以提前暴露权限不足、API 版本不支持、CRD 不存在等问题。--hide-secret 很重要,否则渲染出来的 Secret 可能直接打到日志里。
_helpers.tpl 统一命名和标签
很多人刚写 Chart 时,会在每个模板里手写 metadata.name 和 labels。短期没问题,后期查资源、看日志、监控采集、清理残留时会很痛苦。
建议把命名和标签放到 _helpers.tpl:
{{- define "mall-api.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "mall-api.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name (include "mall-api.name" .) | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- define "mall-api.labels" -}}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
app.kubernetes.io/name: {{ include "mall-api.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "mall-api.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mall-api.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}以后查资源就很方便:
kubectl get all -n mall-prod -l app.kubernetes.io/instance=mall-api
kubectl logs -n mall-prod -l app.kubernetes.io/instance=mall-api --tail=100正常情况下,第一条能看到这个 Release 创建的 Deployment、Pod、Service 等资源。第二条可以直接按 label 看日志。如果查不到,先检查模板里 labels 和 selector 是否一致。
ConfigMap 和 Secret
普通配置可以从 values 渲染成 ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "mall-api.fullname" . }}-config
labels:
{{- include "mall-api.labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.config }}
{{ $key }}: {{ $value | quote }}
{{- end }}Pod 里引用:
envFrom:
- configMapRef:
name: {{ include "mall-api.fullname" . }}-config安装后验证:
kubectl get configmap -n mall-prod
kubectl get configmap mall-api-config -n mall-prod -o yaml如果配置没有生效,先看 ConfigMap 里有没有,再看 Pod 是否重新滚动。应用如果只在启动时读取环境变量,改 ConfigMap 后不重启 Pod 是不会生效的。
Secret 要谨慎。可以让 Chart 引用一个已经存在的 Secret:
externalSecret:
enabled: true
name: mall-api-secret模板里这样用:
envFrom:
- secretRef:
name: {{ .Values.externalSecret.name }}先创建 Secret:
kubectl create secret generic mall-api-secret \
-n mall-prod \
--from-literal=DB_PASSWORD='change-me'验证:
kubectl get secret mall-api-secret -n mall-prod生产建议:密钥优先交给外部密钥系统、SealedSecret、External Secrets Operator 或企业平台管理。Helm values 可以引用 Secret 名称,不要把生产密码写进 Chart 包。
排查时如果需要 dry-run,记得加:
--hide-secretRelease 生命周期
Helm 的好处不是少写几行 YAML,而是每次发布都有历史。先看最常用的几条命令。
第一次安装:
helm install mall-api ./mall-api \
-n mall-prod \
--create-namespace \
-f values-prod.yaml安装后查看:
helm list -n mall-prod
helm status mall-api -n mall-prod
kubectl get pods -n mall-prod -l app.kubernetes.io/instance=mall-api升级:
helm upgrade mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml更常用的是安装和升级合成一条:
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
--create-namespace \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure \
--history-max 20这条命令适合放到流水线里。--wait 会等 Deployment、Pod 等资源达到就绪状态;--timeout 控制最多等多久;--rollback-on-failure 在失败时自动回滚到上一版;--history-max 控制保留历史数量。
升级和回滚要按生命周期看,而不是把 helm upgrade 当成一条孤立命令。下面这张图适合用于发布评审、失败复盘和回滚演练:它把 revision、history、values 合并、hook、K8S 就绪检查、自动回滚、人工回滚和 CRD 边界放在同一张图里。
这张图的落点是上线动作:发布前确认 values 覆盖策略,不要误用 --reuse-values 或 --reset-values;发布中看 helm status/history 和 kubectl rollout status;失败时先保存 helm get values、helm get manifest 和 K8S events,再决定依赖自动回滚还是手工 helm rollback。涉及 CRD 的 Chart 要额外评审,因为 Helm 的生命周期管理边界和普通业务资源不一样。
查看历史:
helm history mall-api -n mall-prod输出里要重点看 REVISION、STATUS、CHART、APP VERSION、DESCRIPTION。如果状态是 failed,不要继续叠加发布,先看失败 revision 的 manifest 和事件。
查看当前生效 values:
helm get values mall-api -n mall-prod --all查看当前渲染后的 manifest:
helm get manifest mall-api -n mall-prod > live-manifest.yaml回滚到上一版:
helm rollback mall-api -n mall-prod回滚到指定 revision:
helm history mall-api -n mall-prod
helm rollback mall-api 12 -n mall-prod --wait --timeout 5m卸载:
helm uninstall mall-api -n mall-prod这里要注意:生产上不要把 uninstall 当成普通清理命令。它可能删除 Deployment、Service、Ingress、ConfigMap、Secret,甚至影响 PVC 相关资源。数据库、中间件、有状态服务尤其要谨慎。
发布前四道检查
一个 Chart 进生产前,至少跑四道检查。不要只在本地看 helm template,也不要直接上生产试。
第一道,检查 Chart 规则和模板基本问题:
helm lint ./mall-api
helm lint ./mall-api -f values-prod.yaml第二道,本地渲染:
helm template mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml \
--debug \
> rendered.yaml第三道,客户端 dry-run:
kubectl apply --dry-run=client -f rendered.yaml第四道,服务端 dry-run:
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml \
--dry-run=server \
--debug \
--hide-secret这四步分别解决不同问题:
| 步骤 | 能发现什么 |
|---|---|
helm lint | Chart 元数据、模板明显错误、values 类型问题 |
helm template | 渲染后到底长什么样 |
kubectl --dry-run=client | YAML 基本结构问题 |
--dry-run=server | 权限、API 版本、CRD、OpenAPI 校验问题 |
如果 CI Runner 能访问目标集群,把这几条固化成门禁:
helm dependency build ./mall-api
helm lint ./mall-api -f values-prod.yaml
helm template mall-api ./mall-api -n mall-prod -f values-prod.yaml > rendered.yaml
kubectl apply --dry-run=client -f rendered.yaml
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml \
--dry-run=server \
--hide-secret任何一步失败,都回到 Chart 或 values 修复。不要靠正式发布去试错。
多环境 values 实战
目录可以这样放:
mall-api/
Chart.yaml
values.yaml
values-dev.yaml
values-test.yaml
values-prod.yaml
templates/values.yaml 放通用默认值:
replicaCount: 1
image:
repository: registry.example.com/backend/mall-api
tag: "dev"
pullPolicy: IfNotPresent
ingress:
enabled: false
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mivalues-prod.yaml 只放生产差异:
replicaCount: 4
image:
repository: harbor.company.local/backend/mall-api
tag: "2026.07.09-001"
pullPolicy: IfNotPresent
ingress:
enabled: true
className: nginx
host: mall-api.company.com
path: /
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
env:
SPRING_PROFILES_ACTIVE: prod
JAVA_TOOL_OPTIONS: "-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"发布前先看最终合成结果:
helm template mall-api ./mall-api \
-n mall-prod \
-f values.yaml \
-f values-prod.yaml \
--debug \
> rendered-prod.yaml确认镜像地址:
grep -n "image:" rendered-prod.yaml生产不建议用 latest。镜像 tag 至少要能追溯到构建号、Git commit、制品版本或日期批次。更严格的环境记录镜像 digest:
docker inspect --format='{{index .RepoDigests 0}}' harbor.company.local/backend/mall-api:2026.07.09-001Helm 管 Chart 和 Release,镜像仓库管镜像制品。回滚时两边都要能找回历史版本。
依赖 Chart
一个业务 Chart 可能依赖 Redis、MySQL、Nacos、网关或通用 library chart。依赖写在 Chart.yaml:
dependencies:
- name: redis
version: "20.6.3"
repository: "oci://harbor.company.local/charts"
condition: redis.enabled更新依赖:
helm dependency update ./mall-api这条命令会解析依赖,把依赖包下载到 charts/,并生成或更新 Chart.lock。
生产发布前更推荐:
helm dependency build ./mall-apidependency build 会按 Chart.lock 还原依赖,适合 CI 和生产发布。这里要注意:不要在生产发布现场临时连公网下载依赖。依赖 Chart 应该提前进入内网仓库,版本锁在 Chart.lock 里。
查看依赖:
helm dependency list ./mall-api
ls -lh ./mall-api/charts如果这里失败,常见原因是仓库地址不可达、认证失败、依赖版本不存在、Chart.lock 和 Chart.yaml 不一致。
Chart 仓库方案
Helm Chart 有两种常见分发方式:传统 HTTP Chart 仓库和 OCI Registry。
传统 HTTP 仓库一般有一个 index.yaml:
helm package ./mall-api --destination dist/charts
helm repo index dist/charts --url https://charts.example.com客户端添加仓库:
helm repo add company https://charts.example.com
helm repo update
helm search repo company/mall-apiOCI 方式更像推镜像,适合 Harbor、云厂商镜像仓库、企业统一制品库:
helm package ./mall-api --destination dist/charts
helm registry login harbor.company.local
helm push dist/charts/mall-api-0.1.0.tgz oci://harbor.company.local/charts
helm pull oci://harbor.company.local/charts/mall-api --version 0.1.0安装:
helm upgrade --install mall-api oci://harbor.company.local/charts/mall-api \
--version 0.1.0 \
-n mall-prod \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure内网选型可以这么看:
| 场景 | 推荐方案 |
|---|---|
| 已经有 Harbor | 优先用 Harbor OCI 存 Chart 和镜像 |
| 已经有 Nexus | 用 Nexus Helm hosted/proxy/group 管理传统 Chart 仓库 |
| 只想快速搭一个 Chart 仓库 | ChartMuseum 可以作为轻量方案 |
| 云上已有企业镜像仓库 | 使用云厂商支持的 OCI Helm Chart |
| 完全离线 | 直接传 .tgz 包和镜像 tar 包 |
这里要注意:Chart 包能下载不代表应用能启动。Chart 里引用的镜像也必须在内网仓库或每个节点本地存在。
生产升级命令模板
生产发布不要只写:
helm upgrade mall-api ./mall-api太短了。出了问题时,你不知道它用哪个 namespace、哪个 values、等没等就绪、失败后有没有回滚、历史保留多少。
建议写完整:
helm upgrade --install mall-api ./mall-api \
--namespace mall-prod \
--create-namespace \
-f values.yaml \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure \
--history-max 20 \
--description "deploy mall-api 2026.07.09-001"如果是 Helm 3:
helm upgrade --install mall-api ./mall-api \
--namespace mall-prod \
--create-namespace \
-f values.yaml \
-f values-prod.yaml \
--wait \
--timeout 5m \
--atomic \
--history-max 20 \
--description "deploy mall-api 2026.07.09-001"发布后马上看:
helm status mall-api -n mall-prod
helm history mall-api -n mall-prod
kubectl rollout status deployment/mall-api -n mall-prod --timeout=5m
kubectl get pods -n mall-prod -l app.kubernetes.io/instance=mall-api -o wide
kubectl get svc,endpoints,ingress -n mall-prod访问健康检查:
curl -fsS https://mall-api.company.com/actuator/health
curl -fsS https://mall-api.company.com/actuator/health/readiness如果只能在集群内访问,可以临时起一个 curl Pod。内网环境要把镜像换成自己的工具镜像:
kubectl run curl-test -n mall-prod --rm -it --image=curlimages/curl -- \
curl -fsS http://mall-api:8080/actuator/health访问后应该看到健康状态。如果 curl 超时,先看 Service 和 endpoints,不要马上怀疑 Helm。
回滚手册
Helm 回滚不是一句命令完事。现场一般按这四步来:确认目标版本、执行回滚、观察 K8S 状态、验证业务入口。
先看历史:
helm history mall-api -n mall-prod找到上一个 deployed 或确认过的稳定 revision,然后回滚:
helm rollback mall-api 12 -n mall-prod --wait --timeout 5m回滚后检查 Helm 状态:
helm status mall-api -n mall-prod
helm history mall-api -n mall-prod检查 K8S 状态:
kubectl rollout status deployment/mall-api -n mall-prod --timeout=5m
kubectl get pods -n mall-prod -l app.kubernetes.io/instance=mall-api -o wide
kubectl describe deployment mall-api -n mall-prod确认镜像已经回到预期版本:
kubectl get deployment mall-api -n mall-prod \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'确认 Service 后面有 Pod:
kubectl get endpoints mall-api -n mall-prod -o wide最后做业务验证:
curl -fsS https://mall-api.company.com/actuator/health这里要注意:Helm 回滚能回滚 K8S 资源和配置,不能自动回滚数据库数据。如果这次升级带了不可逆 DDL、数据迁移、外部缓存结构变化,只回滚 Helm 可能不够。生产预案里要写清楚应用回滚、数据库回滚、配置回滚分别怎么做。
保存故障版本,方便复盘:
mkdir -p release-debug/mall-api
helm get values mall-api -n mall-prod --revision 13 --all > release-debug/mall-api/values-r13.yaml
helm get manifest mall-api -n mall-prod --revision 13 > release-debug/mall-api/manifest-r13.yaml
helm get values mall-api -n mall-prod --revision 12 --all > release-debug/mall-api/values-r12.yaml
helm get manifest mall-api -n mall-prod --revision 12 > release-debug/mall-api/manifest-r12.yaml
diff -u release-debug/mall-api/values-r12.yaml release-debug/mall-api/values-r13.yaml
diff -u release-debug/mall-api/manifest-r12.yaml release-debug/mall-api/manifest-r13.yaml复盘时不要只写“服务已恢复”。要知道到底是镜像问题、模板问题、values 问题、权限问题还是集群资源问题。
权限和命名空间边界
Helm 使用当前 kubeconfig 的权限操作集群。生产上不要用集群管理员账号跑所有发布。一般按团队或应用拆 namespace,再给发布账号最小权限。
如果没有 namespace 就新建:
kubectl create namespace mall-prod创建发布账号:
apiVersion: v1
kind: ServiceAccount
metadata:
name: helm-deployer
namespace: mall-prodRole 示例:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: helm-deployer
namespace: mall-prod
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets", "services", "serviceaccounts", "pods", "pods/log"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: helm-deployer
namespace: mall-prod
subjects:
- kind: ServiceAccount
name: helm-deployer
namespace: mall-prod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: helm-deployer验证权限:
kubectl auth can-i create deployment -n mall-prod --as=system:serviceaccount:mall-prod:helm-deployer
kubectl auth can-i patch secret -n mall-prod --as=system:serviceaccount:mall-prod:helm-deployer
kubectl auth can-i create clusterrole --as=system:serviceaccount:mall-prod:helm-deployer前两条按需应该是 yes,最后一条一般应该是 no。业务应用 Chart 尽量不要创建 ClusterRole、CRD、Webhook 这类集群级资源。确实需要时,单独评审,不要混在普通应用发布里。
Helm 的 Release 元数据默认以 Secret 存在目标 namespace:
kubectl get secrets -n mall-prod -l owner=helm这些 Secret 记录了历史 revision。不要随便清理,否则 Helm 可能找不到历史版本,回滚能力会受影响。
中国大陆、内网和离线方案
国内和内网环境落 Helm,重点不是某条命令,而是三类东西都要闭环:
- Helm CLI。
- Chart 包和依赖 Chart。
- 容器镜像。
只同步 Chart,不同步镜像,Pod 会卡在 ImagePullBackOff。只同步镜像,不处理依赖 Chart,helm dependency build 会失败。只准备包,不做 checksum,现场出问题很难判断是包坏了还是版本错了。
联网构建机准备包
先在联网构建机把 Chart 和依赖准备好:
helm version
helm dependency build ./mall-api
helm lint ./mall-api -f values-prod.yaml
helm package ./mall-api --destination dist/charts复制依赖和锁文件:
mkdir -p dist/charts/deps
cp mall-api/charts/*.tgz dist/charts/deps/
cp mall-api/Chart.lock dist/charts/准备镜像:
docker pull registry.example.com/backend/mall-api:2026.07.09-001
docker save -o dist/images/mall-api-2026.07.09-001.tar \
registry.example.com/backend/mall-api:2026.07.09-001生成校验文件:
sha256sum dist/images/*.tar > dist/images/SHA256SUMS
sha256sum dist/charts/*.tgz dist/charts/deps/*.tgz > dist/charts/SHA256SUMS离线现场先校验:
sha256sum -c dist/images/SHA256SUMS
sha256sum -c dist/charts/SHA256SUMS校验通过再导入或发布。如果 checksum 不通过,不要继续安装,先重新传包。
内网 OCI 仓库
如果内网有 Harbor 或兼容 OCI 的仓库,优先把 Chart 和镜像都推进去。
推 Chart:
helm registry login harbor.company.local
helm push dist/charts/mall-api-0.1.0.tgz oci://harbor.company.local/charts推镜像:
docker load -i dist/images/mall-api-2026.07.09-001.tar
docker tag registry.example.com/backend/mall-api:2026.07.09-001 \
harbor.company.local/backend/mall-api:2026.07.09-001
docker push harbor.company.local/backend/mall-api:2026.07.09-001生产 values 使用内网地址:
image:
repository: harbor.company.local/backend/mall-api
tag: "2026.07.09-001"
pullPolicy: IfNotPresent安装:
helm upgrade --install mall-api oci://harbor.company.local/charts/mall-api \
--version 0.1.0 \
-n mall-prod \
--create-namespace \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure如果安装报认证失败,先分别验证 Chart 仓库和镜像仓库:
helm registry login harbor.company.local
helm pull oci://harbor.company.local/charts/mall-api --version 0.1.0
docker login harbor.company.local
docker pull harbor.company.local/backend/mall-api:2026.07.09-001完全离线无仓库
如果现场没有 Chart 仓库,可以直接使用 .tgz:
helm upgrade --install mall-api dist/charts/mall-api-0.1.0.tgz \
-n mall-prod \
--create-namespace \
-f values-prod.yaml \
--wait \
--timeout 5m \
--rollback-on-failure镜像如果不推私有仓库,而是每个节点导入,要先确认容器运行时。
Docker 节点:
docker load -i mall-api-2026.07.09-001.tar
docker images | grep mall-apicontainerd 节点:
sudo ctr -n k8s.io images import mall-api-2026.07.09-001.tar
sudo crictl images | grep mall-api所有可能调度到 Pod 的节点都要导入。只导入一台节点没有用,Pod 调度到别的节点照样起不来。
如果确认所有节点都有镜像,values 可以写:
image:
repository: registry.example.com/backend/mall-api
tag: "2026.07.09-001"
pullPolicy: NeverNever 只适合完全离线且每个节点都已经有镜像。更通用的生产策略是内网镜像仓库加 IfNotPresent。
常见故障排查
1. helm lint 报 YAML 错
现象:
error converting YAML to JSON先跑:
helm lint ./mall-api -f values-prod.yaml
helm template mall-api ./mall-api -f values-prod.yaml --debug > rendered.yaml看 rendered.yaml 报错附近的缩进。常见原因是 toYaml 没配 nindent,或者 values 里把字符串写成了布尔值、数字。
修复方向:
value: {{ .Values.config.FEATURE_NEW_ORDER_FLOW | quote }}
resources:
{{- toYaml .Values.resources | nindent 2 }}生产建议加 values.schema.json,提前约束参数类型。
2. template 成功,install 失败
现象:
Error: INSTALLATION FAILED: unable to build kubernetes objects先跑服务端 dry-run:
helm upgrade --install mall-api ./mall-api \
-n mall-prod \
-f values-prod.yaml \
--dry-run=server \
--debug \
--hide-secret继续验证权限和 API:
kubectl auth can-i create deployment -n mall-prod
kubectl api-resources | grep -i ingress
kubectl get crd常见原因:账号没权限、namespace 不存在、Ingress API 版本不匹配、CRD 没安装。
3. Release 状态是 failed
先看 Helm:
helm status mall-api -n mall-prod
helm history mall-api -n mall-prod
helm get manifest mall-api -n mall-prod > failed-manifest.yaml再看 K8S 事件:
kubectl get events -n mall-prod --sort-by=.lastTimestamp
kubectl get pods -n mall-prod -l app.kubernetes.io/instance=mall-api
kubectl describe pod -n mall-prod -l app.kubernetes.io/instance=mall-api如果事件里是 ImagePullBackOff,看镜像;如果是 FailedScheduling,看资源、亲和性、PVC;如果是探针失败,看应用日志和健康检查路径。
4. Pod 卡在 ImagePullBackOff
排查:
kubectl describe pod <pod-name> -n mall-prod
kubectl get secret -n mall-prod
kubectl get serviceaccount -n mall-prod -o yaml重点看:
- 镜像地址是不是内网仓库。
- 镜像 tag 是否存在。
- 节点是否能访问镜像仓库。
imagePullSecrets是否配置到 Pod 或 ServiceAccount。- 离线节点是否已经导入镜像。
如果是私有仓库认证问题,补 Secret:
kubectl create secret docker-registry harbor-pull-secret \
-n mall-prod \
--docker-server=harbor.company.local \
--docker-username=robot \
--docker-password='change-me'values 里引用:
imagePullSecrets:
- name: harbor-pull-secret5. Pod 一直 Pending
排查:
kubectl describe pod <pod-name> -n mall-prod
kubectl describe node <node-name>
kubectl get pvc -n mall-prod
kubectl get pv常见原因:
resources.requests太高,节点资源不够。nodeSelector或 affinity 没有匹配节点。- PVC 没有绑定 PV。
- 节点有 taint,Pod 没有 toleration。
如果是资源请求过高,先在 values 调整:
resources:
requests:
cpu: 500m
memory: 512Mi再升级:
helm upgrade mall-api ./mall-api -n mall-prod -f values-prod.yaml --wait --timeout 5m6. Pod Running,但访问不通
先不要急着回滚,按链路查:
kubectl get svc -n mall-prod
kubectl get endpoints mall-api -n mall-prod -o wide
kubectl get ingress -n mall-prod
kubectl describe ingress mall-api -n mall-prod再看探针和日志:
kubectl logs -n mall-prod -l app.kubernetes.io/instance=mall-api --tail=200
kubectl describe pod -n mall-prod -l app.kubernetes.io/instance=mall-api重点看 Service 的 selector 是否能选中 Pod、containerPort 和 service.port 是否一致、readinessProbe 是否失败导致 endpoints 为空、IngressClass 是否存在。
7. rollback 后还是不正常
先确认是否真的回到了目标 revision:
helm history mall-api -n mall-prod
helm get values mall-api -n mall-prod --all
kubectl get deployment mall-api -n mall-prod \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'如果镜像回去了,服务还不正常,继续看外部依赖:
kubectl logs -n mall-prod -l app.kubernetes.io/instance=mall-api --tail=200
kubectl get configmap,secret -n mall-prod
kubectl get endpoints -n mall-prod常见原因是数据库变更不可逆、配置中心已经变更、缓存结构变更、外部服务接口不兼容。Helm 只能回滚 K8S 资源,不能替你回滚业务状态。
最佳实践
Chart 设计:
- 模板里少写死值,差异走 values。
metadata.labels和 selector 用_helpers.tpl统一。- values 默认值能在开发环境跑通,但生产参数单独维护。
- 镜像 tag 不用
latest。 - Secret 优先引用外部已有资源,不把生产密钥写进 Chart。
- 依赖 Chart 固定版本,提交
Chart.lock。
发布治理:
- 生产发布必须带
--namespace。 - 生产发布必须带
--wait和--timeout。 - Helm 4 用
--rollback-on-failure,Helm 3 用--atomic。 - 发布描述写清楚业务版本、镜像 tag、变更单或流水线号。
helm history至少保留 10 到 20 条。- 回滚前确认镜像、Chart 包、values 都还能找回。
内网离线:
- Helm CLI、Chart 包、依赖 Chart、镜像 tar 包一起准备。
- 所有包生成 checksum,现场先校验再安装。
- 优先建设内网 OCI 仓库,Chart 和镜像统一治理。
- 完全离线时确认每个调度节点都有镜像。
- 不把公网 Chart 仓库作为生产发布时依赖。
排障习惯:
- 先
helm status/history/get manifest/get values,再看 K8S 事件。 - 模板问题看
helm template输出。 - 权限问题用
kubectl auth can-i。 - 镜像问题看
describe pod里的事件。 - 访问问题看 Service、endpoints、Ingress、readinessProbe。
落地工程深水区
Helm 深水区在于:它让发布更标准,也会把错误模板标准化地发到所有环境。
values 治理:环境差异只能进 values,不能散落在模板里。生产 values 必须入库或进入受控配置仓库,禁止临时在 Jenkins 输入框里手写一长串 --set。发布记录要能找回当时的 Chart 包、values 文件、镜像 tag/digest。
模板安全:helm template 成功不代表能创建资源。模板要继续过 K8S schema、权限和服务端 dry-run:
helm lint ./mall-api
helm template mall-api ./mall-api -f values-prod.yaml > rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
kubectl auth can-i create deployment -n mall-prod升级和回滚:helm rollback 只回滚 Kubernetes manifest,不会自动回滚数据库结构、外部配置、消息兼容和缓存数据。只要版本涉及数据结构变化,就要单独写前滚/回滚策略。
依赖和离线:依赖 Chart、OCI 仓库、镜像仓库要一起离线闭环。只把业务 Chart 打包出去,现场仍然会卡在依赖 Chart 或镜像拉取上。
权限和多团队协作:每个团队只发布自己的 namespace。平台团队维护公共 _helpers.tpl、基础 Chart 和命名规范;业务团队维护应用 values。不要让每个服务都复制一套难以对齐的模板。
长期维护:Chart 版本、应用版本和镜像版本要各自清楚。模板结构变更升级 Chart.yaml 的 version,业务镜像变更记录 appVersion 或发布元数据,不能全靠一个 latest 或一个 Jenkins build number 硬撑。
常用命令速查
创建 Chart:
helm create mall-api检查 Chart:
helm lint ./mall-api
helm lint ./mall-api -f values-prod.yaml渲染模板:
helm template mall-api ./mall-api -n mall-prod -f values-prod.yaml --debug安装或升级:
helm upgrade --install mall-api ./mall-api -n mall-prod -f values-prod.yaml --wait --timeout 5m查看 Release:
helm list -n mall-prod
helm status mall-api -n mall-prod
helm history mall-api -n mall-prod查看 values 和 manifest:
helm get values mall-api -n mall-prod --all
helm get manifest mall-api -n mall-prod回滚:
helm rollback mall-api 12 -n mall-prod --wait --timeout 5m卸载:
helm uninstall mall-api -n mall-prod依赖:
helm dependency update ./mall-api
helm dependency build ./mall-api
helm dependency list ./mall-api打包:
helm package ./mall-api --destination dist/chartsOCI 登录、推送、拉取:
helm registry login harbor.company.local
helm push dist/charts/mall-api-0.1.0.tgz oci://harbor.company.local/charts
helm pull oci://harbor.company.local/charts/mall-api --version 0.1.0发布检查清单
发布前:
发布后:
如果失败:
官方文档入口
生产落地时,建议把这些入口加入团队发布手册:
- Helm Overview:https://helm.sh/docs/overview/
- Installing Helm:https://helm.sh/docs/intro/install/
- Charts:https://helm.sh/docs/topics/charts/
- Chart Template Guide:https://helm.sh/docs/chart_template_guide/
- helm install:https://helm.sh/docs/helm/helm_install/
- helm upgrade:https://helm.sh/docs/helm/helm_upgrade/
- OCI Registries:https://helm.sh/docs/topics/registries/
- Role Based Access Control:https://helm.sh/docs/topics/rbac/
- Harbor Helm OCI Charts:https://goharbor.io/docs/main/working-with-projects/working-with-oci/working-with-helm-oci-charts/
Helm 真正适合落地的方式,不是把 YAML 换成一堆模板语法,而是让部署包、环境参数、发布历史、回滚预案和内网制品一起可追溯。先把一个服务做扎实,再推广到更多服务,这样 Helm 才会变成标准化部署能力,而不是另一套没人敢改的模板目录。
