Kubernetes 基础体系 · 第 17/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
StatefulSet:稳定身份、顺序、PVC、更新和分布式系统边界
StatefulSet 是 Kubernetes apps/v1 中用于管理有状态 Pod 的工作负载控制器。它与 Deployment 都负责维持副本数量、创建 Pod 和执行更新,但两者对 Pod 的身份处理不同:
- Deployment 管理的是一组可互换的 Pod;
- StatefulSet 管理的是一组具有稳定序号、稳定网络名称和稳定存储关联的 Pod。
这里的“稳定”不是指 Pod 进程永远不重启,也不是指数据一定安全,而是指 Kubernetes 会尽量让某个逻辑成员在 Pod 被替换后继续对应相同的身份和存储声明。
StatefulSet 解决的是 Kubernetes 对象层面的身份、顺序和存储关联问题;它不会自动解决数据库复制、选主、共识、事务一致性、备份恢复或跨故障域可用性。
一、先区分 Deployment 与 StatefulSet 的语义
Deployment 通常创建带有随机后缀的 ReplicaSet 和 Pod。例如:
web-7d8f9c6f5b-mxq2k
web-7d8f9c6f5b-p7v4n
某个 Pod 被删除后,Deployment 只需要创建一个新的 Pod 来补足副本数。新 Pod 可以具有不同的名称,也可以运行在不同节点上。对无状态 HTTP 服务而言,客户端通常只关心“有一个可用实例”,而不关心实例的具体身份。
StatefulSet 则为副本分配稳定的序号:
db-0
db-1
db-2
如果 db-1 被删除,StatefulSet 会重新创建名为 db-1 的 Pod。新的 Pod 具有新的 UID,容器进程也可能运行在另一台节点上,但它仍然是逻辑上的 db-1,并会重新关联 db-1 对应的 PVC。
因此需要区分三种身份:
| 身份 | 是否稳定 | 说明 |
|---|---|---|
| Pod 名称 | 在满足控制器语义时稳定 | 例如 db-1 |
| Pod UID | 不稳定 | Pod 重建后一定变化 |
| 容器进程身份 | 不稳定 | 可能重启、迁移或重新挂载存储 |
StatefulSet 提供的是逻辑成员身份,不是进程级永久性。
二、StatefulSet 的核心对象关系
一个典型 StatefulSet 通常与以下对象协作:
flowchart LR
C[kubectl apply] --> S[StatefulSet]
S --> P0[Pod db-0]
S --> P1[Pod db-1]
S --> P2[Pod db-2]
S --> PVC0[PVC data-db-0]
S --> PVC1[PVC data-db-1]
S --> PVC2[PVC data-db-2]
PVC0 --> PV0[PV 0]
PVC1 --> PV1[PV 1]
PVC2 --> PV2[PV 2]
HS[Headless Service db] --> P0
HS --> P1
HS --> P2
其中:
- StatefulSet 负责 Pod 的副本、序号、更新和生命周期协调;
- Headless Service 负责提供稳定的 DNS 发现入口;
volumeClaimTemplates为每个序号生成独立 PVC;- PVC 再通过 StorageClass 动态绑定到 PV,或绑定到预先创建的 PV。
这些职责不能互相替代。Headless Service 不会提供数据复制,PVC 不会提供数据库一致性,StatefulSet 也不会因为创建了三个 Pod 就自动形成三副本数据库。
三、稳定身份:序号、名称与 DNS
1. Pod 序号
设 StatefulSet 名称为 db,副本数为 N,则通常会得到序号集合:
序号为 的 Pod 名称为:
db-i
例如副本数为 3 时:
db-0
db-1
db-2
缩容会移除较大的序号。将副本数从 3 改为 2,通常移除 db-2,而不是 db-0。
这并不代表应用一定应该把 db-2 视为“最不重要的节点”。StatefulSet 只按序号执行控制器层面的生命周期操作,数据库该如何安全移除成员,仍然由数据库自身的集群协议决定。
2. Headless Service 与稳定 DNS
StatefulSet 的 spec.serviceName 指向一个 Service。通常该 Service 使用:
clusterIP: None
这样的 Service 称为 Headless Service,即无 ClusterIP 的 Service。它不提供一个虚拟 IP 来负载均衡,而是让 Kubernetes DNS 为后端 Pod 发布记录。
稳定名称的一般形式是:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
对于 StatefulSet db、Service db、命名空间 default:
db-0.db.default.svc.cluster.local
db-1.db.default.svc.cluster.local
db-2.db.default.svc.cluster.local
下面是一个完整的 Service 定义:
apiVersion: v1
kind: Service
metadata:
name: db
labels:
app: demo-db
spec:
clusterIP: None
publishNotReadyAddresses: true
selector:
app: demo-db
ports:
- name: http
port: 80
targetPort: 80
selector 必须能够选中 StatefulSet 的 Pod。publishNotReadyAddresses: true 会让未处于 Ready 状态的 Pod 也可能出现在 DNS 端点中,这对某些需要“先发现成员、再完成集群初始化”的系统有用,但也会让客户端连接到尚未可用的实例。因此它不是通用的“高可用开关”。
如果未设置 publishNotReadyAddresses: true,通常只有 Ready 的 Pod 才会被视为可用端点。此时一个 Pod 可能已经创建并拥有稳定 DNS 名称,但在 readiness probe 通过前,客户端未必能通过 Service 发现它。
还要注意 DNS 不是瞬时同步的。CoreDNS、客户端 DNS 缓存和负缓存都可能造成短暂延迟。因此应用不能把“刚创建 Pod 后立即解析失败”简单等同于 StatefulSet 创建失败。
四、顺序:OrderedReady 与 Parallel
StatefulSet 的 podManagementPolicy 控制 Pod 创建和删除的协调方式:
OrderedReady:默认值;Parallel:允许并行创建和删除。
1. OrderedReady 的行为
默认的 OrderedReady 大致遵循以下约束:
创建时:
只有较小序号的 Pod 达到控制器认为的就绪条件后,才继续推进较大的序号。
删除或缩容时通常反向进行:
因此,副本数从 3 缩到 1 时,控制器通常先删除 db-2,再删除 db-1。
关键点在于:这里的“Ready”主要是 Kubernetes Pod 状态,而不是 StatefulSet 理解后的数据库业务健康状态。StatefulSet 不会解析数据库的复制延迟、选主状态或事务提交状态。
如果 db-0 的 readiness probe 永远失败,那么在 OrderedReady 下,db-1 可能根本不会被创建。这种停滞是顺序保证的直接结果。
2. Parallel 的行为
配置:
spec:
podManagementPolicy: Parallel
后,StatefulSet 可以并行创建和删除多个 Pod。它仍然为 Pod 分配稳定序号,也仍然支持稳定 PVC,但不再等待低序号 Pod Ready 后才处理高序号 Pod。
这适合以下场景:
- 应用可以自行处理成员发现;
- 各 Pod 初始化相互独立;
- 不需要通过创建顺序建立集群;
- 希望缩短扩容和替换时间。
它不适合依赖严格启动顺序的应用,除非应用本身已经实现了等待、重试和成员协调。
3. 顺序保证不等于集群初始化协议
例如,某数据库要求:
db-0先初始化;db-1和db-2再加入;- 集群形成多数派;
- 之后才允许业务请求。
StatefulSet 的 OrderedReady 最多能帮助控制 Pod 的创建顺序;它不会自动执行上述数据库命令,也不会判断“初始化是否已经提交”。这些逻辑需要数据库镜像、启动脚本或 Operator 完成。
五、PVC:每个序号对应独立存储
1. volumeClaimTemplates 的展开
StatefulSet 可以使用 volumeClaimTemplates:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 10Gi
假设 StatefulSet 名称为 db,副本数为 3,模板名称为 data,则通常会创建:
data-db-0
data-db-1
data-db-2
对应关系可以写成:
这意味着 db-0 和 db-1 默认不是共享同一个 PVC,而是分别使用自己的 PVC。
Pod 模板中需要通过相同的卷名称引用它:
spec:
containers:
- name: app
image: nginx:1.27
volumeMounts:
- name: data
mountPath: /var/lib/app
这里的 data 不是 PVC 名称,而是 volumeClaimTemplates.metadata.name。StatefulSet 控制器会将它解析为当前序号对应的 PVC。
2. PVC 与 PV 的生命周期不是 Pod 生命周期
删除 Pod:
kubectl delete pod db-1
通常只删除 Pod,不删除 data-db-1。新建的 db-1 会尝试重新挂载同一个 PVC。
因此,Pod 重建后的关键变化通常是:
旧 Pod: name=db-1, uid=旧值
新 Pod: name=db-1, uid=新值
PVC: data-db-1,不变
删除 StatefulSet 时,PVC 默认也不会因为 StatefulSet 消失而自动删除。这是重要的安全默认值,因为误删 StatefulSet 不应该顺带无条件删除有价值的数据。
较新的 Kubernetes 版本支持 persistentVolumeClaimRetentionPolicy,可以显式控制 PVC 在 StatefulSet 删除或缩容时的保留策略,例如:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
Retain 表示保留,Delete 表示由控制器删除对应 PVC。该字段的可用性和默认行为应以目标集群的 Kubernetes 版本为准;文章示例假设集群支持当前稳定的 apps/v1 能力。即使 PVC 被删除,PV 是否被回收还受 PV 的 persistentVolumeReclaimPolicy 影响,常见值包括 Retain 和 Delete。这两个策略属于不同层次:
StatefulSet/PVC retention
↓
PVC 是否还存在
↓
PV reclaim policy
↓
底层卷是否释放或保留
3. PVC 模板不会替已有 PVC 自动迁移数据
修改 volumeClaimTemplates 中的容量、StorageClass 或其他字段,不等于自动迁移已有数据。
例如,将模板容量从 10Gi 改为 100Gi,至少涉及:
- 已存在的 PVC 是否被更新;
- StorageClass 和 CSI 驱动是否支持扩容;
- PV 是否允许扩容;
- 文件系统是否需要在线或离线扩展;
- 应用是否能观察到新的容量。
不能把“修改 StatefulSet YAML”当成完整的存储迁移方案。生产环境需要先验证存储驱动、PVC 状态和应用数据一致性。
4. 访问模式不是分布式锁
ReadWriteOnce(RWO)表示卷可以被一个节点以读写方式挂载;它不严格表示“整个集群中只有一个 Pod 能同时读取和写入”。在某些实现和调度情况下,同一节点上的多个 Pod 可能共同使用该卷。
如果要求更严格的“单 Pod 读写”,需要评估 ReadWriteOncePod(RWOP)以及目标 CSI 驱动是否支持。访问模式只是卷挂载约束,不是数据库锁,也不是跨进程一致性协议。
六、一个可运行的 StatefulSet 示例
下面的示例创建:
- 一个 Headless Service;
- 一个三副本 StatefulSet;
- 每个 Pod 一个 1 GiB PVC;
- 每个 Pod 把自己的主机名写入持久卷;
- 使用 HTTP readiness probe。
该示例用于观察 StatefulSet 行为,不构成生产数据库部署方案。
apiVersion: v1
kind: Service
metadata:
name: demo-db
labels:
app: demo-db
spec:
clusterIP: None
selector:
app: demo-db
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: demo-db
spec:
serviceName: demo-db
replicas: 3
podManagementPolicy: OrderedReady
selector:
matchLabels:
app: demo-db
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: demo-db
spec:
terminationGracePeriodSeconds: 10
containers:
- name: web
image: nginx:1.27
ports:
- name: http
containerPort: 80
command:
- /bin/sh
- -c
- |
echo "pod=$(hostname)" > /usr/share/nginx/html/identity.txt
exec nginx -g 'daemon off;'
readinessProbe:
httpGet:
path: /identity.txt
port: http
initialDelaySeconds: 2
periodSeconds: 3
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
应用并观察:
kubectl apply -f statefulset-demo.yaml
kubectl get pods -l app=demo-db -w
kubectl get pvc
kubectl get pods -l app=demo-db \
-o custom-columns=NAME:.metadata.name,UID:.metadata.uid,READY:.status.containerStatuses[0].ready
在支持动态供应的集群中,预期可以看到:
demo-db-0
demo-db-1
demo-db-2
以及:
data-demo-db-0
data-demo-db-1
data-demo-db-2
如果 Pod 停在 Pending,常见原因不是 StatefulSet 顺序,而是 PVC 无法绑定,例如:
- 集群没有默认 StorageClass;
storageClassName不存在;- 没有符合条件的 PV;
- 存储拓扑与节点不兼容;
- CSI 驱动故障。
可以用以下命令查看原因:
kubectl describe pod demo-db-0
kubectl describe pvc data-demo-db-0
kubectl get events --sort-by=.lastTimestamp
从临时 Pod 查询稳定 DNS:
kubectl run dns-test \
--rm -it \
--restart=Never \
--image=busybox:1.36 \
-- nslookup demo-db-0.demo-db.default.svc.cluster.local
如果命名空间不是 default,需要替换 DNS 名称中的命名空间部分。
访问某个成员:
kubectl run curl-test \
--rm -it \
--restart=Never \
--image=curlimages/curl:8.10.1 \
-- curl -s http://demo-db-1.demo-db.default.svc.cluster.local/identity.txt
预期输出类似:
pod=demo-db-1
删除 demo-db-1 后再次检查:
kubectl delete pod demo-db-1
kubectl get pods -w
kubectl get pvc
新 Pod 的 UID 会变化,但名称和 PVC 名称保持为:
Pod: demo-db-1
PVC: data-demo-db-1
这证明的是 Kubernetes 身份和存储关联保持稳定,不证明应用数据已经正确复制或事务已经提交。
七、更新机制:RollingUpdate、partition 与 OnDelete
1. 默认 RollingUpdate
StatefulSet 默认使用:
updateStrategy:
type: RollingUpdate
当 Pod 模板发生变化,例如镜像从 nginx:1.27 改为另一个版本,控制器会按照 StatefulSet 的顺序执行替换。常见过程是从较大的序号开始:
删除并重建 db-2
等待 db-2 Ready
删除并重建 db-1
等待 db-1 Ready
删除并重建 db-0
等待 db-0 Ready
实际推进受 Pod 管理策略、Ready 状态和控制器实现约束影响。默认更新不会因为容器进程已经启动就立即认为完成;readiness probe、启动失败、卷挂载失败都可能阻塞后续更新。
检查状态:
kubectl rollout status statefulset/demo-db
kubectl rollout history statefulset/demo-db
kubectl get controllerrevision
StatefulSet 会使用 ControllerRevision 记录修订版本,便于控制器判断 Pod 是否需要更新。它不是完整的应用发布系统,也不会替代数据库迁移和回滚策略。
2. partition:先更新一个高序号成员
partition 将 StatefulSet 的 Pod 划分为更新区间。假设:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
通常表示只更新序号满足:
的 Pod。因此:
db-2:更新
db-1:保留旧模板
db-0:保留旧模板
这可以用于金丝雀式验证:先只让 db-2 运行新版本,确认兼容性后再逐步降低 partition。
例如:
kubectl patch statefulset demo-db --type=merge \
-p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
随后修改 Pod 模板镜像,观察只有 demo-db-2 先被替换。确认后将 partition 改为 0:
kubectl patch statefulset demo-db --type=merge \
-p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
不同 Kubernetes 版本对可选更新字段和行为细节可能存在差异,应以目标集群的 OpenAPI schema 和官方版本文档为准。
3. OnDelete:控制器不主动替换旧 Pod
配置:
updateStrategy:
type: OnDelete
后,修改 Pod 模板只会更新 StatefulSet 的期望模板;已有 Pod 不会因为模板变化而自动删除。只有手动删除 Pod,控制器才会按新模板重建它。
这给应用运维更强的控制权,但也增加了风险:StatefulSet 可能长期处于“模板已更新、部分 Pod 仍使用旧版本”的状态。需要明确记录每个序号的实际版本:
kubectl get pods -l app=demo-db \
-o custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image
八、更新为什么会卡住
StatefulSet 的顺序更新是有意保守的。以下场景都可能导致更新卡住:
1. Readiness probe 永远失败
kubectl describe pod demo-db-2
kubectl logs demo-db-2
应重点查看:
Readiness probe failed;- 应用监听地址或端口错误;
- 应用启动时间超过探针容忍范围;
- 探针依赖的外部服务不可用。
如果 db-2 没有 Ready,后续 db-1 和 db-0 可能不会继续更新。
2. PVC 无法挂载
典型事件包括:
FailedAttachVolume
FailedMount
Multi-Attach error
可能原因:
- 上一个 Pod 仍处于 Terminating;
- 云盘无法同时挂载到多个节点;
- 节点故障后卷的附件状态尚未清理;
- StorageClass 的拓扑约束与调度结果冲突。
此时不应直接强制删除 Pod。强制删除可能让 Kubernetes 很快创建同名新 Pod,而底层存储或旧进程仍未真正退出,形成两个进程同时操作同一数据的风险。应先确认旧进程、卷附件状态以及数据库是否安全停止。
3. 应用层拒绝启动
容器处于 Running 不代表应用已经能作为集群成员工作。例如:
- 新版本配置格式不兼容;
- 节点发现地址错误;
- 数据目录来自旧版本;
- 集群要求先执行迁移;
- 节点启动后发现自己不是合法成员。
Kubernetes 只能观察进程、端口和探针结果,不能替应用完成协议升级。
九、缩容、删除与 PVC 保留的边界
执行:
kubectl scale statefulset demo-db --replicas=1
在默认策略下,通常会删除:
demo-db-2
demo-db-1
并保留:
demo-db-0
data-demo-db-0
data-demo-db-1
data-demo-db-2
之后重新扩容到 3:
kubectl scale statefulset demo-db --replicas=3
控制器通常会重新创建 demo-db-1 和 demo-db-2,并尝试使用原来的 PVC。
这说明缩容并不等同于删除数据。它更接近于:
停止并移除高序号成员
保留这些成员的存储声明
对于数据库而言,缩容前仍然需要执行应用层操作,例如:
- 从集群成员列表中移除目标节点;
- 确认副本或分片已经迁移;
- 确认剩余节点仍满足法定人数;
- 再缩小 StatefulSet 副本数。
直接执行 Kubernetes 缩容,可能让数据库认为成员突然失联,甚至破坏多数派。
删除 StatefulSet 时,可以先查看对象和 PVC:
kubectl get statefulset demo-db
kubectl get pvc -l app=demo-db
如果目标是删除控制器但保留数据,应显式确认 PVC 保留策略,并避免把删除 PVC 的命令与删除 StatefulSet 混在一起。恢复前还要确认旧 Pod 已经真正停止、卷没有被其他实例使用。
十、StatefulSet 不会自动形成分布式系统
这是最重要的边界。
假设 StatefulSet 创建了 3 个 Pod:
db-0
db-1
db-2
它只保证 Kubernetes 层面的若干事实:
- 有 3 个期望副本;
- 每个副本有稳定序号;
- 每个副本可以关联独立 PVC;
- 可以按一定顺序创建、删除和更新;
- Pod 可以通过稳定名称被发现。
它不保证:
- 三个 Pod 之间已经复制数据;
- 数据已经持久化到多个故障域;
- 任意两个 Pod 可以自动组成多数派;
db-0一定是 Leader;- 删除
db-2一定安全; - Pod Ready 就代表数据库可写;
- 一个 PVC 的数据在其他 PVC 上存在副本;
- 备份一定可恢复。
1. 多数派的形式化条件
以典型多数派协议为例,若集群有 个成员,允许同时失效 个成员而仍保留多数派,通常需要:
原因是多数派大小为:
要在 个成员失效后仍有多数派,需要:
当 时:
多数派 = 2
可容忍失效 = 1
但这只说明成员数量满足一个数学条件。若三个 Pod 都位于同一节点、同一可用区或同一个存储故障域,那么一次节点或区域故障仍可能同时丢失全部成员。
因此,Kubernetes 的副本数与分布式系统的容错副本不是同一个概念。还需要结合:
- topology spread constraints;
- pod anti-affinity;
- StorageClass 的拓扑绑定;
- 数据库自身的复制协议;
- 备份和恢复流程。
2. Readiness 不等于数据库可用性
一个数据库 Pod 的 readiness probe 可能只检查 TCP 端口:
readinessProbe:
tcpSocket:
port: 5432
这只能证明端口可连接,不能证明:
- 节点已经加入集群;
- 节点数据已追平;
- 节点可以接受写请求;
- 当前节点不是只读副本;
- 当前事务日志已持久化;
- 集群仍有多数派。
更可靠的探针通常需要调用数据库提供的健康检查接口,并明确区分:
进程存活
实例已初始化
节点可接受连接
节点可接受写入
节点属于当前多数派
这些状态不能由 StatefulSet 自动推导。
十一、应用如何使用稳定身份
应用可以通过 Downward API 获得自己的 Pod 名称:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
容器内可能得到:
POD_NAME=demo-db-1
POD_NAMESPACE=default
应用可以从名称末尾解析序号,但这不是所有应用都应该采用的成员标识方案。更稳妥的做法是:
- 应用明确记录成员 ID;
- 启动时根据 Pod 名称和配置计算预期地址;
- 发现其他成员时进行重试;
- 对成员加入、退出和数据追赶实现幂等处理;
- 不把“序号较小”误认为“拥有更高权限”。
例如,db-0 可以作为初始引导地址,但 Leader 身份应由数据库协议或选举机制决定,而不是由 StatefulSet 名称决定。
十二、Operator 与 StatefulSet 的分工
直接编写 StatefulSet 适合:
- 学习和验证控制器行为;
- 部署有简单身份需求的服务;
- 应用已经自行实现集群初始化和成员管理;
- 运维人员愿意手动处理升级、备份和恢复。
当系统涉及复杂数据库协议时,Operator 通常负责比 StatefulSet 更多的事情,例如:
- 初始化集群;
- 生成或轮换证书;
- 执行安全扩缩容;
- 处理 Leader 迁移;
- 识别复制延迟;
- 执行版本升级;
- 创建备份和恢复任务;
- 配置监控和告警;
- 在故障后重建成员。
但 Operator 也不是数据安全的替代品。它仍然依赖底层 PV、CSI、网络、备份存储和正确的恢复测试。安装一个数据库 Operator,不等于已经建立了可验证的备份责任链。
十三、故障诊断的路径
遇到 StatefulSet 异常时,应先判断故障属于哪一层。
1. 控制器和模板层
kubectl get statefulset demo-db -o yaml
kubectl describe statefulset demo-db
关注:
selector是否匹配 Pod 标签;serviceName是否存在;replicas与 Ready 数量;- 更新策略和 partition;
- 事件中的创建或删除失败。
2. Pod 调度与容器层
kubectl get pods -l app=demo-db -o wide
kubectl describe pod demo-db-1
kubectl logs demo-db-1 --all-containers
关注:
Pending:调度、PVC、资源或拓扑问题;ContainerCreating:镜像、挂载、网络或 Secret 问题;CrashLoopBackOff:进程启动失败;Running但 NotReady:探针或业务健康状态问题。
3. PVC、PV 与存储层
kubectl get pvc,pv
kubectl describe pvc data-demo-db-1
kubectl get storageclass
关注:
- PVC 是否
Bound; - PVC 请求的 StorageClass 是否存在;
- PV 的访问模式和容量是否匹配;
- CSI 驱动是否报告 Attach、Mount 或拓扑错误;
- 文件系统扩容是否完成。
4. 应用集群层
即使 Kubernetes 对象全部正常,也要检查应用自身:
- 集群成员列表;
- Leader 或 Primary 状态;
- 复制延迟;
- 法定人数;
- WAL、日志或分片状态;
- 应用级备份和恢复验证。
这一步无法由 kubectl get pods 替代。
十四、常见误解与反例
误解一:三个 StatefulSet Pod 就是三副本数据
反例:三个 Pod 各自挂载三个独立 PVC,但应用只是把数据写到本地目录,没有复制协议。此时任意 PVC 损坏都可能导致对应数据丢失。
误解二:Pod 名称稳定,所以进程状态稳定
反例:db-1 被删除后,新 Pod 仍名为 db-1,但 UID、节点、IP、容器临时文件和内存状态都已经变化。应用必须从持久化数据和集群协议恢复,而不能依赖旧进程仍然存在。
误解三:OrderedReady 能自动完成数据库启动顺序
反例:db-0 已经 Ready,但它可能尚未完成数据库初始化,只是端口已经打开。StatefulSet 会继续创建 db-1,因为它只看 Kubernetes 的就绪条件。
误解四:缩容到一个副本就是安全删除其他数据库节点
反例:共识集群可能要求先执行成员移除。如果直接删除高序号 Pod,剩余成员可能仍把它视为投票成员,导致选举或提交受阻。
误解五:删除 StatefulSet 就删除了所有数据
默认情况下,StatefulSet 删除通常不会自动删除 PVC。但 PVC 的显式 retention policy、PV reclaim policy、云厂商存储控制器和人工清理命令都可能改变最终结果,必须逐层确认。
十五、什么时候不该使用 StatefulSet
以下场景通常不需要 StatefulSet:
- Web、API、Worker 等实例完全可互换;
- 应用不需要稳定主机名;
- 应用不需要每个副本独立持久卷;
- 副本启动和删除没有顺序要求;
- 使用共享对象存储或外部数据库,而不是每个 Pod 独立磁盘。
此时 Deployment 更直接。强行使用 StatefulSet 不会自动带来更高可靠性,反而可能引入:
- 更新被单个 Pod 阻塞;
- PVC 长期残留;
- 缩容操作复杂;
- 错误地把 Pod 序号当成数据库角色;
- 运维人员误以为副本已经具备数据冗余。
StatefulSet 适用于“实例身份和实例存储本身有意义”的系统,但它只是分布式系统的基础设施协调层。完整的有状态服务还需要应用协议、存储拓扑、备份恢复、升级兼容性和明确的责任边界共同成立。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Deployment 与 ReplicaSet:滚动更新、Revision、暂停、回滚和失败
- 下一篇:DaemonSet:节点级 Agent、调度、更新、容忍和资源治理
- 延伸:Kubernetes PV 与 PVC:绑定、访问模式、回收策略和生命周期
- 延伸:数据库运行在 Kubernetes:Operator、存储、拓扑、备份和责任边界
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论