Kubernetes 有状态服务与存储实践:StatefulSet、PVC 与数据可靠性
Kubernetes 最初给人的印象是适合无状态服务:Pod 可以随时销毁、重建、漂移。但现实中,团队总会遇到数据库、消息队列、搜索引擎、缓存集群、对象存储网关等有状态组件。能不能在 K8s 上跑有状态服务,答案不是简单的能或不能,而是要看你是否理解身份、存储、备份、调度和恢复的边界。
有状态服务的核心诉求是稳定身份、稳定存储、有序变更和可靠恢复。Deployment 适合无状态副本,StatefulSet 更适合需要固定网络标识和持久卷绑定的服务。
StatefulSet 的价值
StatefulSet 为每个 Pod 提供稳定序号,例如 mysql-0、mysql-1、mysql-2。这些名称不会因为重建而变化。配合 Headless Service,可以得到稳定 DNS:
mysql-0.mysql.default.svc.cluster.local
mysql-1.mysql.default.svc.cluster.local
这对数据库主从、共识系统、分片系统非常重要。很多有状态系统需要节点身份参与集群成员管理,如果 Pod 名称和存储随意变化,就容易造成脑裂、重复成员或数据目录错配。
PVC 与 StorageClass
PVC 是应用对存储的声明,PV 是具体存储资源,StorageClass 定义动态供应方式。生产环境要重点关注 StorageClass 的几个能力:
- 是否支持动态创建卷。
- 是否支持在线扩容。
- 是否支持快照。
- 是否支持多可用区。
volumeBindingMode是立即绑定还是等待调度。- 回收策略是 Retain 还是 Delete。
对多可用区集群,推荐使用 WaitForFirstConsumer,让卷绑定与 Pod 调度协同,避免卷创建在一个可用区,而 Pod 被调度到另一个可用区。
volumeBindingMode: WaitForFirstConsumer
不要忽略回收策略
StorageClass 的 reclaimPolicy 决定 PVC 删除后底层 PV 如何处理。对生产数据库,误删 PVC 后直接删除底层磁盘是高风险行为。很多团队会为核心数据类 StorageClass 设置 Retain,让底层卷保留,人工确认后再清理。
这并不能替代备份。它只是降低误删立刻丢数据的风险。
有状态服务的调度
有状态服务通常要考虑磁盘性能、可用区分布、节点稳定性和反亲和性。建议把关键有状态服务调度到专用节点池,并避免多个副本落在同一节点。
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: redis
topologyKey: kubernetes.io/hostname
如果业务要求跨可用区高可用,还要结合应用自身复制能力。K8s 能帮助调度,但不能自动解决数据库一致性问题。
备份与恢复优先于部署
上线有状态服务前,先设计恢复流程。一个服务能部署成功,不代表它能在事故中恢复。生产检查清单包括:
- 是否有定期全量和增量备份。
- 是否能恢复到独立环境验证。
- 是否记录恢复时间目标 RTO 和恢复点目标 RPO。
- 是否测试过 PVC 误删、节点丢失、单副本损坏。
- 是否有跨区域或离线备份。
备份的价值只有在恢复演练后才成立。没有演练的备份,只是心理安慰。
扩容与缩容
StatefulSet 扩容通常相对安全:新增序号 Pod,并创建对应 PVC。缩容要谨慎,因为 PVC 默认不会自动删除。这是设计上的保护,避免缩容误删数据。
缩容有状态集群前,要先让应用层完成成员下线。例如从数据库复制集移除节点,从消息队列集群迁移分片,再缩小 StatefulSet 副本。否则可能出现数据不完整、集群状态异常或长时间恢复。
滚动升级
StatefulSet 默认按序滚动更新,通常从最高序号 Pod 开始。对数据库和中间件,升级前应确认:
- 版本兼容性。
- 数据格式是否可回滚。
- 主从角色切换策略。
- readinessProbe 是否能真实表达服务可用。
- PodDisruptionBudget 是否保护最小可用副本。
如果升级涉及数据格式不可逆变更,应先在测试环境用生产备份恢复验证。不要把生产 StatefulSet 当作升级实验场。
探针设计
有状态服务的 livenessProbe 不能过于激进。数据库在高负载、恢复日志、执行主从同步时可能短暂响应慢,如果 livenessProbe 频繁杀进程,会让恢复过程反复中断。
建议:
- readinessProbe 用于判断是否接收流量。
- livenessProbe 只判断进程是否彻底失效。
- startupProbe 用于保护慢启动服务。
对数据库,readiness 可以检查只读/读写角色是否符合预期,而不是简单 TCP 端口通就算可用。
什么时候不该放进 K8s
不是所有有状态服务都适合自建在 K8s。以下情况可以优先考虑托管服务:
- 团队缺少数据库运维经验。
- 数据价值极高,恢复要求严苛。
- 需要跨区域容灾但团队没有成熟方案。
- 业务更需要稳定交付而不是底层控制权。
K8s 提供的是编排能力,不是数据库专家系统。运行 MySQL、PostgreSQL、Kafka、Elasticsearch 时,仍然要理解这些系统本身。
总结
有状态服务上 K8s 的关键不是 YAML 写法,而是数据生命周期管理。StatefulSet、PVC、CSI、StorageClass 只是工具。真正决定可靠性的,是备份恢复、调度隔离、容量规划、升级流程和应用自身的一致性机制。只要把这些边界讲清楚、演练充分,K8s 可以成为有状态服务的稳定运行平台;否则,它只会把数据风险包装成看起来整齐的清单。
适用场景
适合正在处理 K8s、Kubernetes, StatefulSet, Storage, PVC 相关问题的团队,用于快速建立排查路径和交付标准。
问题背景
讲解 K8s 中有状态服务的身份、存储、调度、备份、扩缩容和故障恢复实践,帮助团队避免把数据库简单当成 Deployment 运行。
排查步骤
先确认影响范围和最近变更,再收集日志、配置、指标和链路数据,最后按风险从低到高执行修复。
命令示例
示例命令请替换为你的真实资源名,并使用环境变量保存账号、密码、token 等敏感信息。
风险说明
生产环境操作前需要确认备份、权限边界、变更窗口和回滚路径,避免扩大故障影响。
回滚方案
保留原配置和发布版本;如修复后指标异常,立即回退配置、镜像或数据库变更并复核日志。
交付清单
问题定位记录、关键命令、修复步骤、验证结果、后续优化建议。
遇到类似技术问题?
如果你的服务器、K8s、Docker、CI/CD、数据库或监控系统出现类似问题,可以提交日志和配置文件,我们帮你远程诊断。