跳到主要内容

运行有状态应用

StatefulSet 适合副本不能简单互换的服务,例如数据库、Kafka、ZooKeeper 或需要固定网络身份的集群软件。它提供稳定的 Pod 名称、可配套独立 PVC,并按顺序创建和终止副本。

本篇目标:能判断何时该用 StatefulSet,写出配套的 Headless Service 和 volumeClaimTemplates,理解有序启动/终止和分区更新。

Kubernetes StatefulSet 稳定身份与存储关系


1. StatefulSet 与 Deployment 的关键区别​

能力DeploymentStatefulSet
Pod 名称随机后缀,随重建变化稳定编号,如 mysql-0、mysql-1
存储通常共享或无状态常为每副本创建独立 PVC
创建/删除顺序并行、以可用性为主默认有序,便于集群成员初始化
网络身份无稳定 DNS 名称配合 Headless Service 提供稳定 DNS
典型用途API、Web、Worker数据库、消息队列、协调服务

StatefulSet 不是"更高级的 Deployment"。如果服务不要求稳定身份或独立数据卷,优先选择更简单的 Deployment。


2. Headless Service 提供稳定 DNS​

StatefulSet 必须配一个 headless Service(clusterIP: None),用于为每个 Pod 提供稳定 DNS:

apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: learning
spec:
clusterIP: None
selector:
app.kubernetes.io/name: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: learning
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: mysql
template:
metadata:
labels:
app.kubernetes.io/name: mysql
spec:
containers:
- name: mysql
image: mysql:8.4
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password

对应 Pod 可以通过 mysql-0.mysql.learning.svc.cluster.local 这类地址互相发现。实际数据库镜像还需数据目录挂载、初始化脚本、备份和 TLS 配置,不能直接拿此示例上生产。


3. 有序启动与终止​

3.1 podManagementPolicy​

策略行为适用场景
OrderedReady(默认)mysql-0 启动 → Ready → mysql-1 启动 → Ready → …需要 leader 先就绪的集群
Parallel所有 Pod 同时启动/终止各副本之间无启动顺序依赖

删除 StatefulSet 时终止顺序与创建相反:mysql-2 先终止 → mysql-1 → mysql-0。

3.2 Init 容器做集群初始化​

典型模式是用 Init 容器在有序启动的基础上判断 leader/follower:

spec:
podManagementPolicy: OrderedReady
template:
spec:
initContainers:
- name: init-cluster
image: registry.example.com/mysql-init:1.0
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# mysql-0 初始化为 leader,mysql-1/2 作为 follower 加入
command:
- sh
- -c
- |
if [ "$POD_NAME" = "mysql-0" ]; then
echo "初始化为主节点"
else
echo "等待 mysql-0 就绪后加入集群"
fi
containers:
- name: mysql
image: mysql:8.4

4. 每个副本的持久化卷​

spec:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi

这会为 mysql-0 创建 data-mysql-0 PVC,mysql-1 创建 data-mysql-1 PVC,依此类推。

4.1 PVC 保留策略​

spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # 删除 StatefulSet 时保留 PVC
whenScaled: Delete # 缩容时删除对应 PVC(数据丢失!)
策略值行为
RetainPVC 保留,需手动清理
DeletePVC 随 StatefulSet 删除或缩容自动删除
缩容 ≠ 暂时不需要数据

whenScaled: Delete + 缩容 = 对应 Pod 的 PVC 和数据被删除。生产上建议 whenScaled: Retain,缩容后手动确认数据不再需要再删除 PVC。


5. 分区更新​

StatefulSet 的 rollingUpdate.partition 让你只更新序号 ≥ N 的 Pod,适合分阶段灰度:

spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只更新 mysql-2 及更大序号的 Pod
# 阶段 1:只更新 mysql-2(partition=2)
# 验证 mysql-2 的日志和指标
# 阶段 2:partition=1 → 更新 mysql-1 和 mysql-2
# 阶段 3:partition=0 → 更新全部
kubectl patch statefulset mysql -n learning \
-p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":1}}}}'

分区更新是 StatefulSet 特有的能力,Deployment 没有等价功能。


6. 生产检查项​

  • 优先使用云厂商托管数据库;自建前先明确高可用、备份、恢复演练和升级责任;
  • 把数据备份恢复流程视为工作负载的一部分,不能只依赖 PVC;
  • 升级镜像前阅读应用自身的兼容性矩阵——Kubernetes 能更新 Pod,不会替你完成数据格式迁移;
  • 避免把单副本 StatefulSet 误当成高可用方案,它仍是单点;
  • 确认 persistentVolumeClaimRetentionPolicy 与团队的备份策略一致;
  • 缩容前先备份数据——Kubernetes 不保证缩掉的 Pod 的数据还能找回。

7. 练习​

练习 A

  1. 创建上面的 Headless Service + StatefulSet(3 副本,用 nginx:alpine 代替 MySQL),观察 Pod 命名是否依次为 mysql-0、mysql-1、mysql-2
  2. 用 kubectl run tmp --image=busybox --rm -it --restart=Never -- nslookup mysql-0.mysql.learning 测试 DNS
  3. 删除 mysql-1 Pod,观察它重新创建后名称是否还是 mysql-1(验证稳定身份)

练习 B

  1. 给 StatefulSet 添加 volumeClaimTemplates,查看是否为每个 Pod 创建了独立 PVC
  2. 将 podManagementPolicy 改为 Parallel,重建 StatefulSet,观察 Pod 是否同时启动而非依次

验收清单

  • 能说明何时需要 StatefulSet,何时应使用 Deployment
  • 知道 Pod 稳定身份来自名称和 headless Service
  • 理解 volumeClaimTemplates 为每个副本创建独立存储
  • 知道 OrderedReady 和 Parallel 的启动差异及分区更新的做法
  • 上线前会确认 PVC 保留策略和备份恢复方案

下一篇:为节点运行系统 Agent。