bigywater.tech
微服务 · 91

Helm 升级与回滚的状态

helm upgrade 失败了会怎样?helm rollback 到底回滚了什么?发布记录存在哪里、能留多少个?线上被人用 kubectl 手工改过之后再 helm upgrade 会怎样?这篇用 kind 集群上的小应用和 Helm 4.3.1 逐项量出来。

10
helm 默认保留的 revision 数(15 个 revision 后只剩 6 到 15)
0 → 1
坏镜像下默认 upgrade 的退出码是 0(看不出坏),加 --wait 后是 1
7
手工 kubectl scale 到 7 之后,Helm 4 默认的 upgrade 与 rollback 因冲突被拒绝,副本数停在 7(或 9)

发布记录

实测 · 本机执行输出
  install --wait:退出码 0,0.7 s
  upgrade(副本数 3→4):退出码 0,0.1 s
  upgrade(日志级别 info→debug):退出码 0,0.1 s
  upgrade(什么都不改):退出码 0,0.1 s
  helm history:rev1=superseded; rev2=superseded; rev3=superseded; rev4=deployed
  集群里的 Secret(名字,release 字段 base64 之后的字符数):sh.helm.release.v1.shop.v1=1888; sh.helm.release.v1.shop.v2=1928; sh.helm.release.v1.shop.v3=1944; sh.helm.release.v1.shop.v4=1932
  解开 v4 这个 Secret(base64 → base64 → gzip → JSON):解压前 1085 字节,解压后 1904 字节;顶层键 ['apply_method', 'chart', 'config', 'info', 'manifest', 'name', 'namespace', 'version'];info.status = deployed;manifest 字段 475 个字符,config(用户给的 values)= {'logLevel': 'debug', 'replicaCount': 4}
  每次 upgrade(包括什么都没改的那次)都会产生一个新的 revision,存成一个叫 sh.helm.release.v1.<名字>.v<编号> 的 Secret,里面是压缩过的整个 release(chart、values、渲染出的清单)

每个 revision 一个 Secret,里面是压缩的 chart、values、渲染出的清单;什么都没改的 upgrade 也会产生新 revision。

历史上限

实测 · 本机执行输出
  --history-max int                            limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
  15 个 revision 之后:helm history 里 10 个(编号 6 到 15),Secret 10 个(最老的是 v6)
  设 --history-max 3 之后连续 5 次 upgrade:helm history 里 3 个(编号 4 到 6)
  默认只保留最近的 10 个 revision,更老的发布记录(和能回滚到的版本)被丢掉

默认只保留 10 个 revision,更老的发布记录和能回滚的版本被丢掉。

坏版本

实测 · 本机执行输出
场景                                       退出码   upgrade 耗时(s)   最新 revision 的状态   helm history                                                                                           3 秒后的 Pod                                       最后一行输出
(a)默认,不加 --wait                          0     0.1             deployed          rev1=superseded; rev2=deployed                                                                         3×1.36(Ready), 1×1.36-nope(ContainerCreating)   -
(b)--wait --timeout 30s                  1     30.1            failed            rev1=deployed; rev2=failed(Upgrade "shop" failed: resource Deployme)                                   3×1.36(Ready), 1×1.36-nope(ErrImagePull)        context deadline exceeded
(c)--rollback-on-failure --timeout 30s   1     30.3            deployed          rev1=superseded; rev2=failed(Upgrade "shop" failed: resource Deployme); rev3=deployed(Rollback to 1)   3×1.36(Ready), 1×1.36-nope(ContainerCreating)   context deadline exceeded
  默认的 upgrade 提交清单就返回,不等 Pod 起来:退出码 0,release 是 deployed,新 Pod 却起不来(maxUnavailable 默认 25% 取整为 0,旧 Pod 还在服务,Deployment 的滚动卡住);加 --wait 能发现它,退出码非 0,release 变成 failed,但不会自己回滚,旧 Pod 照常在;--rollback-on-failure 在等待超时后自动回滚

默认的 upgrade 提交清单就返回,不等 Pod;--wait 能发现失败但不回滚;--rollback-on-failure 超时后自动回滚。

回滚

