StatefulSet 的滚动更新和 Deployment 有什么不同?partition 怎么做金丝雀?更新到一半卡住了怎么办?这篇用 4 个副本的 StatefulSet 把更新顺序、partition、OnDelete 和“坏版本卡住后怎么救”都实测了一遍。
初始 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=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=4updateStrategy.rollingUpdate.partition: 2:只有序号 ≥ 2 的 web-2、web-3 被更新成 v2,web-0、web-1 保持 v1;currentRevision(旧)与 updateRevision(新)并存,updatedReplicas=2。观察一段时间没问题,再把 partition 改成 0,其余的就接着更新。
改 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=4updateStrategy.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。