摘要:数据库、消息队列等有状态应用需要稳定的网络标识和持久存储。StatefulSet 为这类应用提供有序部署、稳定标识和持久存储能力。本文详解 StatefulSet 的三大保证、Pod 管理策略、更新策略、volumeClaimTemplates 机制及与 Deployment 的对比。

一、Deployment vs StatefulSet

Deployment 管理的 Pod 彼此等价、可任意替换,适合无状态服务。StatefulSet 管理的 Pod 具有固定标识和独立存储,适合有状态服务。

StatefulSet

Pod 固定名

固定 DNS

独立 PVC

Deployment

Pod 随机名

随机 IP

共享/无持久存储

上图为 Deployment 与 StatefulSet 的差异。StatefulSet 的 Pod 名称格式为 <name>-<序号>,如 mysql-0、mysql-1、mysql-2,且每个 Pod 绑定独立的 PVC。

StatefulSet: 有状态

mysql-0

mysql-1

mysql-2

Deployment: 无状态

app-abc123

app-def456

app-ghi789

二、StatefulSet 的三大保证详解

2.1 稳定的网络标识

Pod 名称格式:<statefulset-name>-<序号>。配合 Headless Service 后,每个 Pod 拥有固定 DNS:<pod-name>.<service-name>.<namespace>.svc.cluster.local。Pod 重建后名称和 DNS 不变。即使 Pod IP 变化,通过 DNS 仍可稳定访问。

2.2 有序的部署和扩缩容

创建顺序:0 → 1 → 2。扩容时需等待前一个 Pod Ready 后再创建下一个。

删除顺序:2 → 1 → 0。缩容时逆序删除,先删除序号最大的 Pod。

该机制确保主从类应用(如 MySQL 主从)能按序建立复制关系。

2.3 持久化存储绑定

每个 Pod 通过 volumeClaimTemplates 绑定独立 PVC。Pod 删除重建后仍绑定同一 PVC,数据得以保留。PVC 命名规则:<volumeClaimTemplate-name>-<statefulset-name>-<序号>

StatefulSet 三大保证

稳定网络标识

有序部署扩缩

持久化存储

固定 Pod 名 + DNS

顺序创建/逆序删除

每 Pod 独立 PVC

三、StatefulSet 完整示例

3.1 Headless Service(必须)

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
spec:
  clusterIP: None              # Headless Service
  selector:
    app: mysql
  ports:
  - port: 3306
字段 说明
clusterIP: None 声明为 Headless Service,不分配 ClusterIP
selector 与 StatefulSet Pod 标签匹配,用于 DNS 解析

3.2 StatefulSet 完整 YAML

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-svc       # 关联 Headless Service
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql

  # 卷声明模板 — 自动为每个 Pod 创建独立 PVC
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: fast-ssd
      resources:
        requests:
          storage: 10Gi
字段 说明
serviceName 必须,关联 Headless Service
volumeClaimTemplates 为每个 Pod 自动创建 PVC,命名格式为 <name>-<statefulset>-<序号>
template Pod 模板,与 Deployment 类似

3.3 验证命令

# 创建 Secret
kubectl create secret generic mysql-secret --from-literal=password=yourpassword

# 部署
kubectl apply -f headless-svc.yaml
kubectl apply -f statefulset.yaml

# 观察有序创建
kubectl get pods -w

# 查看 PVC
kubectl get pvc

# 验证 DNS
kubectl run -it --rm debug --image=busybox -- nslookup mysql-0.mysql-svc

四、Pod 管理策略

策略 说明
OrderedReady(默认) 有序创建/删除,前一个 Ready 后才处理下一个
Parallel 并行创建/删除,不保证顺序
spec:
  podManagementPolicy: Parallel

Parallel 适用于可独立启动的有状态应用,可加快部署速度;主从类应用建议使用 OrderedReady。

五、更新策略

5.1 RollingUpdate 与 partition

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 1           # 分区更新:只更新序号 >= 1 的 Pod

partition 用于金丝雀发布:partition=2 时,仅 mysql-2 及之后的 Pod 会更新;验证无误后可将 partition 设为 0,完成全部 Pod 的更新。

