一、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。对有状态服务来说,优雅终止往往不是锦上添花,而是避免数据损坏、脑裂和脏状态的底线配置。