2. 有状态工作负载的容器化与编排基础
无状态服务可以随意替换副本,但数据库、消息队列、搜索引擎这类有状态工作负载,每个实例都携带不可丢失的身份与数据。把它们放进 Kubernetes,核心矛盾不是“能不能跑起来”,而是“重启、迁移、扩缩容之后,数据还在不在、身份还认不认得、访问入口还稳不稳”。本节围绕 StatefulSet、Operator 模式、持久卷声明(PVC)与调度约束四条主线,说明有状态负载的编排基础,并给出数据库容器化的适用边界与风险判断。
StatefulSet:稳定身份与稳定存储的绑定
Deployment 管理的是“无差别副本”,Pod 名字随机、存储共享或干脆无状态。StatefulSet 则给每个副本一个稳定且有序的身份:Pod 名称固定为 <statefulset-name>-<序号>,例如 mysql-0、mysql-1、mysql-2;每个 Pod 通过 volumeClaimTemplates 自动生成专属 PVC,命名形如 data-mysql-0。这个“序号—网络标识—存储卷”三者一一对应的关系,是 StatefulSet 最本质的语义。
它带来两个可预期的行为。第一,有序性:默认按 0、1、2 顺序创建,缩容时从最大序号开始删除,滚动更新也逆序进行。第二,稳定网络标识:配合 Headless Service,每个 Pod 获得 mysql-0.mysql-headless.default.svc.cluster.local 这样的稳定 DNS 名,主从复制、集群成员发现都依赖它。需要注意,StatefulSet 删除 Pod 时默认保留 PVC,这是保护数据的默认行为,但也意味着缩容不会自动回收存储,需要人工确认后清理。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
podManagementPolicy: OrderedReady
selector:
matchLabels: { app: mysql }
template:
metadata:
labels: { app: mysql }
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: ssd-replicated
resources:
requests:
storage: 100Gi
Operator 模式:把运维知识编码进控制器
StatefulSet 只解决“稳定身份 + 稳定存储”,它不理解 MySQL 主从如何选主、副本如何重建、备份如何触发。Operator 模式用自定义资源(CRD)描述领域对象,例如 MySQLCluster、PostgresCluster,再用控制器持续对比“期望状态”与“实际状态”,自动完成成员加入、故障切换、版本升级、备份恢复等操作。它本质上是把 DBA 的运维手册变成了可循环执行的调谐逻辑。
使用 Operator 时要关注三点:一是它是否声明了明确的健康判据与切换条件,避免脑裂;二是它管理的 PVC、Service、Secret 是否可被审计和接管;三是升级 Operator 本身是否会影响存量集群。Operator 降低了日常操作门槛,但把复杂度转移到了“控制器行为是否符合预期”上,生产环境必须先在预发集群验证其故障切换路径。
持久卷声明与调度约束
PVC 是“我要一块多大、什么性能的存储”的声明,真正落盘由 StorageClass 与 CSI 驱动完成。有状态 Pod 的调度必须与存储位置协同:如果使用本地盘或区域级云盘,Pod 只能调度到卷所在节点或可用区。常见做法是用 volumeBindingMode: WaitForFirstConsumer 延迟绑定,让调度器先选节点、再创建卷,避免“卷建好了却没有节点可用”。
调度约束还包括:用 nodeAffinity 把数据库固定到高主频、大内存的节点池;用 podAntiAffinity 让同一集群的多个副本分散到不同节点甚至不同可用区;用 topologySpreadConstraints 控制副本在机架/可用区的均衡度。对延迟敏感的数据库,还应避免与高吞吐批处理任务混部,必要时通过 taints/tolerations 独占节点。
数据库容器化的适用边界与风险
并非所有数据库都适合容器化。判断标准可以归纳为:数据规模与 IO 模式是否可被容器存储满足、故障恢复是否依赖稳定网络标识、是否有成熟的 Operator 支撑、团队是否具备容器网络与存储的排障能力。适合的场景包括:中小规模 MySQL/PostgreSQL、开发测试环境、需要快速交付的多租户实例、以及已有成熟 Operator 的云原生数据库。需要谨慎的场景包括:超大单实例(数十 TB 级)、对本地 NVMe 极致延迟敏感、依赖特殊内核参数或裸设备、以及强合规要求下不允许共享存储的部署。
主要风险集中在四处。第一,存储性能与可靠性:容器网络存储的 IOPS、延迟、抖动通常不如本地盘,且 CSI 驱动的故障恢复行为需要验证。第二,数据持久性:PVC 误删、StorageClass 回收策略设为 Delete、跨可用区卷无法挂载,都可能造成数据不可用。第三,网络与时钟:有状态集群对网络分区、时钟漂移敏感,容器环境的网络抖动可能触发误切换。第四,升级与回滚:Operator 或数据库版本升级失败时,回滚路径是否清晰、备份是否可恢复,必须提前演练。
reclaimPolicy。若为 Delete,删除 PVC 会连带删除底层卷;数据库场景建议使用 Retain,并配合独立备份策略。落地检查清单
| 检查项 | 判断依据 | 示例值 |
|---|---|---|
| 身份稳定性 | Pod 名与 DNS 是否固定 | mysql-0.mysql-headless |
| 存储绑定 | 每个副本是否有独立 PVC | data-mysql-0 / 100Gi |
| 卷绑定模式 | 是否延迟绑定以配合调度 | WaitForFirstConsumer |
| 回收策略 | 删除 PVC 是否保留底层卷 | Retain |
| 副本分布 | 是否跨节点/可用区 | podAntiAffinity + topologySpread |
| 故障切换 | Operator 是否声明切换条件 | 主库失联 30s 触发选主 |
| 备份恢复 | 是否有可验证的恢复演练 | 每日全备 + binlog,RPO≤5min |