11.2 Kubernetes 有状态工作负载:身份、存储与恢复
把数据库 YAML 改成 StatefulSet 不等于获得了高可用数据库。Kubernetes 能维护 Pod 身份、挂载声明和编排顺序;复制协议、一致性、备份校验和故障切换仍由应用、operator 或托管服务负责。
PV、PVC 与 StorageClass 分工
Pod → PersistentVolumeClaim → PersistentVolume → 存储系统
↑
StorageClass 动态供应策略- PVC 表达工作负载需要多大的卷、访问模式和存储类;
- PV 表示集群中可绑定的存储资源;
- StorageClass 描述 provisioner、参数、回收策略和绑定方式。
访问模式如 ReadWriteOnce 描述卷的挂载约束,不等同于应用级并发写安全,也不直接说明底层存储的故障域和性能保证。
对拓扑受限的块存储,volumeBindingMode: WaitForFirstConsumer 可以等调度器选定节点区域后再供应卷,减少“Pod 在 A 区、卷在 B 区”造成的不可调度。
StatefulSet 提供稳定身份
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ledger
spec:
serviceName: ledger-headless
replicas: 3
selector:
matchLabels:
app: ledger
template:
metadata:
labels:
app: ledger
spec:
terminationGracePeriodSeconds: 60
containers:
- name: ledger
image: registry.example/ledger@sha256:REPLACE_ME
ports:
- name: peer
containerPort: 7000
volumeMounts:
- name: data
mountPath: /var/lib/ledger
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast-zonal
resources:
requests:
storage: 100GiPod 会获得 ledger-0、ledger-1 这样的稳定序号,并在重新调度后重新关联自己的 PVC。默认 OrderedReady 按序创建和终止;只有应用不依赖顺序时才考虑 Parallel。
StatefulSet 需要 headless Service 提供网络身份,但 DNS 名称稳定不代表进程一定健康。集群成员发现还要处理 readiness、脑裂防护和失联成员重加入。
PVC 生命周期默认偏向保数据
缩容或删除 StatefulSet 时,关联卷通常不会自动删除,这是有意的数据安全取舍。新项目应明确 persistentVolumeClaimRetentionPolicy、StorageClass 的 reclaimPolicy,以及删除 namespace 时谁负责最终确认。
扩容前先验证应用是否支持增加成员;缩容前先让应用层安全移除副本,再改变 replicas。直接把三副本数据库缩成一副本,控制器不会替你维护 quorum。
探针不能只检查端口打开
有状态服务常需要区分:
- startup:恢复日志、重放 WAL 或加入集群是否完成;
- readiness:该副本此刻能否安全接收目标类型的流量;
- liveness:进程是否已进入只能重启恢复的状态。
把“暂时落后”写成 liveness 失败会触发重启循环,反而永远追不上。把主库专属写能力与普通存活混在一个 readiness 中,也会让读流量无端中断。
快照不是完整备份策略
CSI VolumeSnapshot 可以快速捕获卷状态,但是否应用一致取决于存储实现和应用冻结流程。生产恢复方案要回答:
- 是 crash-consistent 还是 application-consistent?
- 数据库日志和多卷如何获得同一恢复点?
- 备份是否复制到独立账户、区域或故障域?
- 加密密钥、schema、配置和 operator 版本是否一起保留?
- 最近一次从空集群恢复演练花了多久,恢复到哪个时间点?
没有成功恢复记录的备份,只是尚未验证的一组对象。RPO 决定允许丢多少数据,RTO 决定服务多久必须恢复,两者共同推导备份频率、复制方式和演练周期。
Operator 的价值与边界
成熟数据库 operator 可以编码成员变更、滚动升级、备份和故障切换知识,但它也引入 CRD、webhook、controller 和版本兼容风险。采用前应检查:
- operator 与数据库版本升级矩阵;
- controller 不可用时数据面是否继续服务;
- CRD 删除和 finalizer 卡住时如何处置;
- 自动故障切换在网络分区下的 fencing 机制;
- 备份能否脱离原集群恢复。
若团队无法承担这些操作责任,托管数据库往往比“把数据库塞进 Kubernetes”更可靠。
上线检查
- 每个副本的身份和卷绑定是否可预测?
- 节点、可用区和存储是否共享同一故障域?
- 资源不足时是否会同时驱逐多数副本?
- 升级、缩容、节点 drain 和区域故障是否演练?
- PVC、快照、备份与密钥的删除权限是否分离?
- RPO/RTO 是否通过真实恢复数据验证?
下一课把资源、调度、扩缩容和中断预算组合起来,控制共享集群中的容量与故障半径。
参考资料
- Kubernetes, StatefulSets
- Kubernetes, Persistent Volumes
- Kubernetes, Volume Snapshots