StatefulSet 控制器为 Kubernetes 有状态应用提供了稳定标识、持久存储和有序部署,是数据库、消息队列等关键服务高可用的基础保障。
StatefulSet 是 Kubernetes 中专门用于管理有状态应用的控制器。与 Deployment 和 ReplicaSet 为无状态服务设计不同,StatefulSet 为 Pod 提供唯一标识,并保证部署和扩缩容的有序性。
StatefulSet 主要解决有状态服务的问题,其典型应用场景包括:
StatefulSet 由以下几个关键部分组成:
StatefulSet 中每个 Pod 的 DNS 格式如下,便于集群内服务发现和通信:
<statefulSetName>-<ordinal>.<serviceName>.<namespace>.svc.cluster.local
其中:
statefulSetName:StatefulSet 的名称ordinal:Pod 的序号(从 0 开始)serviceName:Headless Service 的名称namespace:所在的命名空间cluster.local:集群域名StatefulSet 适用于具有以下一个或多个需求的应用:
如果应用不需要稳定的标识符或有序部署,建议使用 Deployment 或 ReplicaSet。
pod.Spec.TerminationGracePeriodSeconds 设置为 0,这样做不安全以下 YAML 示例展示了一个典型的 nginx 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: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 1Gi
StatefulSet 通过序数和 DNS 规则为每个 Pod 提供唯一身份,便于服务发现和数据隔离。
对于有 N 个副本的 StatefulSet,每个副本都有一个唯一的整数序数,范围在 [0,N) 之间。
每个 Pod 的主机名遵循 $(statefulset 名称)-$(序数) 的模式。上述示例将创建名为 web-0、web-1、web-2 的 Pod。
DNS 解析示例:
| 集群域 | Service | StatefulSet | Pod DNS | Pod 主机名 |
|---|---|---|---|---|
| cluster.local | default/nginx | default/web | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
Kubernetes 会为每个 VolumeClaimTemplate 创建 PersistentVolume。Pod 重新调度时,volumeMounts 会挂载对应的 PersistentVolume。需要注意的是,删除 Pod 或 StatefulSet 时,PersistentVolume 不会被自动删除。
StatefulSet 在部署和扩缩容过程中,严格保证 Pod 的有序性和依赖关系。
StatefulSet 支持两种 Pod 管理策略,适应不同业务场景。
按序启动和终止 Pod,确保前一个 Pod 就绪后再启动下一个。
并行启动和终止所有 Pod,不等待其他 Pod 状态。
spec:
podManagementPolicy: "Parallel"
StatefulSet 支持多种更新策略,满足不同的升级需求。
手动删除 Pod 后才会重新创建新版本的 Pod。
spec:
updateStrategy:
type: OnDelete
自动滚动更新,按序数从大到小更新 Pod。
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
通过设置 partition 参数可以实现分段更新:
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只更新序数 >= 2 的 Pod
以下命令展示了 StatefulSet 的常用运维操作。
# 创建 StatefulSet
kubectl apply -f web.yaml
# 查看 Service 和 StatefulSet
kubectl get service nginx
kubectl get statefulset web
# 查看自动创建的 PVC
kubectl get pvc
# 查看 Pod 状态
kubectl get pods -l app=nginx
# 扩容到 5 个副本
kubectl scale statefulset web --replicas=5
# 缩容到 3 个副本
kubectl patch statefulset web -p '{"spec":{"replicas":3}}'
# 更新镜像
kubectl patch statefulset web --type='json' \
-p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.21"}]'
# 删除 StatefulSet(保留 PVC)
kubectl delete statefulset web
# 删除 Service
kubectl delete service nginx
# 清理 PVC(可选)
kubectl delete pvc www-web-0 www-web-1 www-web-2
# 创建测试 Pod 验证 DNS 解析
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup web-0.nginx.default.svc.cluster.local
以下 YAML 示例展示了生产级 ZooKeeper StatefulSet 的配置方式:
apiVersion: v1
kind: Service
metadata:
name: zk-headless
labels:
app: zookeeper
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zookeeper
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-headless
replicas: 3
selector:
matchLabels:
app: zookeeper
template:
metadata:
labels:
app: zookeeper
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- zookeeper
topologyKey: kubernetes.io/hostname
containers:
- name: zookeeper
image: zookeeper:3.7
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
env:
- name: ZK_REPLICAS
value: "3"
- name: ZK_HEAP_SIZE
value: "1G"
- name: ZK_CLIENT_PORT
value: "2181"
- name: ZK_SERVER_PORT
value: "2888"
- name: ZK_ELECTION_PORT
value: "3888"
readinessProbe:
exec:
command:
- sh
- -c
- "echo ruok | nc localhost 2181 | grep imok"
initialDelaySeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command:
- sh
- -c
- "echo ruok | nc localhost 2181 | grep imok"
initialDelaySeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /data
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
对于需要从集群外部访问 StatefulSet 中特定 Pod 的场景,可以通过以下方式实现。
# 为特定 Pod 添加标签
kubectl label pod zk-0 instance=zk-0
kubectl label pod zk-1 instance=zk-1
# 暴露为 NodePort 服务
kubectl expose pod zk-0 --port=2181 --target-port=2181 \
--name=zk-0-external --selector=instance=zk-0 --type=NodePort
kubectl expose pod zk-1 --port=2181 --target-port=2181 \
--name=zk-1-external --selector=instance=zk-1 --type=NodePort
apiVersion: v1
kind: Service
metadata:
name: zk-0-lb
spec:
type: LoadBalancer
ports:
- port: 2181
targetPort: 2181
selector:
statefulset.kubernetes.io/pod-name: zk-0
在生产环境中,建议遵循以下最佳实践以提升有状态服务的可靠性和可维护性。
常见问题及解决方案如下:
StatefulSet 是 Kubernetes 管理有状态应用的核心控制器,提供稳定标识、持久存储和有序部署等能力。通过合理配置 Headless Service、PVC、Pod 管理策略和更新策略,可以高效支撑数据库、消息队列等关键业务场景。建议结合最佳实践和监控体系,持续优化有状态服务的高可用性和可维护性。