2. 容器持久化存储基础

在 Kubernetes 中,容器本身是无状态的、可随时被调度或重建的,但数据库、日志、消息队列、AI 训练数据集等负载却必须把数据写到容器生命周期之外的地方。Kubernetes 为此抽象出一套“声明式存储”模型:用户只描述“我需要多大、什么访问模式的存储”,由控制器和存储驱动去完成实际的卷创建与挂载。理解这套模型,是后续把 Pure Storage FlashArray/FlashBlade 接入 Kubernetes 的前提。

一、五个核心对象各自扮演什么角色

持久卷 PV(PersistentVolume)是集群层面的存储资源对象,代表一块真实可用的存储,例如 FlashArray 上的一个卷、一个 NFS 导出目录或云盘。PV 由管理员静态创建,或由供给器动态创建,它记录容量、访问模式、回收策略、后端标识等属性,独立于任何 Pod 存在。

持久卷声明 PVC(PersistentVolumeClaim)是命名空间内用户对存储的“申请单”。用户不关心底层是 FlashArray 还是别的设备,只声明 storage: 100Gi、accessModes: ReadWriteOnce 等需求。PVC 被绑定到某个满足条件的 PV 后,Pod 才能通过 volumes.persistentVolumeClaim 引用它。

存储类 StorageClass描述“一类存储的能力与供给方式”,包含 provisioner(如 pure-csi)、parameters(如后端阵列、精简置备、复制策略)、reclaimPolicy、volumeBindingMode 等。它把“用户需求”翻译成“后端如何创建卷”。

容器存储接口 CSI(Container Storage Interface)是 Kubernetes 与存储厂商之间的标准 gRPC 接口。Pure Storage 的 CSI 驱动以 DaemonSet 与 Controller 形式部署,实现 CreateVolume、ControllerPublishVolume、NodeStageVolume、NodePublishVolume、DeleteVolume 等方法,把 Kubernetes 的存储动作翻译成对 FlashArray 的 REST 调用。

动态卷供给(Dynamic Provisioning)是当 PVC 找不到现成 PV 时,由 StorageClass 指定的 provisioner 实时创建 PV 的机制。它是 CSI 驱动最常被使用的路径,也是“用户只写 PVC 就能拿到存储”的关键。

二、对象之间的协作链路

下面这张图把五个对象放在同一条链路上:用户提交 PVC,StorageClass 决定由谁供给,CSI 驱动调用后端阵列创建卷并生成 PV,PV 与 PVC 绑定,最终由 kubelet 通过 CSI 节点插件把卷挂载进 Pod。

用户 / 应用团队 提交 PVC 声明需求 PVC 100Gi / RWO / pure-block StorageClass provisioner: pure-csi Pure Storage CSI 驱动 Controller:CreateVolume / DeleteVolume / ControllerPublish Node:NodeStageVolume / NodePublishVolume / NodeUnpublish FlashArray / FlashBlade REST API 创建卷 iSCSI / FC / NFS / SMB PV Bound to PVC Pod 挂载 /data 读写数据 声明 选择 触发供给 调用后端 生成 PV 绑定 挂载

三、动态供给的完整过程

假设管理员已部署 Pure CSI 驱动并创建了名为 pure-block 的 StorageClass,用户提交如下 PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
  namespace: prod
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: pure-block
  resources:
    requests:
      storage: 100Gi

提交后,Kubernetes 的 PersistentVolumeController 发现该 PVC 没有匹配的 PV,且 storageClassName 指向一个存在且允许动态供给的 StorageClass,于是调用 pure-csi 的 CreateVolume。CSI 驱动读取 StorageClass 的 parameters(例如 backend: flasharray-prod、pure_volume_type: thin),通过 REST 在 FlashArray 上创建一个 100GiB 的精简置备卷,并返回卷名、容量、拓扑等信息。控制器据此生成一个 PV 对象,其 claimRef 指向该 PVC,状态变为 Bound。

当引用该 PVC 的 Pod 被调度到某节点后,kubelet 调用 CSI 节点插件:先 NodeStageVolume 把卷挂到节点的全局目录,再 NodePublishVolume 把它绑定到 Pod 的 /var/lib/kubelet/pods/<uid>/volumes/... 路径,最终以 /data 出现在容器内。整个过程用户只写了一个 PVC,其余动作全部由控制器与 CSI 驱动完成。

四、访问模式与绑定模式的关键差异

访问模式决定卷能被多少个节点同时挂载:ReadWriteOnce 只允许单节点读写,适合数据库;ReadOnlyMany 允许多节点只读;ReadWriteMany 允许多节点读写,通常需要 FlashBlade 的 NFS/SMB 或文件类后端;ReadWriteOncePod 则进一步限制为单个 Pod。选择错误会导致 Pod 卡在 ContainerCreating,事件里出现 Multi-Attach error 或 volume is already exclusively attached。

绑定模式由 StorageClass 的 volumeBindingMode 控制。Immediate 表示 PVC 一创建就供给,不等待 Pod 调度;WaitForFirstConsumer 则等到 Pod 被调度后才创建卷,从而让卷落在与 Pod 相同的可用区或节点拓扑上。对于跨可用区的 FlashArray 部署,WaitForFirstConsumer 能避免“卷在 A 区、Pod 调度到 B 区”的挂载失败。

五、回收策略与生命周期

PV 的 persistentVolumeReclaimPolicy 决定 PVC 删除后卷如何处理:Delete 会调用 CSI 的 DeleteVolume 删除后端卷,是动态供给的默认值;Retain 保留后端卷和 PV 对象,仅把 PV 置为 Released,需要管理员手动清理或重新绑定;Recycle 已废弃,不应再使用。生产环境中,数据库类 PVC 常配合 Retain 或后端快照策略,防止误删 PVC 导致数据丢失。

排查存储问题时,按“PVC → PV → StorageClass → CSI Controller 日志 → 后端阵列”的顺序逐层查看,通常能快速定位是声明不匹配、供给失败还是节点挂载失败。

把这五个对象的关系记成一句话:PVC 是需求,StorageClass 是模板,CSI 是翻译官,PV 是结果,Pod 是最终消费者。后续接入 Pure Storage 时,所有配置与排障都围绕这条链路展开。