K8s StatefulSet 详解
核心定义 StatefulSet 是 Kubernetes 用于管理有状态应用的工作负载控制器。它为每个 Pod 提供稳定的名称、稳定的网络身份、独立的持久化存储和有序的创建、更新、删除语义,常用于数据库、消息队列、协调服务等有状态系统。
1. StatefulSet 解决什么问题
Deployment 适合无状态应用,Pod 可以任意替换;但有状态应用通常需要固定身份和稳定数据。
典型需求:
- 每个副本都有固定名称,例如
mysql-0、mysql-1。 - 每个副本都有稳定 DNS,例如
mysql-0.mysql.default.svc.cluster.local。 - 每个副本绑定自己的 PVC,重建后仍挂载原数据。
- 启动、扩容、缩容和更新需要按顺序执行。
- 应用内部可能依赖成员序号进行主从、分片或选主。
StatefulSet 正是为这些需求设计的。
2. StatefulSet 与 Deployment 对比
| 对比项 | Deployment | StatefulSet |
|---|---|---|
| 适用应用 | 无状态应用 | 有状态应用 |
| Pod 名称 | 随机后缀,例如 web-7d8b9 | 有序编号,例如 mysql-0、mysql-1 |
| 网络身份 | Pod IP 变化,名称不稳定 | 基于 Headless Service 的稳定 DNS |
| 存储 | 通常共享或无状态 | 每个 Pod 独立 PVC |
| 启动顺序 | 通常并行 | 默认按 0、1、2 顺序启动 |
| 删除顺序 | 通常无严格顺序 | 默认按最大序号到最小序号删除 |
| 更新策略 | RollingUpdate | RollingUpdate、OnDelete、Partition |
| 典型场景 | Web、API、无状态服务 | MySQL、Kafka、ZooKeeper、Redis Cluster |
3. 三个核心稳定性
3.1. 稳定 Pod 名称
StatefulSet Pod 名称由 StatefulSet 名称和序号组成。
<StatefulSet 名称>-<序号>
例如副本数为 3:
mysql-0
mysql-1
mysql-2
即使 mysql-1 被删除,控制器重建后仍然叫 mysql-1。
3.2. 稳定网络身份
StatefulSet 通常配合 Headless Service 使用。Headless Service 设置:
clusterIP: None
Pod 的稳定 DNS 形式:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
示例:
mysql-0.mysql.default.svc.cluster.local
mysql-1.mysql.default.svc.cluster.local
mysql-2.mysql.default.svc.cluster.local
这对主从复制、分片成员发现和集群选主非常重要。
3.3. 稳定存储
StatefulSet 通过 volumeClaimTemplates 为每个 Pod 自动创建独立 PVC。
mysql-0 → data-mysql-0
mysql-1 → data-mysql-1
mysql-2 → data-mysql-2
Pod 删除后,PVC 默认不会自动删除。Pod 重建后会重新挂载原 PVC,从而保留数据。
4. 基本 YAML 示例
Headless Service:
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- name: mysql
port: 3306
StatefulSet:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
关键字段:
| 字段 | 作用 |
|---|---|
serviceName | 指向 Headless Service,用于生成稳定 DNS |
replicas | 副本数量 |
selector | 匹配被管理的 Pod |
volumeClaimTemplates | 为每个 Pod 创建独立 PVC |
5. 创建、扩容与缩容顺序
5.1. 创建顺序
默认情况下,StatefulSet 会按序号从小到大创建:
mysql-0 → mysql-1 → mysql-2
前一个 Pod Ready 后,才继续创建下一个 Pod。
5.2. 缩容顺序
缩容时按序号从大到小删除:
mysql-2 → mysql-1 → mysql-0
这对有主从关系或成员顺序依赖的应用更安全。
5.3. podManagementPolicy
podManagementPolicy 控制 Pod 管理顺序。
| 策略 | 含义 |
|---|---|
OrderedReady | 默认,按顺序创建和删除 |
Parallel | 并行创建或删除 Pod |
Parallel 适合不依赖顺序的有状态服务,但使用前需要确认应用自身能处理并发启动。
6. 更新策略
6.1. RollingUpdate
默认策略。StatefulSet 会按序号从大到小更新 Pod。
mysql-2 → mysql-1 → mysql-0
这样可以避免低序号节点在某些有状态系统中被过早更新。
6.2. OnDelete
OnDelete 表示 StatefulSet 模板更新后,不会自动重建 Pod。只有手动删除 Pod 时,才会按新模板重建。
适合需要人工控制更新节奏的数据库或中间件。
6.3. Partition 灰度更新
partition 可用于灰度更新,只更新序号大于等于 partition 的 Pod。
例如副本数为 5,partition 为 3:
更新 mysql-3、mysql-4
保留 mysql-0、mysql-1、mysql-2
示例:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 3
7. PVC 生命周期注意点
StatefulSet 删除 Pod 或缩容时,PVC 默认保留。这是为了避免误删数据。
常见现象:
删除 mysql-1 Pod → PVC data-mysql-1 仍保留
重建 mysql-1 Pod → 重新挂载 data-mysql-1
删除 StatefulSet 不等于删除数据 删除 StatefulSet 或缩容副本不会自动删除 PVC。需要清理数据时,应明确确认 PVC 和 PV 的回收策略,避免误删生产数据。
8. 常见应用场景
| 场景 | 为什么适合 StatefulSet |
|---|---|
| MySQL 主从 | 每个节点需要固定身份和独立数据目录 |
| Kafka | Broker ID、日志目录和网络身份需要稳定 |
| ZooKeeper | 节点 myid 和成员 DNS 需要稳定 |
| Redis Cluster | 分片节点和持久化数据需要绑定 |
| Elasticsearch | 数据节点需要独立存储和稳定拓扑 |
9. 常见排查命令
查看 StatefulSet:
kubectl get statefulset -n <namespace>
kubectl describe statefulset <name> -n <namespace>
查看 Pod 顺序和状态:
kubectl get pod -n <namespace> -o wide
查看 PVC:
kubectl get pvc -n <namespace>
kubectl describe pvc <pvc_name> -n <namespace>
查看 Headless Service:
kubectl get svc -n <namespace>
kubectl describe svc <service_name> -n <namespace>
测试 DNS:
kubectl exec -it <pod> -n <namespace> -- nslookup mysql-0.mysql.default.svc.cluster.local
10. 常见问题
10.1. Pod 卡在创建阶段
常见原因:
- PVC 无法绑定。
- StorageClass 不存在或 CSI 异常。
- 前一个序号 Pod 未 Ready,导致后续 Pod 不创建。
- 镜像拉取失败或探针配置错误。
10.2. DNS 无法解析
排查方向:
- Headless Service 是否存在。
- StatefulSet 的
serviceName是否正确。 - Pod labels 是否匹配 Service selector。
- CoreDNS 是否正常。
10.3. 更新卡住
常见原因:
- 高序号 Pod 更新后未 Ready。
- readinessProbe 配置过严。
- 新版本应用启动失败。
- PVC 挂载或权限异常。
11. 面试回答模板
标准回答 StatefulSet 是 Kubernetes 用于管理有状态应用的控制器。它和 Deployment 最大的区别是提供稳定 Pod 名称、稳定 DNS、独立 PVC 和有序创建、更新、删除语义。StatefulSet 通常配合 Headless Service 生成固定域名,并通过
volumeClaimTemplates为每个 Pod 创建独立 PVC。默认创建顺序是从 0 到 N,删除和滚动更新通常从最大序号开始。它适合 MySQL、Kafka、ZooKeeper、Redis Cluster 等依赖稳定身份和持久化数据的服务。
关联文档
- K8s 核心概念:理解 StatefulSet 与 Deployment、Pod、Service 的关系。
- PV & PVC 完整深度详解:理解 StatefulSet 独立持久化存储。
- StorageClass + CSI 动态存储体系:理解动态卷供给和 CSI。
- K8s Service DNS 实现原理:理解 Headless Service 与稳定 DNS。
