helm upgrade 失败了会怎样?helm rollback 到底回滚了什么?发布记录存在哪里、能留多少个?线上被人用 kubectl 手工改过之后再 helm upgrade 会怎样?这篇用 kind 集群上的小应用和 Helm 4.3.1 逐项量出来。
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 的三方合并则悄悄改回清单里写的字段。