StatefulSet 入门
为有状态应用提供稳定身份与存储。
StatefulSet 适合数据库、队列等有状态应用,核心是“稳定身份 + 稳定存储”。每个 Pod 都有固定名称,并绑定独立的 PVC,这让它适合需要持久化数据和稳定网络标识的系统。
本节在基础概念上补充 Headless Service、扩缩容行为、更新策略与运维要点。
适用场景
- 数据库、消息队列、有状态缓存。
- 依赖稳定主机名的服务。
- 每个副本需要独立存储的应用。
典型特性
- Pod 名称固定(如
mysql-0)。 - 与 PVC 一一绑定。
- 有序启动、扩缩容和滚动更新。
- 依赖 Headless Service 提供稳定 DNS。
Headless Service
StatefulSet 必须配合 Headless Service:
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
这样可以得到稳定 DNS,例如 mysql-0.mysql.default.svc.cluster.local。
核心片段
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
扩缩容行为
StatefulSet 按序扩容与缩容:扩容从 0 递增,缩容从最大序号开始。这对数据库更安全,但速度比 Deployment 慢。
滚动更新
默认从最大序号开始更新。可通过 updateStrategy 控制:
updateStrategy:
type: RollingUpdate
分区更新与灰度
当你不希望一次性更新所有副本时,可以使用分区策略。分区会让小于指定序号的 Pod 保持旧版本,其余副本更新。这个方式适合有主从拓扑的数据库,可以先更新从节点,验证无误后再推进到主节点。
稳定网络标识与访问方式
StatefulSet 的稳定 DNS 名称对于集群内部发现非常关键。典型模式是写请求走固定主节点,读请求走 Service 做负载均衡。对于需要直连副本的场景,可以通过 pod-ordinal.service.namespace.svc 直接访问指定实例,减少重连时的拓扑混乱。
PVC 命名与扩容
PVC 会按模板名称和序号拼接,例如 data-mysql-0。如果 StorageClass 允许扩容,通常可以在线扩容 PVC,但仍需确认文件系统是否需要额外的扩容步骤。建议在运维文档中记录扩容流程,避免在紧急情况下出错。
访问模式与共享存储
大多数有状态应用使用 ReadWriteOnce,因为每个副本独占一个卷。只有在明确需要共享存储时才考虑 ReadWriteMany,否则多个副本共享同一卷可能导致锁冲突、性能抖动或数据不一致。
Pod 管理策略
如果业务允许并行启动,可设置 podManagementPolicy: Parallel,但需确认应用能接受乱序启动。
存储规划
每个副本都会创建独立 PVC。数据库常用 SSD StorageClass,扩容前要计算总容量需求。
有序启动与就绪细节
StatefulSet 在默认策略下会等待前一个 Pod Ready 才启动下一个。readiness 探针应代表“真正可用”,但也要避免过度严格导致滚动更新卡住。对于需要初始化数据的场景,可以通过 init 容器完成引导,避免主容器在未准备好时接收流量。
探针与就绪
有状态服务的探针更要谨慎。readiness 要确保就绪后再接流量,liveness 不宜过于激进,避免恢复过程被反复重启。
示例说明
以 MySQL 为例,通常包括 Headless Service、Secret、以及 StatefulSet。本质区别是每个副本拥有独立存储和稳定身份。
备份与恢复
PVC 只能保证数据持久化,不能替代备份。建议配合快照、逻辑备份或运维工具定期导出数据,并演练恢复流程。对于有主从复制的系统,恢复顺序与主从切换策略要提前规划。
删除与回收策略
删除 StatefulSet 时 PVC 默认会保留,这是为了防止误删数据。若确实需要回收存储,应明确检查对应 PVC,并结合 StorageClass 的回收策略决定是否自动删除底层卷。
迁移与改造建议
从 Deployment 迁移到 StatefulSet 通常需要重新创建资源并迁移数据。可以先在新 StatefulSet 中创建空数据卷,再通过迁移工具同步数据,最后切换客户端到新的 Headless Service,尽量缩短不可用时间。
资源与性能
数据库等有状态应用对 CPU、内存和磁盘 IO 较为敏感。建议在测试环境压测后确定 requests 和 limits,避免因资源争抢引起延迟抖动。磁盘容量要预留扩容空间,防止满盘导致崩溃。
可观测与告警
监控不仅要看 Pod 状态,还要关注存储使用率、复制延迟、慢查询和磁盘延时。将这些指标与告警策略结合,可以更早发现隐患。
PDB 与维护窗口
通过 PodDisruptionBudget 限制同时被驱逐的副本数,可避免维护时造成服务不可用。维护前应评估副本数与业务承受能力,并在非高峰期执行升级或节点维护。
常见问题
- Pod Pending:PVC 未绑定或 StorageClass 缺失。
- 初始化卡住:应用需要有序启动或数据引导。
- 更新阻塞:readiness 探针失败。
排查命令
kubectl get pods
kubectl get pvc
kubectl describe pod <pod-name>
kubectl get events -A
实操建议
- 先单副本验证,再扩容。
- PVC 不是备份,仍需制定备份策略。
- 使用反亲和性将副本分散到不同节点。
快速检查清单
- 资源定义与业务意图一致。
- Namespace、权限、镜像与环境匹配。
- 上线前具备健康探针与可观测日志。
- 单副本存储容量规划充足。
收个尾:StatefulSet 的“麻烦”是正常的
有状态服务的升级和恢复,天然就比无状态慢。这不是你配置写错了,而是数据和卷的约束决定的。
上线前你可以简单记三件事:
- PVC 不是备份:一定要有备份/恢复演练,不然出事只能祈祷。
- 恢复会慢:卷重新 attach/mount 需要时间,readiness 也要反映真实可用。
- 升级要分批:别一次滚全量,先从 0 号或小分区开始,观察再推进。