Helm 管理 chart 的 release 历史,Kubernetes 管理运行资源。先记录集群、namespace、release、chart 版本与 values 来源,才能解释“升级成功但行为没变”。示例使用本地 ./chart,执行前换成经过审阅的 chart。
四种配置视图不要混用
固定版本与发布对象
helm version --short
下文 your-context、default、web 分别是 context、namespace、release 的示例,每条命令显式指定。用 kubectl config get-contexts 核对 context;不要将 chart 名误当成 release 名。
helm list --kube-context your-context -n default --all
查看 NAME、REVISION、STATUS、CHART 与 APP VERSION;CHART 版本和应用版本不是同一个概念。
helm status web --kube-context your-context -n default
deployed 表示 Helm 记录的发布状态,不代替 Pod / 业务检查。pending 时先查仍在运行的发布任务。
按问题选择配置视图
helm show values ./chart
./chart 是准备发布的本地 chart 目录,不是 release 当前实际值。
helm get values web --kube-context your-context -n default
未设置过覆盖值可能为空;需要合并后值时,另选下面的 --all 查询。
helm get values web --all --kube-context your-context -n default
确认目标字段的最终值;这些是该 release 配置,不是控制器当前内存状态。输出可能含敏感配置,分享前检查。
helm get manifest web --kube-context your-context -n default
与 kubectl --context your-context -n default get deployment web -o yaml 对照:API 对象可能经过默认化、控制器或人工修改。对象名依 chart 而定,从清单取得。
| 来源 | 回答的问题 |
| helm show values ./chart | chart 作者提供的默认值 |
| helm get values RELEASE | release 存储的用户覆盖值;--all 显示合并结果 |
| helm get manifest RELEASE | 该 revision 生成并保存的清单 |
| kubectl get ... -o yaml | API 当前对象,可能被默认化、控制器或人工修改 |
values 的覆盖顺序与发布验证
正在呈现知识画面
同一个键的来源,列表的整体替换Chart 默认值被右侧启用的 values 文件和 CLI 覆盖;对象可按键合并,列表是整体替换,不逐项追加。
{"id":"ops-helm-values","title":"同一个键的来源,列表的整体替换","summary":"Chart 默认值被右侧启用的 values 文件和 CLI 覆盖;对象可按键合并,列表是整体替换,不逐项追加。","height":600,"html":"<main><p class=\"quiet\" data-i18n=\"scope\"></p><div class=\"layers\"><div class=\"row code\">defaults\nreplicas: 1\nimage: {repository: app, tag: v1}\nports: [80, 443]</div><div class=\"row code\">prod.yaml\nreplicas: 2\nimage: {tag: v2}\nports: [8080]</div></div><div class=\"checks\"><label><input id=\"file\" type=\"checkbox\" checked><span data-i18n=\"file\"></span></label><label><input id=\"cli\" type=\"checkbox\" ><span data-i18n=\"cli\"></span></label></div><section class=\"row\"><h3 data-i18n=\"result\"></h3><pre class=\"code\" id=\"values\"></pre></section><div class=\"explain\"><div class=\"stable \"><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"hint\"></p><p id=\"message\" aria-live=\"polite\"></p></div></div></main>\n","css":"main {\n display: grid;\n gap: 18px;\n min-width: 0;\n}\np {\n overflow-wrap: anywhere;\n}\n.controls {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n.presets {\n display: flex;\n flex-wrap: wrap;\n}\n.presets button {\n flex: 1 1 85px;\n font-size: 15px;\n}\n.cards {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n}\n.card {\n border: 1px solid var(--rule);\n border-radius: 10px;\n padding: 14px;\n min-width: 0;\n background: var(--surface);\n}\n.card h3 {\n font-size: 15px;\n color: var(--muted);\n margin-bottom: 8px;\n}\n.card strong {\n font-size: 23px;\n font-weight: 500;\n overflow-wrap: anywhere;\n}\n.code {\n font-family: var(--viz-font-mono);\n font-size: 15px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.stable {\n display: grid;\n min-width: 0;\n}\n.stable > * {\n grid-area: 1/1;\n margin: 0;\n}\n.stable > .reserve {\n visibility: hidden;\n pointer-events: none;\n}\n.explain {\n border-left: 3px solid var(--accent);\n padding: 12px 14px;\n background: var(--accent-soft);\n}\n.arrow {\n text-align: center;\n color: var(--accent);\n font-size: 25px;\n}\n.row {\n padding: 12px;\n border: 1px solid var(--rule);\n border-radius: 8px;\n min-width: 0;\n}\n.row.on {\n border-color: var(--accent);\n background: var(--accent-soft);\n}\n.row.off {\n color: var(--muted);\n}\n.bar {\n height: 18px;\n background: var(--surface);\n border: 1px solid var(--rule);\n border-radius: 5px;\n overflow: hidden;\n margin-top: 8px;\n}\n.bar > i {\n display: block;\n height: 100%;\n background: var(--accent);\n width: 0;\n transition: width 0.22s;\n}\n.checks {\n display: grid;\n gap: 8px;\n}\n.checks label {\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 9px;\n border: 1px solid var(--rule);\n border-radius: 7px;\n font-size: 16px;\n}\n.checks input {\n width: 18px;\n height: 18px;\n accent-color: var(--accent);\n flex-shrink: 0;\n}\nlabel.range {\n display: block;\n font-size: 16px;\n}\nlabel.range output {\n float: right;\n font-variant-numeric: tabular-nums;\n}\n.pair {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.flow {\n display: grid;\n gap: 6px;\n}\n.quiet {\n font-size: 14px;\n color: var(--muted);\n}\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 5px;\n background: var(--accent-soft);\n color: var(--accent);\n}\nbutton {\n overflow-wrap: anywhere;\n}\n.large {\n font-size: 32px;\n color: var(--accent);\n font-weight: 500;\n}\n.layers {\n display: grid;\n gap: 12px;\n}\n.layer {\n border-left: 3px solid var(--rule);\n padding-left: 12px;\n}\n.layer.on {\n border-color: var(--accent);\n}\n@media (max-width: 520px) {\n .cards,\n .pair {\n grid-template-columns: 1fr;\n }\n .card {\n padding: 12px;\n }\n .card strong {\n font-size: 22px;\n }\n}\n.code {\n white-space: pre-wrap;\n}\n#values {\n min-height: 11em;\n margin: 8px 0 0;\n}\n","js":"const $ = (s) => document.querySelector(s);\nconst $$ = (s) => [...document.querySelectorAll(s)];\nconst t = (k) => viz.t(k);\nconst put = (id, v) => ($(\"#\" + id).textContent = v);\nconst say = (k) => put(\"message\", t(k));\nconst state = (v) => ($(\"main\").dataset.state = JSON.stringify(v));\nconst press = (key, v) =>\n $$(\"[data-\" + key + \"]\").forEach((b) =>\n b.setAttribute(\"aria-pressed\", String(b.dataset[key] === v)),\n );\nfunction draw() {\n const file = $(\"#file\").checked,\n cli = $(\"#cli\").checked,\n replicas = cli ? 3 : file ? 2 : 1,\n tag = file ? \"v2\" : \"v1\",\n ports = file ? [8080] : [80, 443];\n put(\n \"values\",\n \"replicas: \" +\n replicas +\n \" ← \" +\n (cli ? \"CLI\" : file ? \"prod.yaml\" : \"defaults\") +\n \"\\nimage.repository: app ← defaults\\nimage.tag: \" +\n tag +\n \" ← \" +\n (file ? \"prod.yaml\" : \"defaults\") +\n \"\\nports: [\" +\n ports.join(\", \") +\n \"]\",\n );\n state({ file, cli, replicas, tag, ports });\n}\n$$(\"input\").forEach((e) => (e.onchange = draw));\nsay(\"hint\");\ndraw();\n","audio":false,"strings":{"cli":"启用 --set replicas=3","file":"启用 prod.yaml","hint":"prod.yaml 只修改 image.tag,image.repository 保留;ports 列表则完全替换。最右侧显式值赢得同名键。","result":"最终 values","scope":"受限 values 示例,不模拟模板、null 删除或 --reuse-values。勾选来源,观察最终每个值。"}}
选择本次 values 的来源
下面使用“chart 默认值 + 本次 values.yaml”,明确 --reset-values;需要继承旧值时应先导出、审阅并纳入本次输入,而不是无意保留上次配置。值文件和 chart 一起纳入版本管理,敏感值按项目密钥方案处理。
helm lint ./chart -f values.yaml
先解决模板/结构错误;lint 不能证明集群接纳清单或应用启动成功。
helm template web ./chart -n default -f values.yaml > rendered.yaml
目标文件会被覆盖,使用本次工作目录。检查镜像、端口、namespace、存储与 Secret;不应直接把完整输出贴入工单。
kubectl --context your-context -n default diff -f rendered.yaml
返回 1 通常表示有差异。此处只借用 kubectl 比较;实际发布仍由 Helm 执行,避免两套管理方式交错写入。
helm upgrade --install web ./chart \
--kube-context your-context -n default \
-f values.yaml --reset-values --wait --timeout 5m
namespace 需已存在;--install 允许首次安装,--wait/--timeout 控制等待,不是业务测试或自动回滚。完成后检查 helm status、Pod Events 和实际请求;失败先保留证据并进入恢复流程。
这里明确从 chart 默认值和本次 values 构建配置,不无意继承旧 release 的值。多个 -f 按从左到右覆盖,--set 优先级更高;列表通常整体替换,不能把数组当成按元素补丁。通过仓库或 OCI 安装时固定 chart 版本,并审阅来源。
template 是本地渲染,不证明 API 支持、admission 接受或运行成功;lookup 等依赖集群的模板还会有差异。upgrade --dry-run 的客户端/服务端形式随版本确认,输出可能含 Secret。Helm 3 的 --atomic 与 Helm 4 的失败回滚选项不同,先看本机 helm upgrade --help,不要混用版本参数。
回滚 release,不等于回滚全部业务状态
回滚前读历史与目标输入
helm history web --kube-context your-context -n default
不要假定 revision 2 正常;先确认上一次业务验证通过的版本。
helm get values web --revision 2 --kube-context your-context -n default
再以 helm get manifest web --revision 2 加相同 context / namespace 检查实际镜像与对象;要恢复的数据状态需要单独方案。
helm rollback web 2 --kube-context your-context -n default --wait --timeout 5m
将 2 替换为确认值;成功后用 history 查看新增 revision,再查 status、Pod 与业务。回滚失败先读 Events / hook Job 日志,不直接删除 release 存储对象。
revision 2 是示例,先确认其中镜像和 values。回滚会形成新的 release revision;PVC 数据、外部服务、hook 的副作用和数据库迁移不能依赖 Helm 自动逆转。pending 状态先查是否仍有发布进程、Job/Pod Events 和 release 历史,不直接删 release Secret 解锁。
查询仓库版本与使用说明
helm repo list
仓库更新以 helm repo update 单独执行,只刷新本地索引,不升级已安装 release。OCI 引用不依赖 repo list 的索引。
helm search repo web --versions
实际从 repo/chart 安装时加 --version VERSION;VERSION 从此清单选择,安装前用相同版本 show values/template 审阅。
helm get notes web --kube-context your-context -n default
说明中的地址和密码获取方式仍要与实际资源核对;notes 不是运行健康证明。
卸载是删除资源的独立任务
helm uninstall web --kube-context your-context -n default --wait --timeout 5m
执行前审阅 manifest、hook、PVC/CRD 与备份;成功后以 helm list --all 和 kubectl 逐项确认残留。--keep-history 是备选的历史保留策略,不是数据保留策略。
卸载用 helm uninstall RELEASE -n NAMESPACE 并固定 context;先读 chart 对 PVC、CRD、hook 和 external 资源的策略。--keep-history 只保留 release 历史,不等于保留全部 Kubernetes 资源或数据。
helm upgrade · helm rollback · Values files