说明:本文基于 Kubernetes v1.32 官方 StatefulSet 文档与示例整理,重点理解“稳定身份、有序部署、专属存储”这三件事为什么让 StatefulSet 成为有状态应用的核心控制器。::cite[320]
1. 为什么 Deployment 不适合所有场景
Deployment 非常适合无状态服务,例如普通 Web API、前端服务、任务消费端等。因为这些应用通常满足两个前提:Pod 可以任意替换,实例之间没有固定身份。但一旦应用开始依赖稳定主机名、固定网络标识、独立存储目录,Deployment 就不再是最自然的选择。::cite[320]
举几个典型例子:
- MySQL 主从节点需要可识别的实例身份
- Kafka Broker 需要稳定的节点编号
- Elasticsearch 节点需要稳定名称与数据目录
- ZooKeeper、Etcd、Cassandra 等集群需要按编号组网
这些系统关心的不是“有 3 个副本就行”,而是“第 0 个、副本 1 个、副本 2 个分别是谁”。这正是 StatefulSet 出现的背景。::cite[320]
2. StatefulSet 到底解决了什么问题
Kubernetes v1.32 官方概念文档把 StatefulSet 定义为用于管理有状态应用的工作负载 API 对象。它和 Deployment 最大的不同,在于它为每个 Pod 提供稳定的身份标识,并支持有序创建、删除和滚动更新。::cite[320]
可以把 StatefulSet 理解为三个稳定性的组合:
- 稳定网络身份:Pod 名称固定,例如
mysql-0、mysql-1 - 稳定存储身份:每个 Pod 都能拿到自己的独立 PVC
- 稳定启动/终止顺序:按照编号顺序创建和销毁
只要一个系统需要上面任意两项,通常就值得认真考虑 StatefulSet。
3. StatefulSet 的稳定身份:为什么这么关键
3.1 稳定 Pod 名称
Deployment 下的 Pod 名称通常带随机后缀,比如 web-7ccf9d8b7f-abcde,重建后名字会变化。StatefulSet 则采用固定编号命名,例如:
app-0app-1app-2
这个编号不是展示效果,而是集群协议设计的重要基础。很多数据库或分布式系统会直接把“节点编号”和“角色”挂钩。::cite[320]
3.2 稳定网络身份
StatefulSet Pod 会结合 Headless Service 获得可预测的 DNS 名称,形如:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
例如:
mysql-0.mysql.default.svc.cluster.local
这意味着集群内部节点间可以通过固定域名彼此发现,而不必每次重新感知随机 Pod 地址。StatefulSet 教程和 Cassandra 示例都依赖这个机制。::cite[319]
3.3 稳定存储身份
StatefulSet 通常与 volumeClaimTemplates 搭配使用。每个副本会自动生成专属 PVC,例如:
data-mysql-0data-mysql-1data-mysql-2
这样即使某个 Pod 被重新调度,只要 PVC 还在,它仍然能拿回自己的那份数据,而不是混用其他实例的数据目录。::cite[322]
4. StatefulSet 的有序部署与有序终止
StatefulSet 的另一个核心价值,是“顺序感”。默认情况下,它会按 0 -> 1 -> 2 的顺序创建 Pod,并在缩容或删除时按相反顺序处理。这种机制对于需要逐个启动、逐个加入集群、逐个下线的系统非常重要。::cite[319]
典型好处包括:
- 主节点先起来,副本再跟进
- 初始化脚本可以依赖前置节点存在
- 滚动升级更容易与数据库复制、选主机制配合
相比之下,Deployment 更强调副本数一致与快速替换,不保证实例身份,也不强调严格顺序。
5. Headless Service 与 StatefulSet 为什么总是一起出现
如果说 StatefulSet 负责“编号管理”,那么 Headless Service 负责“编号可发现”。Headless Service 的核心特征是:
clusterIP: None
这表示它不提供一个统一的 ClusterIP 负载均衡入口,而是直接把后端 Pod 的 DNS 记录暴露出来。StatefulSet 官方教程和 Cassandra 官方示例都要求先准备 Headless Service。::cite[319]
5.1 它们的配合关系
- StatefulSet 生成稳定 Pod 名称
- Headless Service 让这些 Pod 拥有稳定 DNS 入口
- 分布式应用通过固定主机名互相发现
如果没有 Headless Service,很多有状态系统虽然还能跑,但很难优雅地做节点发现和集群组网。::cite[321]
6. 实操:创建一个最小可运行的 StatefulSet
这一节我们用一个最小示例体验 StatefulSet 的稳定身份与存储模板机制。
6.1 YAML:Headless Service + StatefulSet
apiVersion: v1
kind: Service
metadata:
name: web
spec:
clusterIP: None
selector:
app: web
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: web
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: demo-csi-sc
6.2 执行步骤
kubectl apply -f statefulset-demo.yaml
kubectl get pods -l app=web
kubectl get pvc
kubectl get svc web
你通常会看到:
- Pod 名称按
web-0、web-1、web-2出现 - 每个 Pod 都生成一份专属 PVC
- Service
web是 Headless Service
6.3 验证稳定身份
查看 Pod DNS:
kubectl exec -it web-0 -- hostname
kubectl exec -it web-1 -- hostname
理论上会分别返回:
web-0
web-1
即使你删除 web-1,重新创建后它依然叫 web-1,并会继续挂载原来那份 PVC。这就是 StatefulSet 的“身份稳定性”。::cite[320]
7. StatefulSet 的更新和缩容要注意什么
7.1 滚动更新不是“随便替换”
StatefulSet 支持滚动更新,但更新时要特别关注应用本身是否允许逐节点替换。例如对数据库、注册中心、消息队列来说,升级顺序、健康检查、同步延迟都可能影响整体可用性。
7.2 缩容不等于删除数据
当 StatefulSet 从 3 副本缩容到 2 副本时,web-2 Pod 会被删除,但对应的 PVC 往往不会自动消失。这样做的好处是:如果后续再扩容回 3,原实例数据还有机会被复用。实际生产中,是否清理旧 PVC,要根据业务恢复策略来决定。::cite[320]
8. 典型场景:数据库、消息队列与分布式存储
8.1 数据库
MySQL、PostgreSQL、MongoDB 等数据库往往需要:
- 独立数据目录
- 稳定节点名
- 有序启动与恢复
尤其在主从、复制集、分片架构中,实例身份不能随意变化。StatefulSet 非常适合承载这类场景。
8.2 消息队列
Kafka、RabbitMQ、Pulsar 等消息系统通常会把 Broker 身份、分区、副本同步、持久化数据目录绑定在具体节点上。Deployment 可以拉起 Pod,但很难优雅表达这些“编号型”身份关系。
8.3 分布式存储
像 Cassandra、ZooKeeper、Etcd 这类系统,本身就要求节点间通过固定身份互认。Kubernetes 官方 Cassandra 示例就是典型 StatefulSet 场景,同时使用 Headless Service 做 DNS 发现。::cite[321]
9. 实操:观察 StatefulSet 的有序行为
9.1 扩容
kubectl scale statefulset web --replicas=4
kubectl get pods -w
你会看到 web-3 在前面副本稳定后再创建。
9.2 缩容
kubectl scale statefulset web --replicas=2
kubectl get pods -w
kubectl get pvc
你会发现 Pod 被按逆序删除,而 PVC 往往仍保留。
9.3 删除单个 Pod
kubectl delete pod web-1
kubectl get pods -w
重建出来的依旧会是 web-1,这非常适合理解“稳定身份”和“控制器自愈”是如何叠加工作的。
10. StatefulSet 常见误区
10.1 误区一:有持久卷就一定要用 StatefulSet
不一定。如果只是一个单实例服务,且不需要稳定网络身份,只想用 PVC 保存数据,那么 Deployment + PVC 也可能够用。StatefulSet 的关键价值不只是“能挂盘”,而是“稳定身份 + 顺序控制 + 每副本专属卷”。
10.2 误区二:StatefulSet 自带高可用
StatefulSet 只是控制器,不会自动帮你解决数据库复制、主从切换、脑裂保护、故障转移等所有问题。高可用仍然取决于应用本身或其 Operator 设计。
10.3 误区三:Headless Service 没必要
如果你的应用需要稳定 DNS 发现,那 Headless Service 往往不是“可选项”,而是 StatefulSet 的天然搭档。::cite[319]
11. 一个更贴近生产的思考模型
当你评估某个应用是否应该使用 StatefulSet 时,可以问自己四个问题:
- 每个副本是否需要稳定名称?
- 每个副本是否需要独立持久卷?
- 启动和关闭顺序是否重要?
- 节点之间是否需要通过稳定 DNS 互相发现?
如果这四个问题里有两个以上回答“是”,大概率就已经进入 StatefulSet 场景了。
12. 小结
本章最重要的结论有三条:
- Deployment 适合无状态,StatefulSet 适合有状态
- StatefulSet 通过稳定身份、有序部署、专属存储来支撑复杂系统
- Headless Service 是 StatefulSet 形成稳定服务发现能力的关键拼图
下一章我们将继续深入到底层机制:CSI(Container Storage Interface),看看 Kubernetes 是如何把各种不同厂商、不同类型的存储系统统一接进来的。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!