Kubernetes StatefulSet 完全指南
Kubernetes StatefulSet 完全指南(适用于 Kubernetes v1.35)
文档说明
- 适用版本:Kubernetes v1.35.4(apiVersion: apps/v1)
- 前置知识:了解 Pod、Deployment、PV/PVC 的基本概念
- 目标:从零掌握 StatefulSet 的原理、配置与使用
第一章:为什么需要 StatefulSet?
1.1 无状态 vs 有状态
在 Kubernetes 中,Deployment 和 ReplicaSet 是为无状态应用设计的:
- Pod 是可互换的,任何一个 Pod 都可以被另一个完全相同的 Pod 替换
- Pod 名称是随机生成的(如
nginx-7d8f9b8c-xyz) - Pod 重启后 IP 会变化,名称也会变化
- 不提供持久化存储
但很多应用是有状态的:
- 数据库(MySQL、PostgreSQL、Cassandra):每个实例有自己的数据,不能随意替换
- 分布式系统(ZooKeeper、Etcd、Kafka):节点有固定的集群角色和标识
- 消息队列(RabbitMQ):需要稳定的网络标识进行集群通信
一句话总结:Deployment 的 Pod 像流水线上的工人——谁来了都能干一样的活;StatefulSet 的 Pod 像公司的正式员工——每个人有自己的工号、工位和档案,不能随便替换。
1.2 StatefulSet 是什么?
StatefulSet 是 Kubernetes 中专门用于管理有状态应用的工作负载 API 对象。它运行一组 Pod,并为每个 Pod 保留一个稳定的、粘性的标识。
StatefulSet 提供的核心能力:
| 能力 | 说明 |
|---|---|
| 稳定的网络标识 | 每个 Pod 拥有唯一的、持久的 DNS 名称 |
| 稳定的持久存储 | 每个 Pod 绑定独立的 PersistentVolume,Pod 重建后自动重连 |
| 有序的部署和扩缩 | Pod 按顺序创建和删除(0→1→2…,缩容时反向) |
| 有序的滚动更新 | 按顺序逐个更新 Pod,降低风险 |
第二章:StatefulSet 的工作原理
2.1 稳定的网络标识 —— “你是谁”永远不会变
StatefulSet 为每个 Pod 分配一个固定的序号(Ordinal) 。
Pod 命名规则:<statefulset-name>-<ordinal>
例如,一个名为 web 的 StatefulSet 有 3 个副本,Pod 名称分别为:
web-0web-1web-2
这些名称是永久的——即使 Pod 被删除并重新创建,新 Pod 依然叫 web-0。
配合 Headless Service 实现稳定的 DNS:
StatefulSet 必须搭配 Headless Service(无头服务,clusterIP: None)使用。每个 Pod 会获得一个固定的 DNS 域名:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
例如 web-0.nginx.default.svc.cluster.local。
这意味着什么? 即使
web-0这个 Pod 挂了又重建,IP 变了,但其他 Pod 依然可以通过这个 DNS 名称找到它。
2.2 有序的部署和扩缩 —— “先来后到”有规矩
创建/扩容(从 0 到 N) :按序号从小到大依次创建
- 先创建
web-0,等它Running且Ready后 - 再创建
web-1,等它Running且Ready后 - 再创建
web-2…
删除/缩容(从 N 到 0) :按序号从大到小依次删除
- 先删除
web-2,等它完全终止后 - 再删除
web-1,等它完全终止后 - 再删除
web-0…
为什么需要有序? 对于数据库集群,主从复制往往要求主节点先启动(通常是
-0),从节点按顺序加入集群。乱序启动可能导致集群初始化失败。
2.3 稳定的持久存储 —— “你的数据永远是你的”
StatefulSet 通过 volumeClaimTemplates 为每个 Pod 自动创建独立的 PVC,实现“一个 Pod 一份专属存储”。
工作原理:
- 你在 StatefulSet 中定义
volumeClaimTemplates - 创建
web-0时,自动创建 PVCdata-web-0并绑定 PV - 创建
web-1时,自动创建 PVCdata-web-1并绑定 PV - 即使
web-0被删除重建,新 Pod 会自动挂载同一个 PVCdata-web-0
关键点:删除 StatefulSet 不会删除关联的 PVC 和 PV。这是为了保护数据安全——你需要手动清理 PVC 才会真正删除数据。
2.4 组件架构图
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes 集群 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Headless Service (无头服务) │ │
│ │ 为每个 Pod 提供稳定的 DNS 域名 │ │
│ │ web-0.nginx.default.svc.cluster.local │ │
│ │ web-1.nginx.default.svc.cluster.local │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│
│ │ web-0 │ │ web-1 │ │ web-2 ││
│ │ (稳定的名称) │ │ (稳定的名称) │ │ (稳定的名称) ││
│ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘│
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐│
│ │ PVC: data-web-0 │ │ PVC: data-web-1 │ │ PVC: data-web-2 ││
│ │ (专属存储) │ │ (专属存储) │ │ (专属存储) ││
│ └──────────────────┘ └──────────────────┘ └──────────────────┘│
└─────────────────────────────────────────────────────────────────┘
第三章:StatefulSet vs Deployment —— 核心区别
| 对比维度 | Deployment | StatefulSet |
|---|---|---|
| 适用场景 | 无状态应用 | 有状态应用 |
| Pod 名称 | 随机生成(如 nginx-7d8f9-xyz) | 固定序号(如 web-0) |
| 网络标识 | 无稳定标识,IP 变化 | 稳定的 DNS 名称 |
| 存储 | 不提供持久化存储 | 每个 Pod 独立 PVC |
| 创建/删除顺序 | 并行 | 有序(0→1→2,缩容反向) |
| 更新策略 | 滚动更新,可自动回滚 | 有序滚动更新,需谨慎 |
| Pod 管理 | 通过 ReplicaSet | 直接管理 |
什么时候用 StatefulSet?
- 数据库(MySQL、PostgreSQL、Cassandra、MongoDB)
- 分布式协调服务(ZooKeeper、Etcd)
- 消息队列(Kafka、RabbitMQ)
- 任何需要稳定标识或持久化存储的应用
第四章:StatefulSet YAML 详解
4.1 API 版本
apiVersion: apps/v1 # Kubernetes v1.35 稳定版本
kind: StatefulSet
4.2 完整 YAML 结构
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web # StatefulSet 名称
namespace: default
spec:
replicas: 3 # 副本数
# 1. 必须:指定 Headless Service 名称
serviceName: nginx # 必须存在对应的 Headless Service
# 2. 选择器:匹配 Pod 标签
selector:
matchLabels:
app: nginx
# 3. Pod 模板
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: web
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
# 4. 持久存储模板(核心特性)
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 1Gi
# 5. Pod 管理策略(可选)
podManagementPolicy: OrderedReady # 默认:OrderedReady / Parallel
# 6. 更新策略(可选)
updateStrategy:
type: RollingUpdate # RollingUpdate(默认)/ OnDelete
rollingUpdate:
partition: 0 # 分区更新,控制更新范围
4.3 核心字段详解
serviceName —— 必须指定
指定管理该 StatefulSet 的 Headless Service 名称。这个 Service 必须提前创建,StatefulSet 依赖它来生成 Pod 的 DNS 记录。
volumeClaimTemplates —— StatefulSet 的灵魂
这是 StatefulSet 区别于 Deployment 最核心的字段。
- 为每个 Pod 自动创建一个独立的 PVC
- PVC 命名规则:
<volumeClaimTemplates.metadata.name>-<statefulset-name>-<ordinal> - 例如:
data-web-0、data-web-1、data-web-2
podManagementPolicy —— Pod 管理策略
| 策略 | 说明 |
|---|---|
OrderedReady(默认) | 有序创建/删除,前一个 Ready 后操作下一个 |
Parallel | 并行创建/删除,不等待前一个 Ready |
updateStrategy —— 更新策略
| 策略 | 说明 |
|---|---|
RollingUpdate(默认) | 按序号从大到小逐个更新 Pod(先更新 web-2,再 web-1,最后 web-0) |
OnDelete | 只有手动删除 Pod 时才会用新模板重建 |
partition —— 分区更新(重要)
在 RollingUpdate 策略下,可以通过 partition 控制更新范围:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
- 序号 ≥ partition 的 Pod 会被更新(如 web-2、web-3…)
- 序号 < partition 的 Pod 保持旧版本(如 web-0、web-1)
用途:灰度发布 / 金丝雀发布。先更新一部分 Pod 验证,确认没问题后再调整 partition 更新剩余 Pod。
第五章:完整实战示例 —— 部署 Nginx 有状态应用
5.1 创建 Headless Service
# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # 关键:Headless Service
selector:
app: nginx
5.2 创建 StatefulSet
# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: nginx # 引用上面的 Headless Service
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: web
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
5.3 部署与验证
# 1. 创建 Headless Service
kubectl apply -f headless-service.yaml
# 2. 创建 StatefulSet
kubectl apply -f statefulset.yaml
# 3. 查看 Pod 创建顺序(web-0 → web-1 → web-2)
kubectl get pods -w
# 输出示例
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 10s
web-1 1/1 Running 0 25s
web-2 1/1 Running 0 40s
# 4. 查看 PVC(每个 Pod 有独立的 PVC)
kubectl get pvc
# 输出示例
NAME STATUS VOLUME CAPACITY AGE
data-web-0 Bound pvc-xxx-xxx-xxx 1Gi 1m
data-web-1 Bound pvc-yyy-yyy-yyy 1Gi 1m
data-web-2 Bound pvc-zzz-zzz-zzz 1Gi 1m
# 5. 验证稳定的 DNS(进入一个 Pod 解析其他 Pod)
kubectl exec -it web-0 -- nslookup web-1.nginx.default.svc.cluster.local
# 6. 验证稳定的身份——删除 web-0,看它重建后是否还叫 web-0
kubectl delete pod web-0
kubectl get pods -w
# web-0 会被重新创建,名称不变
5.4 扩缩容
# 扩容到 5 个副本(按顺序创建 web-3、web-4)
kubectl scale statefulset web --replicas=5
# 缩容到 2 个副本(按顺序删除 web-4、web-3)
kubectl scale statefulset web --replicas=2
# 注意:缩容不会删除 PVC,数据依然保留
kubectl get pvc
# data-web-3 和 data-web-4 依然存在
5.5 滚动更新
# 更新镜像版本
kubectl patch statefulset web -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.26"}]}}}}'
# 观察更新顺序(从大到小:web-2 → web-1 → web-0)
kubectl get pods -w
# 查看更新状态
kubectl rollout status statefulset web
# 回滚
kubectl rollout undo statefulset web
第六章:高级场景 —— 分区更新(金丝雀发布)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
# ... 其他配置 ...
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只更新序号 ≥ 2 的 Pod
效果:
web-2、web-3… 会使用新版本(金丝雀)web-0、web-1保持旧版本(稳定)
发布流程:
- 设置
partition: 2,只更新web-2 - 验证
web-2运行正常 - 调整
partition: 0,更新所有 Pod
第七章:StatefulSet 的限制与注意事项
| 限制 | 说明 |
|---|---|
| 必须使用 Headless Service | StatefulSet 依赖无头服务提供网络标识 |
| 存储需提前准备 | PVC 需要 StorageClass 动态制备或管理员静态制备 |
| 删除 StatefulSet 不会删除 PVC | 数据安全优先,需手动清理 PVC |
| 删除 StatefulSet 不保证 Pod 优雅终止 | 建议先缩容到 0 再删除 |
| 滚动更新可能卡住 | 如果 Pod 一直处于 Pending 或 CrashLoopBackOff,更新会卡住 |
第八章:最佳实践总结
-
用对场景:只有真正需要稳定标识或持久化存储的应用才用 StatefulSet。能用 Deployment 解决的,别用 StatefulSet。
-
提前规划存储:确保集群有可用的 StorageClass 或预先创建好 PV。
-
Headless Service 先创建:StatefulSet 依赖它,必须先存在。
-
谨慎缩容:缩容不会删除 PVC,如果不再需要记得手动清理,避免浪费存储资源。
-
善用分区更新:生产环境使用
partition做灰度发布,降低风险。 -
删除前先缩容:删除 StatefulSet 前先缩容到 0,确保 Pod 优雅终止。
-
监控 PVC 使用:StatefulSet 的 PVC 是持久化的,注意监控存储容量,避免写满。
附录:快速索引
| 需求 | 命令 |
|---|---|
| 创建 StatefulSet | kubectl apply -f statefulset.yaml |
| 查看 StatefulSet | kubectl get statefulset |
| 查看 StatefulSet 详情 | kubectl describe statefulset <name> |
| 查看 Pod(含序号) | kubectl get pods -l app=nginx |
| 查看 PVC | kubectl get pvc |
| 扩容 | kubectl scale statefulset <name> --replicas=5 |
| 更新镜像 | kubectl set image statefulset/<name> <container>=<new-image> |
| 查看滚动更新状态 | kubectl rollout status statefulset <name> |
| 回滚 | kubectl rollout undo statefulset <name> |
| 删除 StatefulSet(保留 PVC) | kubectl delete statefulset <name> |
| 删除 StatefulSet 和 PVC | kubectl delete statefulset <name> --cascade=orphan 然后手动删 PVC |
文档版本:v1.0
适用环境:Kubernetes v1.35+ / apps/v1
前置条件:集群已配置 StorageClass 或静态 PV
更多推荐
所有评论(0)