5.2 OnDelete

spec:
  updateStrategy:
    type: OnDelete

OnDelete 下,只有 Pod 被手动删除后才会用新镜像重建,适用于需要严格人工控制的场景。

partition: 0

mysql-0 v2

mysql-1 v2

mysql-2 v2

partition: 2

mysql-0 v1

mysql-1 v1

mysql-2 v2

六、volumeClaimTemplates 详解

volumeClaimTemplates 在 StatefulSet 创建时,为每个副本生成一个 PVC。命名规则:<volumeClaimTemplate-name>-<statefulset-name>-<序号>

行为 说明
扩容 新 Pod 创建时,自动创建对应 PVC
缩容 Pod 删除后,PVC 不会自动删除,需手动清理
重建 Pod 重建后自动绑定同一 PVC,数据保留

mysql-0

data-mysql-0

mysql-1

data-mysql-1

mysql-2

data-mysql-2

PV-1

PV-2

PV-3

七、扩缩容行为

扩容:按序创建,mysql-0 Ready → mysql-1 创建 → mysql-1 Ready → mysql-2 创建。

缩容:逆序删除,先删除 mysql-2,再 mysql-1,最后 mysql-0。缩容不会自动删除 PVC。

创建 mysql-0

mysql-0 Ready

创建 mysql-1

mysql-1 Ready

创建 mysql-2

mysql-2 Ready

上图为 replicas=3 时的部署顺序。每个 Pod 创建前会先创建对应的 PVC(data-mysql-0、data-mysql-1、data-mysql-2)。

八、网络标识详解

Headless Service(clusterIP: None)会为每个 Pod 创建 DNS 记录,格式为 <pod-name>.<service-name>.<namespace>.svc.cluster.local。即使 Pod 重建导致 IP 变化,通过 DNS 仍可稳定访问。

Pod IP

Headless Service: mysql-svc

mysql-0.mysql-svc...

mysql-1.mysql-svc...

mysql-2.mysql-svc...

10.244.1.5

10.244.2.3

10.244.3.7

适用于 MySQL 主从等场景:mysql-0 作为主节点,mysql-1、mysql-2 作为从节点,通过固定 DNS 建立复制关系。

九、与 Deployment 的详细对比

对比项 Deployment StatefulSet
Pod 名称 随机后缀(如 app-abc123) 固定格式(如 mysql-0)
网络标识 通过 Service 负载均衡 固定 DNS,每 Pod 可独立访问
存储 共享或无 每 Pod 独立 PVC
创建顺序 并行 有序(可配置 Parallel)
删除顺序 无保证 逆序
更新策略 RollingUpdate / Recreate RollingUpdate(支持 partition)/ OnDelete
典型场景 无状态 Web、API 数据库、消息队列、分布式协调

十、生产环境建议

  1. Headless Service:必须配置,否则无法获得稳定 DNS
  2. volumeClaimTemplates:为每个 Pod 分配独立存储,避免数据混用
  3. 优雅关闭:设置 terminationGracePeriodSeconds,确保应用处理 SIGTERM 并完成复制同步
  4. 备份策略:StatefulSet 不负责数据备份,需通过 CSI 快照或应用层备份
  5. 缩容前确认:手动删除 PVC 前务必确认数据已迁移或不需要保留

十一、FAQ

Q1:StatefulSet 扩容时 PVC 不自动创建?

检查 StorageClass 是否存在且 provisioner 正常;查看 StatefulSet controller 日志;确认 volumeClaimTemplates 的 storageClassName 与集群中 StorageClass 名称一致。

Q2:Pod 卡在 Terminating 怎么办?

常见原因:finalizers 未完成、存储未卸载、节点异常。可 kubectl describe pod <name> 查看 Events;若确认可强制删除:kubectl delete pod <name> --force --grace-period=0,注意数据一致性风险。

Q3:StatefulSet 是否必须使用 Headless Service?

是的。Headless Service 提供 Pod 的 DNS 解析,是 StatefulSet 稳定网络标识的前提。

Q4:缩容时 PVC 会被删除吗?数据如何保证持久性?

