4. StatefulSet 与有状态数据库:StatefulSet 原理、稳定网络标识、有序部署与滚动更新策略
在 Kubernetes 中部署有状态数据库(如 MySQL、PostgreSQL、MongoDB、Redis 集群)时,StatefulSet 是唯一能够提供稳定标识与有序生命周期的原生工作负载。与 Deployment 不同,StatefulSet 为每个 Pod 分配一个持久的身份标识,确保数据库节点在重启、迁移或扩缩容后仍能保持原有的网络名称和存储绑定。
4.1 StatefulSet 核心原理
StatefulSet 通过以下三个机制实现有状态应用的可靠运行:
| 机制 | 说明 | 数据库场景价值 |
|---|---|---|
| 稳定网络标识 | 每个 Pod 拥有固定的 DNS 名称(如 mysql-0.mysql-svc.ns.svc.cluster.local) |
主从复制、集群发现无需动态更新连接字符串 |
| 有序部署与终止 | Pod 按序号从 0 到 N-1 依次创建,反向顺序终止 | 确保主节点先启动、从节点后加入;避免脑裂 |
| 持久存储绑定 | 每个 Pod 绑定独立的 PVC,Pod 重建后自动挂载同一卷 | 数据库数据不丢失,节点恢复后直接加载本地数据 |
关键区别: Deployment 创建的 Pod 名称是随机的(如
app-7d8f9c6b4-abcde),而 StatefulSet 创建的 Pod 名称固定为 <statefulset-name>-<ordinal>(如 mysql-0、mysql-1)。这个序号就是 Pod 的“身份指纹”。
4.2 稳定网络标识:有状态数据库的通信基石
StatefulSet 与 Headless Service(ClusterIP: None)配合使用,为每个 Pod 生成可预测的 DNS 记录。数据库集群内部通过 SRV 记录或 A 记录直接定位每个节点。
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
spec:
clusterIP: None # Headless Service
selector:
app: mysql
ports:
- port: 3306
---
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
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 50Gi
创建后,三个 Pod 的 DNS 解析结果如下:
mysql-0.mysql-svc.default.svc.cluster.local→ Pod IPmysql-1.mysql-svc.default.svc.cluster.local→ Pod IPmysql-2.mysql-svc.default.svc.cluster.local→ Pod IP
数据库配置实践: MySQL Group Replication 或 PostgreSQL Patroni 可直接使用
mysql-0.mysql-svc 作为种子节点地址,无需服务发现中间件。
4.3 有序部署与滚动更新策略
StatefulSet 的 Pod 管理策略分为两种模式,直接影响数据库集群的升级与扩缩容行为:
| 策略 | 创建顺序 | 更新顺序 | 终止顺序 | 适用场景 |
|---|---|---|---|---|
| OrderedReady(默认) | 0 → 1 → 2 | 从序号最大开始,逐个更新 | 2 → 1 → 0 | 主从复制、有主节点选举的数据库 |
| Parallel | 同时创建所有 Pod | 同时更新所有 Pod | 同时删除所有 Pod | 无状态或对启动顺序不敏感的组件 |
4.4 滚动更新策略配置
StatefulSet 的 updateStrategy 支持两种方式:
- RollingUpdate(默认):按序号从大到小逐个更新 Pod,每个 Pod 更新前会等待前一个 Pod 进入 Ready 状态。
- OnDelete:手动删除 Pod 后才触发重建,适用于需要精确控制升级节奏的数据库场景。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只更新序号 >= 2 的 Pod
podManagementPolicy: OrderedReady
...
数据库升级避坑: 使用
partition 字段可以实现金丝雀更新。例如设置 partition: 2,则只有 mysql-2 会被更新,mysql-0 和 mysql-1 保持不变。验证新版本稳定后再逐步降低 partition 值,完成全量升级。
4.5 数据库迁移中的 StatefulSet 最佳实践
迁移清单:
- 始终使用 Headless Service:确保数据库节点间通过固定 DNS 通信,避免 IP 漂移导致复制中断。
- 设置
podManagementPolicy: OrderedReady:保证主节点先启动,从节点后加入,防止从节点竞选为主导致数据不一致。 - 利用
partition做灰度升级:先升级一个从节点,验证查询性能与复制延迟,再逐步推进。 - 为每个节点配置独立的资源请求与限制:数据库对 CPU/内存敏感,避免因资源竞争导致 OOM 或性能抖动。
- 启用
terminationGracePeriodSeconds:给数据库进程足够时间完成事务提交与 binlog 刷新(建议 60s+)。
性能优化提示: 对于高并发写入的数据库,建议将 StatefulSet 的
minReadySeconds 设置为 30-60 秒,确保新 Pod 在加入流量前已完成 WAL 恢复和缓存预热。