一、StatefulSet 解决的核心问题是什么

有状态应用最怕两件事:

  • 实例一重建,身份就变了
  • 实例一扩缩,顺序和数据关系就乱了

StatefulSet 解决的正是这些问题。它为每个 Pod 提供:

  • 稳定的名称和序号
  • 稳定的网络标识
  • 可绑定的持久化存储语义
  • 有序的创建、更新和删除流程

这也是为什么 Eureka、MongoDB、Elasticsearch、Redis、Kafka 等场景会频繁使用 StatefulSet。

二、为什么 Headless Service 是 StatefulSet 的前提

StatefulSet 通常要配合 Headless Service 使用。所谓 Headless Service,本质上就是:

apiVersion: v1
kind: Service
metadata:
  name: my-headless-service
spec:
  clusterIP: None
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

clusterIP 设成 None 之后,这个 Service 不再承担普通负载均衡入口,而是把后端 Pod 的网络身份直接暴露出来。于是每个 StatefulSet Pod 都能拥有稳定域名。

典型格式如下:

statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local

比如 web-0.nginx.default.svc.cluster.local,看到这个名字你就能立刻知道它是谁、属于哪个 StatefulSet、挂在哪个服务名下面。

三、什么时候该选 StatefulSet

如果你的业务满足下面任意一条,就应优先考虑 StatefulSet:

  • 需要稳定且唯一的网络标识
  • 需要持久化数据
  • 需要按顺序启动、扩容、缩容
  • 需要按序滚动更新

同时也要知道它的限制:

  • 存储通常要依赖 StorageClass 或预制的 PV
  • 删除或缩容 StatefulSet 默认不会帮你删除关联存储
  • 需要你先准备好 Headless Service
  • 不恰当的更新策略可能导致修复过程更复杂

四、创建一个最小可用的 StatefulSet

原文给出的示例非常典型:先定义一个 Headless Service,再定义 StatefulSet。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.14.2
        ports:
        - containerPort: 80
          name: web

创建后可以直接观察:

kubectl create -f stateful.yaml
kubectl get sts
kubectl get svc
kubectl get pods -l app=nginx

五、StatefulSet 的“有序”到底体现在哪

StatefulSet 最值得记住的,不是 YAML 长什么样,而是它的顺序语义:

  • 创建时按 0 -> N-1 顺序拉起
  • 删除时按 N-1 -> 0 逆序停止
  • 前一个 Pod 没准备好,后一个 Pod 不会继续创建

这和 Deployment 最大的区别在于:Deployment 更关心“副本数达到”,而 StatefulSet 更关心“每个身份明确的副本按顺序达到”。

另外,StatefulSet 不适合把 terminationGracePeriodSeconds 设成 0。对有状态服务来说,优雅终止往往不是锦上添花,而是避免数据损坏、脑裂和脏状态的底线配置。