不会。为保护数据,K8s 不会自动删除 StatefulSet 的 PVC。需手动删除不再使用的 PVC。数据持久性由底层 PV 的回收策略和备份策略保证。

Q5:如何实现 StatefulSet 的零停机滚动更新?

需应用支持优雅关闭和滚动更新。设置 terminationGracePeriodSeconds,确保应用正确处理 SIGTERM;主从类应用还需在断开前完成复制同步。可使用 partition 进行金丝雀发布:先更新从节点,验证无误后再更新主节点。

十二、零停机滚动更新实践

实现 StatefulSet 的零停机滚动更新需满足:

  1. 应用支持优雅关闭:正确处理 SIGTERM,在 terminationGracePeriodSeconds 内完成收尾
  2. 主从类应用:从节点在断开前完成复制同步
  3. partition 金丝雀:先更新从节点,验证无误后再更新主节点
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 30
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 1    # 先更新 mysql-1、mysql-2,保留 mysql-0

12.1 更新流程示例

  1. 设置 partition: 2,仅 mysql-2 更新为新镜像
  2. 验证 mysql-2 运行正常
  3. 设置 partition: 1,更新 mysql-1
  4. 验证后设置 partition: 0,更新 mysql-0(主节点)

十三、常用命令

# 查看 StatefulSet
kubectl get statefulset
kubectl get sts

# 扩缩容
kubectl scale statefulset mysql --replicas=5

# 滚动更新(修改镜像)
kubectl set image statefulset/mysql mysql=mysql:8.0.33

# 分区更新
kubectl patch statefulset mysql -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":1}}}}'

# 查看 Pod 顺序
kubectl get pods -l app=mysql

十四、典型应用场景配置

14.1 Redis 集群

Redis 集群需固定节点标识,StatefulSet 可为每个节点分配独立存储与 DNS:

volumeClaimTemplates:
- metadata:
    name: redis-data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: standard
    resources:
      requests:
        storage: 5Gi

14.2 Kafka

Kafka Broker 需持久化 topic 分区数据,每 Pod 独立存储,通过固定 DNS 建立 Broker 间通信。

14.3 与 Headless Service 的读写分离

可为 StatefulSet 同时创建普通 Service(负载均衡)和 Headless Service(直接访问):

# 普通 Service:用于读负载均衡
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
---
# Headless Service:用于主从复制等直接访问
apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306

14.4 terminationGracePeriodSeconds 配置

主从类应用在关闭前需完成复制同步,需设置足够的优雅关闭时间:

spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60

默认 30 秒,MySQL 等应用建议 60 秒以上。应用需在收到 SIGTERM 后停止接受新连接、完成复制同步再退出。

14.5 扩缩容注意事项

  • 扩容:新 Pod 会按序创建,每个 Pod 会创建新的 PVC;需确保 StorageClass 的 provisioner 可用
  • 缩容:逆序删除 Pod,PVC 保留;若需释放存储,需手动删除 PVC,删除前务必确认数据已备份或不需要

14.6 删除顺序与数据安全

缩容时,StatefulSet 按逆序删除 Pod(先删除序号最大的)。若需缩容时保留某 Pod 的数据,可先备份对应 PVC 的数据,再执行缩容。删除 StatefulSet 时,Pod 会按逆序删除,但 PVC 不会被删除;需手动删除 PVC 才能释放存储。

十五、总结

StatefulSet 适用于有状态应用的编排,主要能力包括:

  1. 稳定网络标识:固定 Pod 名与 DNS
  2. 有序部署扩缩:按序创建与逆序删除
  3. 持久化存储:每 Pod 独立 PVC

需要配合 Headless Service 使用。典型场景:数据库(MySQL、PostgreSQL)、缓存集群(Redis)、消息队列(Kafka、RabbitMQ)、分布式协调(ZooKeeper、etcd)等。

特性 说明
网络标识 Headless Service + 固定 Pod 名
有序性 OrderedReady(默认)或 Parallel
更新 RollingUpdate + partition 金丝雀
存储 volumeClaimTemplates 每 Pod 独立 PVC
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