返回首页

5.3:StatefulSet 与有状态应用

说明:本文基于 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 理解为三个稳定性的组合:

  1. 稳定网络身份:Pod 名称固定,例如 mysql-0mysql-1
  2. 稳定存储身份:每个 Pod 都能拿到自己的独立 PVC
  3. 稳定启动/终止顺序:按照编号顺序创建和销毁

只要一个系统需要上面任意两项,通常就值得认真考虑 StatefulSet。

3. StatefulSet 的稳定身份:为什么这么关键

3.1 稳定 Pod 名称

Deployment 下的 Pod 名称通常带随机后缀,比如 web-7ccf9d8b7f-abcde,重建后名字会变化。StatefulSet 则采用固定编号命名,例如:

  • app-0
  • app-1
  • app-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-0
  • data-mysql-1
  • data-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-0web-1web-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 时,可以问自己四个问题:

  1. 每个副本是否需要稳定名称?
  2. 每个副本是否需要独立持久卷?
  3. 启动和关闭顺序是否重要?
  4. 节点之间是否需要通过稳定 DNS 互相发现?

如果这四个问题里有两个以上回答“是”,大概率就已经进入 StatefulSet 场景了。

12. 小结

本章最重要的结论有三条:

  • Deployment 适合无状态,StatefulSet 适合有状态
  • StatefulSet 通过稳定身份、有序部署、专属存储来支撑复杂系统
  • Headless Service 是 StatefulSet 形成稳定服务发现能力的关键拼图

下一章我们将继续深入到底层机制:CSI(Container Storage Interface),看看 Kubernetes 是如何把各种不同厂商、不同类型的存储系统统一接进来的。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

1.3 快速上手:搭建本地实验环境

下一篇

8.3 多集群管理