LabHub
学习 学习路径 课程

Helm 发布与回滚

--atomic 与 --wait 实际在做什么

在 LabHub 中继续学习

一句话总结

--wait 表示“等待所有资源就绪”,--atomic 表示“如果无法全部就绪,就回滚”。不使用它们,失败的部署也会被报告为成功。

概念图: 失败的部署也会被报告为成功。 · 立即返回成功 · --atomic 内含 --wait · 启动最慢的 Pod 所需时间,再加上余量

为什么需要这些选项

helm upgrade 默认只把清单发送给 API 服务器,随后立即返回成功,并不检查 Pod 是否真的启动。即使镜像标签写错并出现 ImagePullBackOff,helm 也会输出 STATUS: deployed。CI 日志显示绿色,只有用户遭遇故障。

加入 --wait 后,helm 会轮询 Deployment、StatefulSet 与 DaemonSet 的就绪状态。如果在 --timeout(默认 5 分钟)内没有就绪,就按失败处理,CI 终于会显示红色。

但如果只停留在失败状态,问题仍然存在:新修订已经创建,资源也只更新了一半。--atomic 会在此时自动运行 helm rollback,恢复到先前状态。--atomic 内含 --wait——如果不等待,就根本无法得知部署失败。

它是如何工作的

helm upgrade demo ./chart \
  --atomic \
  --timeout 5m \
  --history-max 10

这一行应该成为部署脚本的基本形式。timeout 应设置为启动最慢的 Pod 所需时间,再加上余量。太短会回滚正常部署,太长则会延迟发现故障。

常见误解

误以为有 --atomic 就万事安全。 atomic 只能回滚 Kubernetes 资源。如果由 hook 运行的迁移 Job 已经修改了数据库,数据库不会自动恢复。因此,模式变更必须拆分为前后版本都能承受的步骤部署:扩展 → 部署 → 清理。

误以为增加 timeout 就能解决问题。 ImagePullBackOff 不会因为等待而恢复。30 分钟的 timeout 只会让故障晚 30 分钟被发现。

发布卡在 pending 状态时

部署在中途终止后,发布可能停留在 pending-upgradepending-install 状态。 此时,下一次部署会被如下错误拒绝。

Error: another operation (install/upgrade/rollback) is in progress

Helm 3 把发布状态保存在命名空间的 Secret 中,却没有单独的锁, 因此“正在进行”的标记会原样残留。进程已经退出,只剩标记还在。

按下面的顺序解除。

helm history demo                    # 1) 마지막 리비전의 상태를 본다
helm rollback demo <직전 성공 리비전>  # 2) 대개 이걸로 풀린다

# 롤백도 거절되면 마지막 수단 — 갇힌 리비전의 Secret 을 지운다
kubectl -n <ns> get secret -l owner=helm,name=demo
kubectl -n <ns> delete secret sh.helm.release.v1.demo.v<갇힌 번호>

最后一种方法会删除该修订的历史记录,无法恢复。删除之前,应使用 helm get manifest demo --revision <n> 保存其内容。

回滚无法恢复的内容

helm rollback 会恢复发布清单,清单以外的内容则保持不变。

会恢复 不会恢复
Deployment、Service、ConfigMap 的 spec hook 执行的数据库迁移
镜像标签、环境变量、replicas PVC 中的数据
资源请求与限制 发送给其他系统的事件与通知
已删除资源原先保存的状态

尤其要注意最后一行。如果从 Chart 中移除某个资源并部署,Helm 会删除它。 回滚后,资源虽然会重新创建,其中的内容却是全新的。暂时移除 StatefulSet 再回滚,就是一条可能导致数据丢失的路径。

部署流水线的基本形式

helm upgrade --install demo ./chart   -f values/prod.yaml   --set image.tag="$GIT_SHA"   --atomic --timeout 5m --history-max 10   --wait-for-jobs

# helm 이 초록불을 준 뒤에 바깥에서 확인한다
curl -fsS --retry 5 --retry-delay 3 https://demo.example.com/healthz

人们经常漏掉 --wait-for-jobs--wait 会等待 Deployment 与 StatefulSet, 却不会等待 Job。迁移 Job 尚未结束,新 Pod 就开始接收流量的事故由此发生。

实际工作中真正重要的事

--wait 检查的是工作负载的就绪状态,而不是“服务是否正常”。即使 Pod 已经 Ready,响应也可能是 500。因此,部署流水线的最后一步必须始终包含一次从外部发起的冒烟测试。helm 显示绿色与用户真正能够使用服务,是两个不同的命题。