讲解
Deployment 的 Pod 是可互换的「牲畜」:名字随机、IP 随机、存储共享不了,谁挂了补一个新的就行。但有一类应用接受不了这种匿名:数据库集群里主从要互相找对方(需要稳定网络身份)、每个实例要有自己的盘(需要稳定存储)、扩缩容要按顺序来(需要有序性)。StatefulSet 就是为这类「有状态应用」准备的控制器,它给每个副本三样保证:稳定的名字(podname-0、podname-1……序号固定)、稳定的 DNS 记录、稳定的独立存储(volumeClaimTemplates 给每个副本各建一个 PVC)。
稳定 DNS 依赖一个前置件:Headless Service(clusterIP: None 的 Service)。普通 Service 有一个虚拟 IP 做负载均衡;Headless Service 没有虚拟 IP,DNS 查询直接返回后端 Pod 的 IP。StatefulSet 里每个 Pod 的域名形如 pod名.服务名.命名空间.svc.cluster.local——比如 ohmydocs-verify-db-0.ohmydocs-verify-hl.ohmydocs-verify.svc.cluster.local,集群成员之间就靠这种固定名字互相寻址。清单上对应 serviceName 字段,指向那个 Headless Service。
行为上的差异也要适应:StatefulSet 默认按序号顺序创建(0 先就绪才创建 1)、逆序删除(podManagementPolicy: Parallel 可改为并行);扩缩容是逐个进行的,滚动更新也是按序号逆序来。删除 StatefulSet 不会删除它的 PVC(数据安全第一),要删得手动。选择标准很简单:实例之间没有区别、挂了无所谓——用 Deployment;实例有身份、有各自的数据、讲究顺序(MySQL 主从、ZooKeeper、Kafka、Elasticsearch)——用 StatefulSet。
示例
Headless Service(clusterIP: None)+ 2 副本 StatefulSet,组成一个「数据库集群」的骨架:
# wait: sts/ohmydocs-verify-db
apiVersion: v1
kind: Service
metadata:
name: ohmydocs-verify-hl
spec:
clusterIP: None
selector:
app: ohmydocs-verify-db
ports:
- port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ohmydocs-verify-db
spec:
serviceName: ohmydocs-verify-hl
replicas: 2
selector:
matchLabels:
app: ohmydocs-verify-db
template:
metadata:
labels:
app: ohmydocs-verify-db
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
Pod 名字是稳定的序号命名(db-0、db-1),不是 Deployment 那种随机后缀:
kubectl get pods -l app=ohmydocs-verify-db -o wide
kubectl get statefulset ohmydocs-verify-db
稳定的 DNS 身份:用客户端 Pod 解析 db-0 的固定域名,能直接解析到该 Pod 的 IP:
kubectl run ohmydocs-verify-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=ready pod/ohmydocs-verify-client --timeout=120s
kubectl exec ohmydocs-verify-client -- nslookup ohmydocs-verify-db-0.ohmydocs-verify-hl.ohmydocs-verify.svc.cluster.local | tail -3
kubectl delete pod ohmydocs-verify-client --wait
删掉 db-1,StatefulSet 会用同一个名字原地重建(名字不变=身份不变):
kubectl delete pod ohmydocs-verify-db-1 --wait
kubectl wait --for=condition=ready pod/ohmydocs-verify-db-1 --timeout=120s
kubectl get pods -l app=ohmydocs-verify-db
常见坑
- 忘了 Headless Service:没有 serviceName 指向的 Headless Service,Pod 的稳定 DNS 名不存在,集群成员互相找不到。
- 以为删 StatefulSet 会清数据:PVC 是单独留下的,删控制器不删数据;反过来想彻底重建时别忘了手动删 PVC,否则新实例挂的还是旧盘。
- 拿 StatefulSet 跑无状态应用:顺序创建、稳定身份的约束全是负担;web 类无状态服务一律 Deployment。
- 指望它自动做主从切换:StatefulSet 只保证身份和存储,主从选举、数据复制是应用自己的事(或交给对应的 Operator)。
小结
StatefulSet = 稳定名字 + 稳定 DNS(Headless Service)+ 稳定存储 + 有序创建删除;有状态应用(数据库、消息队列、协调服务)的归宿,无状态应用别用它。下一章看另一种特殊负载:每个节点一份的 DaemonSet。