一次 Helm 升级被中断后 release 卡在 pending-upgrade、回滚又遭遇并发操作的复盘(匿名化合成案例)。
某业务集群的发布流水线执行 Helm 升级后,新版本 Pod 一直未就绪。流水线在自身超时时间到达时强行结束任务,Helm 客户端没有机会完成收尾。随后 helm list --all 显示 release 处于 pending-upgrade,再次执行 upgrade 或 rollback 都报 another operation (install/upgrade/rollback) is in progress。旧版本仍在服务,但该 release 的所有发布操作被卡住。
helm upgrade --wait,新配置被渲染并提交。新 Pod 因一个错误的配置键无法通过就绪探针。pending-upgrade。helm history 只显示一个 pending revision 和一个更早的 deployed revision。旧版本 Pod 保持可用,没有发生整站故障。影响集中在交付侧:该 release 约半小时无法继续升级,流水线自动重试和人工操作互相阻塞;同一命名空间的后续变更被迫排队。失败的 Pod 占用少量资源,节点容量略紧但没有触发驱逐。由于本次变更不包含数据库结构变更,回滚不涉及数据恢复;如果涉及不可逆迁移,处理顺序必须完全不同。
看到 pending-upgrade 和“another operation in progress”,第一反应是有一个陈旧的 Helm 锁,或者 release Secret 损坏。实际上 Helm 没有需要人工删除的独立锁对象,release 当前 revision 的状态记录就是并发保护的依据。第一次 rollback 失败并不是状态无法恢复,而是此时自动重试真的在运行,两个操作发生了重叠。
第二个误判是把问题归咎于 chart 或 Helm 本身。检查 Pod 日志后可以看到明确的配置键错误,升级之所以没有正常变成 failed,是因为流水线在 Helm 完成收尾前杀掉了客户端。推翻“锁坏了”的证据是:自动重试停止后,同一个 rollback 命令成功;helm history 也清楚显示上一版处于 deployed,release 数据没有丢失。
直接触发因素是新版本配置键错误,Pod 无法就绪。更关键的是发布链路的超时边界设置错误:流水线硬超时早于 Helm 的 --wait 完成,取消任务时直接终止了 Helm,使其没有机会执行失败回滚或写入最终状态。流水线还启用了自动重试,与人工回滚并发;同一个 release 没有互斥的 CI 并发组,最终形成了“状态锁住 + 操作竞争”。
如果升级命令使用了 --atomic,Helm 会尝试在失败时自动回滚,但前提是客户端能完整执行到失败处理阶段。外层进程被强杀时,任何应用层清理都可能来不及执行,因此不能只靠 --atomic 兜底。
先停止所有自动化,包括队列中的重试、部署机器人和同一 release 的其他流水线,保证只有一个操作者:
helm list -n <ns> --all
helm history <release> -n <ns>
helm status <release> -n <ns>
kubectl get pods -n <ns> -l app=<app>
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --previous
从历史记录中选出明确为 deployed 的 revision,读取它的实际 values 和 manifest,确认回滚目标:
helm get values <release> -n <ns> --revision <last-deployed> --all
helm get manifest <release> -n <ns> --revision <last-deployed> > /tmp/last-deployed.yaml
helm rollback <release> <last-deployed> -n <ns> --wait --timeout 10m
helm history <release> -n <ns>
不要用删除 release Secret 的方式强行“解锁”。如果 rollback 仍提示有操作进行中,先找到并停止真正的并发任务;如果 rollback 因为 hook 失败,查看对应 Job 或 Pod 的日志,而不是反复重试。回滚完成后检查 Deployment 的可用副本和 Service 端点,再修复配置。重新发布前先本地渲染并与上一版对比:
helm get manifest <release> -n <ns> --revision <last-deployed> > /tmp/previous.yaml
helm template <release> <chart> -n <ns> -f <values-file> > /tmp/rendered.yaml
diff -u /tmp/previous.yaml /tmp/rendered.yaml
确认差异只包含预期变更后,使用 --atomic --wait 并让 Helm 超时小于流水线超时,再执行升级:
helm upgrade <release> <chart> -n <ns> -f <values-file> \
--atomic --wait --timeout 10m
--wait 超时,并留出回滚时间;需要取消时先发送可处理信号,不要直接强杀,确保 --atomic 有机会运行。helm history,发现 pending 或 failed 状态就阻止新任务并告警,避免在异常状态上叠加操作。helm history、当前 values、Pod 事件和日志纳入发布失败的标准输出;不要只显示“升级超时”。合上页面,先回答,再展开对照。
pending-upgrade 时,为什么不能直接删除 release Secret?--atomic 应该如何配合?--atomic 只有在 Helm 客户端能完成失败处理时才有效,强杀会绕过它。helm history 并执行回滚。
小程序版
微信扫码,随时查命令