bigywater.tech
Kubernetes · 50

StatefulSet 更新策略:partition 与滚动顺序

StatefulSet 的滚动更新和 Deployment 有什么不同?partition 怎么做金丝雀?更新到一半卡住了怎么办?这篇用 4 个副本的 StatefulSet 把更新顺序、partition、OnDelete 和“坏版本卡住后怎么救”都实测了一遍。

web-3 → web-0
滚动更新的顺序:从最大序号开始,耗时 +6.7 / +11.7 / +16.1 / +21.7 秒
2 个 v2 + 2 个 v1
partition=2 时更新后的版本分布
web-3 仍是 bad
把模板改回好的 25 秒后,坏掉的 web-3 依然没被替换

逆序、一次一个

实测 · 本机执行输出
  初始 4 个副本:revision={'web-0': '567bd6', 'web-1': '567bd6', 'web-2': '567bd6', 'web-3': '567bd6'};currentRevision=567bd6 updateRevision=567bd6 currentReplicas=4 updatedReplicas=4 readyReplicas=4
  改 env V=v2 触发更新,各个 Pod 被替换成新 Pod 并 Ready 的时刻:web-3 +6.7s;web-2 +11.7s;web-1 +16.1s;web-0 +21.7s
  更新后:revision={'web-0': '75f475', 'web-1': '75f475', 'web-2': '75f475', 'web-3': '75f475'};env={'web-0': 'v2', 'web-1': 'v2', 'web-2': 'v2', 'web-3': 'v2'};currentRevision=75f475 updateRevision=75f475 currentReplicas=4 updatedReplicas=4 readyReplicas=4

改环境变量触发更新:web-3 +6.7 秒换完并 Ready,web-2 +11.7 秒,web-1 +16.1 秒,web-0 +21.7 秒——从最大序号开始,前一个 Ready 才换下一个。每个 Pod 的 controller-revision-hash 标签标记它属于哪个版本,status.currentRevision/updateRevision 在更新结束后合一。

partition 做金丝雀

实测 · 本机执行输出
  partition=2、改 env V=v2:被替换的 Pod ['web-2', 'web-3'];env={'web-0': 'v1', 'web-1': 'v1', 'web-2': 'v2', 'web-3': 'v2'};currentRevision=567bd6 updateRevision=75f475 currentReplicas=2 updatedReplicas=2 readyReplicas=4
  把 partition 改成 0:env={'web-0': 'v2', 'web-1': 'v2', 'web-2': 'v2', 'web-3': 'v2'};currentRevision=75f475 updateRevision=75f475 currentReplicas=4 updatedReplicas=4 readyReplicas=4

updateStrategy.rollingUpdate.partition: 2:只有序号 ≥ 2 的 web-2、web-3 被更新成 v2,web-0、web-1 保持 v1;currentRevision(旧)与 updateRevision(新)并存,updatedReplicas=2。观察一段时间没问题,再把 partition 改成 0,其余的就接着更新。

OnDelete

实测 · 本机执行输出
  改 env V=v2 后 8 秒:env={'web-0': 'v1', 'web-1': 'v1', 'web-2': 'v1', 'web-3': 'v1'};currentRevision=567bd6 updateRevision=75f475 currentReplicas=4 updatedReplicas=None readyReplicas=4
  手动删掉 web-1 之后:env={'web-0': 'v1', 'web-1': 'v2', 'web-2': 'v1', 'web-3': 'v1'};currentRevision=567bd6 updateRevision=75f475 currentReplicas=3 updatedReplicas=1 readyReplicas=4

updateStrategy.type: OnDelete 下改了模板 8 秒,四个 Pod 全是 v1,updatedReplicas 为空;手动 kubectl delete pod web-1,重建出来的 web-1 才是 v2。适合必须由人来决定升级顺序和时机的有状态应用。

坏版本卡住

实测 · 本机执行输出
  更新成一个就绪探针永远失败的模板:20 秒后 env={'web-0': 'v1', 'web-1': 'v1', 'web-2': 'v1', 'web-3': 'bad'};ready 的 Pod ['web-0', 'web-1', 'web-2'];currentRevision=567bd6 updateRevision=88b5b7 currentReplicas=3 updatedReplicas=1 readyReplicas=3
    web-3 是新的但不 Ready,更新停在这里:web-2、web-1、web-0 都还是旧模板(没有继续)
  再把模板改成好的(env V=v3):25 秒后 env={'web-0': 'v1', 'web-1': 'v1', 'web-2': 'v1', 'web-3': 'bad'};ready 的 Pod ['web-0', 'web-1', 'web-2'];currentRevision=567bd6 updateRevision=c49476 currentReplicas=3 updatedReplicas=None readyReplicas=3
    web-3 仍然是坏的 bad:StatefulSet 控制器不会替换一个不健康的 Pod,哪怕模板已经改好了——必须手动删掉它
  手动删掉 web-3 之后 17 秒:env={'web-0': 'v3', 'web-1': 'v3', 'web-2': 'v3', 'web-3': 'v3'};currentRevision=c49476 updateRevision=c49476 currentReplicas=4 updatedReplicas=4 readyReplicas=4(更新继续从 web-3 往 web-0 推进完成)

更新到一个就绪探针永远失败的模板:web-3 起不来,更新停在这里,web-2、web-1、web-0 仍是旧版本。这时把模板改回好的,25 秒后 web-3 依然是坏的——StatefulSet 控制器不会替换不健康的 Pod。手动删掉 web-3 之后,新建的 web-3 用的是好模板,更新才继续往 web-0 推进,17 秒全部完成。这是 StatefulSet 与 Deployment 一个重要的差别:Deployment 会按最新模板继续滚动,StatefulSet 会等你亲自处理坏 Pod。

分步演示

StatefulSet 更新
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。独立的 5 节点 kind 集群 kx-sched(Kubernetes v1.37.0);StatefulSet 是 busybox:1.36 sleep,环境变量 V 用来改变 Pod 模板,就绪探针 initialDelaySeconds=2,terminationGracePeriodSeconds=1;被替换的时刻由 0.2 秒间隔轮询 Pod 的 uid 变化并 Ready 得到;版本取 controller-revision-hash 标签的后 6 位;坏模板的就绪探针是 exit 1;每种情形各一次
    • 没有验证:maxUnavailable 与 podManagementPolicy: Parallel 下的更新(特性较新);PVC 在更新中的行为;minReadySeconds;kubectl rollout undo 对 StatefulSet 的作用;revisionHistoryLimit 与 ControllerRevision 对象;partition 大于副本数时的行为;更新期间 Pod 被驱逐或节点故障的情形
    京ICP备2026067502号-1京公网安备11010502063487号