实测 · 本机执行输出
  坏镜像 upgrade --wait:退出码 1;helm history:rev1=superseded; rev2=deployed; rev3=failed(Upgrade "shop" failed: resource Deployme)
  helm rollback shop 2 --wait:退出码 0,0.5 s;输出 Rollback was a success! Happy Helming!;回滚后副本数 4,日志级别 debug,镜像 tag 1.36;helm history:rev1=superseded; rev2=superseded; rev3=failed(Upgrade "shop" failed: resource Deployme); rev4=deployed(Rollback to 2)
  再 helm rollback shop 1 --wait:退出码 0;回滚后副本数 2,日志级别 info;helm history:rev1=superseded; rev2=superseded; rev3=failed(Upgrade "shop" failed: resource Deployme); rev4=superseded(Rollback to 2); rev5=deployed(Rollback to 1)
  回滚不是 “回到过去”,而是用旧 revision 的清单再做一次新的 revision(编号继续往上走),旧的记录都还在

helm rollback 用旧 revision 的清单再做一次新的 revision,编号继续往上走。

手工改动

实测 · 本机执行输出
  手工 kubectl scale 之后副本数 7;(a)helm upgrade 只改日志级别(退出码 1):副本数 7;Error: UPGRADE FAILED: conflict occurred while applying object v91/shop apps/v1, Kind=Deployment: Apply failed with 1 conflict: conflict with "kubectl" with sub
  (b)helm upgrade 改 replicaCount=5(退出码 1):副本数 7;Error: UPGRADE FAILED: conflict occurred while applying object v91/shop apps/v1, Kind=Deployment: Apply failed with 1 conflict: conflict with "kubectl" with sub
  再手工 scale 到 9,(c)helm rollback(不带编号,回到上一个 revision,退出码 1):副本数 9;各 revision 的清单里 replicas:{1: '3', 2: '3', 3: '5'};Error: conflict occurred while applying object v91/shop apps/v1, Kind=Deployment: Apply failed with 1 conflict: conflict with "kubectl" with subresource "scale"
  Deployment 的 managedFields 里的管理者(manager/operation):helm/Apply kubectl/Update kube-controller-manager/Update
  (d)手工 scale 到 7 之后 helm upgrade 只改日志级别并加 --force-conflicts(退出码 0):副本数 3
  Helm 4 默认用服务端应用(server-side apply):helm 作为字段管理者 "helm" 提交清单,kubectl scale 改过的字段属于另一个管理者,冲突时默认拒绝并报错;--force-conflicts 让 helm 抢回这些字段的所有权,清单里写的是 3,所以副本数回到 3
  (e)改用客户端三方合并(--server-side=false)重来一遍:手工 scale 到 7 → upgrade 只改日志级别(退出码 0):副本数 3,手工加的注解 note = hand;upgrade 改 replicaCount=5(退出码 0):副本数 5;再手工 scale 到 9 → helm rollback 1(退出码 0):副本数 3
  客户端三方合并下,清单里写了的字段(replicas: 3)会被覆盖回清单的值,即使这个字段在两个 revision 之间没有变;清单里没有写的字段(手工加的注解)被保留。所以手工改过的副本数在下一次 helm upgrade 时被悄悄改回去,不报错;服务端应用则是报冲突、拒绝
说明(哪些是测到的,哪些没有测)
  每个场景只跑了一次;耗时含 helm 进程本身;没有测:helm hooks(pre-upgrade / post-upgrade Job)、数据库迁移与回滚的配合、CRD、有状态应用与 PVC、--reuse-values 的陷阱、helm 3 的行为(这里只用 4.3.1,它的 --atomic 在 4 里改名 --rollback-on-failure)、更大的 release(Secret 有 1 MiB 的体积限制,没有逼近它)

Helm 4 默认服务端应用,kubectl 改过的字段会让 upgrade 与 rollback 报冲突;--force-conflicts 抢回字段;--server-side=false 的三方合并则悄悄改回清单里写的字段。

分步演示

helm 升级与回滚
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Helm v4.3.1,kind 集群,一个 3 副本的小应用(busybox httpd,readinessProbe 是 tcpSocket 8080);「坏版本」是不存在的镜像 busybox:1.36-nope;每个场景只跑了一次,耗时含 helm 进程本身(例如 --wait 的 30.1 s 与 --rollback-on-failure 的 30.3 s 之间的差别在运行间抖动范围内);rollback-on-failure 3 秒后还能看到一个坏镜像 Pod(ContainerCreating),我认为它正在被删除,没有再等着确认
    • 没有验证:helm hooks(pre-upgrade、post-upgrade Job);数据库迁移与回滚的配合(helm 回滚不会回滚数据);CRD;有状态应用与 PVC;--reuse-values 的陷阱;Helm 3 的行为(这里只用 4.3.1,它的 --atomic 在 4 里改名为 --rollback-on-failure);更大的 release(Secret 有 1 MiB 的体积限制,这里远没有逼近);服务端应用冲突在 Helm 升级时的 field manager 细节
    京ICP备2026067502号-1京公网安备11010502063487号