3. Kubernetes 存储抽象与数据库持久化选型

在 Kubernetes 上运行数据库,最先要解决的不是“怎么装”,而是“数据放在哪里、以什么方式挂进来”。Kubernetes 用三层抽象把存储从 Pod 生命周期中解耦:PV(PersistentVolume)是集群里一块真实可用的存储资源,由管理员或动态供给器创建;PVC(PersistentVolumeClaim)是应用对存储的“需求声明”,描述容量、访问模式等诉求;StorageClass则是“供给模板”,规定用哪个插件、什么参数、什么回收策略来动态生成 PV。Pod 只认 PVC,PVC 再绑定 PV,从而让数据库容器在重建、漂移后仍能拿回同一份数据。

理解这三者的关键是分清“谁定义能力、谁提出需求、谁完成匹配”。PV 是能力侧,PVC 是需求侧,StorageClass 是撮合规则。绑定成功后,PVC 与 PV 形成一对一关系,直到释放。数据库场景下,这个绑定关系必须稳定,否则一次误删 PVC 就可能连带删除后端卷。

Kubernetes 存储抽象与数据库持久化绑定关系 数据库 Pod mysql-0 / pg-0 挂载路径 /var/lib/mysql PVC 需求声明 storage: 500Gi accessMode: RWO StorageClass 供给模板 provisioner: csi 插件 reclaimPolicy: Retain 引用 按类供给 PV 持久卷 capacity: 500Gi bound → PVC 绑定 本地盘 local NVMe / SSD 低延迟·节点绑定 网络块存储 EBS / Ceph RBD RWO·可漂移 文件存储 NFS / CephFS RWX·共享 对象存储 S3 / OSS 备份·归档 数据库 IO 特征匹配: OLTP 随机小 IO → 本地 NVMe / 网络块存储(低延迟、高 IOPS) OLAP 顺序大 IO → 网络块存储 / 文件存储(高吞吐) | 备份归档 → 对象存储(低成本、无限容量)

接下来看四类后端存储的差异。它们不是“谁替代谁”,而是对应不同的 IO 特征和可靠性诉求。

存储类型典型实现访问模式延迟/吞吐数据库适用场景
本地盘local PV、hostPathRWO极低延迟、高 IOPS单节点高性能 OLTP、缓存层
网络块存储EBS、Ceph RBD、云盘RWO低延迟、中等吞吐主库、有状态副本、可漂移
文件存储NFS、CephFS、EFSRWX中等延迟、共享读写多 Pod 共享、日志、备份中转
对象存储S3、OSS、MinIOREST API高延迟、高吞吐备份、归档、冷数据、WAL 归档

选型的核心是“把数据库的 IO 路径拆开看”。以 MySQL 为例,InnoDB 的 redo log 是顺序写、对延迟极敏感;数据文件是随机读写、IOPS 要求高;binlog 是顺序追加;备份则是一次性大吞吐。因此一个常见组合是:数据目录与 redo log 放在本地 NVMe 或高性能网络块存储上,binlog 与备份落到对象存储。如果使用云环境,主库通常选 SSD 类型的网络块存储,保证 Pod 漂移后数据仍可挂载;只读副本或缓存层可以接受本地盘以换取更低延迟。

访问模式(accessMode)是另一个容易踩坑的点。RWO(ReadWriteOnce)表示同一时刻只能被一个节点挂载读写,绝大多数数据库主库都用它;RWX(ReadWriteMany)允许多节点同时读写,适合共享文件场景,但传统单机数据库并不支持多写,强行用 RWX 挂载 MySQL 数据目录会导致数据损坏。ROX(ReadOnlyMany)多用于分发只读数据。选型时必须先确认数据库自身是否支持多写,再决定 accessMode。

关键风险:StorageClass 的 reclaimPolicy 默认可能是 Delete。数据库 PVC 一旦被删除,后端卷会被一并销毁。生产环境应显式设置为 Retain,并配合 VolumeSnapshot 做定期快照。此外,本地盘 PV 与节点强绑定,节点故障时 Pod 无法漂移到其他节点,必须依赖数据库自身的副本机制或备份恢复。

下面给出一个可直接参考的 StorageClass 与 PVC 配置示例,用于生产级 MySQL 主库。示例参数为合理假设,实际需按云厂商和集群能力调整。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: db-ssd-retain
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "6000"
  throughput: "250"
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
spec:
  storageClassName: db-ssd-retain
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 500Gi

这里有两个参数值得展开。volumeBindingMode: WaitForFirstConsumer 表示 PV 延迟到 Pod 调度时才创建,确保卷和 Pod 落在同一可用区,避免跨区挂载失败;allowVolumeExpansion: true 允许后续在线扩容 PVC,数据库磁盘写满时不必重建。扩容操作本身只是改 PVC 的 storage 字段,但文件系统扩容还需要 Pod 内执行 resize2fs 或 xfs_growfs,具体取决于文件系统类型。

判断选型是否合理,可以从三个可观测维度验证:一是 kubectl get pvc 确认状态为 Bound 且容量正确;二是用 fio 在 Pod 内做随机读写测试,观察 IOPS 与延迟是否满足数据库基线;三是模拟节点故障或 Pod 重建,确认数据卷能重新挂载且数据完整。只有这三步都通过,存储选型才算真正落地。

选型速记:OLTP 主库优先“网络块存储 + RWO + Retain”;极致低延迟可评估本地盘但需接受节点绑定;共享读场景用文件存储 RWX;备份归档一律走对象存储。先看 IO 特征,再看访问模式,最后看回收策略。