bigywater.tech
Kubernetes · 49

StatefulSet:稳定的名字、顺序与存储

StatefulSet 比 Deployment 多了什么?Pod 的名字、DNS 和数据在重建后到底哪些不变?为什么有序创建会“卡住”?这篇用一个带 PVC 的 StatefulSet,把有序创建与终止、名字与 IP、存储与 PVC 的保留策略,以及 podManagementPolicy 的差别都实测一遍。

7 秒一个
OrderedReady 时 web-0、web-1、web-2 依次创建并 Ready 的间隔
10.244.2.106 → .107
web-0 删除重建后 Pod IP 的变化,而名字与 DNS 名字不变
ord-0 only
没有 Pod 就绪时 OrderedReady 只创建了的 Pod(Parallel 创建了 3 个)

有序创建,逆序终止

实测 · 本机执行输出
  web-0:创建于 + 0.0s;Ready 于 + 7.0s;所在节点 worker3
  web-1:创建于 + 7.0s;Ready 于 +14.0s;所在节点 worker2
  web-2:创建于 +14.0s;Ready 于 +21.0s;所在节点 worker4
  (就绪探针 initialDelaySeconds=3,加上调度、绑定 PVC 和拉起容器,每个 Pod 要 6~7 秒才 Ready;下一个 Pod 在上一个 Ready 之后才创建)
  缩到 1 个副本(Pod 的 terminationGracePeriodSeconds=5,sleep 不处理 SIGTERM,每个要 5 秒才结束):web-2 在 +0.2s 开始终止,web-1 在 +5.8s 才开始终止(web-2 完全结束之后才轮到 web-1:从大序号往小序号逐个删)

OrderedReady(默认)下,web-0 创建于 +0.0 秒、Ready 于 +7.0 秒;web-1 在 web-0 Ready 的同一刻才创建,+14.0 秒 Ready;web-2 +14.0 创建、+21.0 秒 Ready。缩容反过来:web-2 先终止,等它完全结束(这里 Pod 要 5 秒),web-1 才在 +5.8 秒开始终止。需要按序号初始化、按序号下线的数据库集群靠的就是这个。

名字稳定,IP 不稳定

实测 · 本机执行输出
  web-0 的 Pod IP 10.244.2.106;hostname=web-0;DNS web-0.web.k43.svc.cluster.local → 10.244.2.106
  删掉 web-0 重建之后:名字仍是 web-0,uid 变了,Pod IP 10.244.2.107;DNS web-0.web.k43.svc.cluster.local → 10.244.2.107
  headless Service 的 DNS 返回所有 Pod:web.k43.svc → 10.244.3.75 10.244.1.83 10.244.2.107

Pod 的名字是 <sts名>-<序号>,hostname 也是它,配合 headless Service 得到稳定的 DNS 名 web-0.web.k43.svc.cluster.local。删掉 web-0 重建:名字不变、uid 变了、Pod IP 从 10.244.2.106 变成 10.244.2.107,DNS 名字跟着解析到新 IP;web.k43.svc 本身返回所有 Pod 的 IP。所以客户端要用名字找成员,不能缓存 IP。

每个 Pod 一个 PVC

实测 · 本机执行输出
  NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
  data-web-0   Bound    pvc-6baed74f-164c-4872-9770-17b9f57e6cc1   10Mi       RWO            standard       <unset>                 56s
  data-web-1   Bound    pvc-0c67d43c-a622-4f9e-a883-1bf8d2916d13   10Mi       RWO            standard       <unset>                 49s
  data-web-2   Bound    pvc-b9fb68c7-4bda-4e14-8a72-2a92a0192246   10Mi       RWO            standard       <unset>                 42s
  web-1 写入的文件:web-1;往 /data/note 写 hello-1
  删掉 web-1 重建之后:/data/note=hello-1;PVC 绑定的 PV 没变:True(pvc-0c67d43c…)
  缩到 1 个副本之后 PVC:persistentvolumeclaim/data-web-0 persistentvolumeclaim/data-web-1 persistentvolumeclaim/data-web-2(data-web-1、data-web-2 没有随 Pod 删除)
  再扩回 3 个:web-1 的 /data/note=hello-1(旧数据还在)
  persistentVolumeClaimRetentionPolicy whenScaled: Delete 的 StatefulSet wd 缩到 1 个副本之后 PVC:persistentvolumeclaim/data-wd-0(data-wd-1 随 Pod 一起被删了)
  删除 StatefulSet(whenDeleted: Delete)之后 PVC:(没有)

volumeClaimTemplates 为每个 Pod 创建 data-web-0、data-web-1、data-web-2。web-1 在 /data/note 里写了 hello-1,删掉重建后文件还在,PVC 绑定的 PV 也没变;缩到 1 个副本之后三个 PVC 仍在,扩回 3 个旧数据依然在。默认的保留策略是为了不丢数据;设置 persistentVolumeClaimRetentionPolicy: {whenScaled: Delete, whenDeleted: Delete} 后,缩容时对应的 PVC 随 Pod 一起删除,删除 StatefulSet 时所有 PVC 也跟着没了。

被一个不就绪的 Pod 卡住

实测 · 本机执行输出
  ord(podManagementPolicy=OrderedReady),就绪探针要求 /data/ready 存在(没人去创建):18 秒后已创建的 Pod ['ord-0'];Ready 的 []
  par(podManagementPolicy=Parallel),就绪探针要求 /data/ready 存在(没人去创建):18 秒后已创建的 Pod ['par-0', 'par-1', 'par-2'];Ready 的 []
  只给 ord-0 创建 /data/ready 之后(ord-1 被创建,但它自己没有这个文件):ord 的 Pod ['ord-0', 'ord-1']

就绪探针要求一个没人创建的文件。OrderedReady 的 StatefulSet 18 秒后只创建了 ord-0(它不 Ready,后面的就不创建);podManagementPolicy: Parallel 的 3 个 Pod 同时创建。给 ord-0 创建了文件之后 ord-1 才出现。一个 Pod 起不来会让整个 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),存储是 kind 默认的 local-path StorageClass(standard);Pod 是 busybox:1.36,就绪探针 initialDelaySeconds=3;第 1 节的缩容用 terminationGracePeriodSeconds=5 让每个 Pod 的终止持续几秒(宽限期为 0 时两个 Pod 同时消失,看不出顺序);DNS 用同命名空间里一个 busybox Pod 的 nslookup;时间取自 Pod 的 creationTimestamp 与 Ready 条件的 lastTransitionTime,以及 0.1 秒间隔轮询;每种情形各一次
    • 没有验证:StatefulSet 更新策略(下一篇);volumeClaimTemplates 的存储类扩容;节点失联时 StatefulSet Pod 的强制删除与“至多一个”语义;minReadySeconds;ordinals.start(从非 0 序号开始);local-path 的卷绑定在某个节点上,Pod 重建后能否换到别的节点;Pod 的 podAntiAffinity 与 StatefulSet 的组合
    京ICP备2026067502号-1京公网安备11010502063487号