3. 存储集成核心对象

在OpenShift中,有状态业务能否稳定运行,很大程度上取决于对存储对象模型的理解是否清晰。很多初学者会把PV、PVC、StorageClass、VolumeAttachment和CSI驱动混为一谈,结果在排查“Pod一直Pending”“PVC一直Pending”“卷挂载失败”时无从下手。本节的目标,是把这五个对象放回它们真实的位置上,讲清楚谁定义能力、谁提出需求、谁负责绑定、谁执行挂载,以及CSI驱动在其中扮演什么角色。

一、五个对象的分工

可以把OpenShift的存储集成理解成一条“需求—匹配—绑定—挂载—使用”的链路,但这条链路并不是简单的线性流程,而是由不同控制器和组件协作完成的状态机。

  • StorageClass:描述“存储能力”。它声明某类存储由哪个Provisioner提供、使用什么参数、是否允许动态供给、绑定模式是Immediate还是WaitForFirstConsumer、回收策略是Delete还是Retain。它是集群管理员预先定义的“存储菜单”。
  • PVC:描述“用户需求”。业务开发者只关心“我要5Gi、ReadWriteOnce、访问模式、指定某个StorageClass”,不需要知道后端是Ceph、iSCSI还是云盘。PVC是命名空间级别的对象。
  • PV:描述“实际存储资源”。它可以是管理员静态创建的,也可以由StorageClass对应的Provisioner动态创建。PV是集群级别对象,记录容量、访问模式、回收策略、后端标识(如Ceph image名、iSCSI IQN、云盘ID)。
  • VolumeAttachment:描述“卷与节点的挂载关系”。当Pod调度到某个节点后,AttachDetach控制器会创建VolumeAttachment,通知CSI驱动把卷挂载到目标节点。它解决的是“卷在哪个节点上可用”的问题。
  • CSI驱动:真正执行存储操作的组件。它通常以DaemonSet形式运行在节点上,负责NodeStageVolume、NodePublishVolume等操作;同时还有Controller部分负责CreateVolume、DeleteVolume、ControllerPublishVolume。CSI驱动是OpenShift与存储后端之间的翻译层。

二、从PVC到Pod的完整链路

下面这张图展示了五个对象之间的真实关系。注意:PVC并不直接“连接”到PV,而是通过绑定关系关联;VolumeAttachment也不是PVC的属性,而是由控制器根据Pod调度结果动态创建。

StorageClass provisioner: csi.example.com bindingMode: WaitForFirstConsumer reclaimPolicy: Delete PVC namespace: app-db storageClassName: fast-ssd resources: 5Gi / RWO PV capacity: 5Gi accessModes: RWO csi.volumeHandle: vol-abc123 引用 绑定 Pod nodeName: worker-02 volumes: pvc-data VolumeAttachment node: worker-02 attacher: csi.example.com 触发挂载 卷标识 CSI 驱动 Controller: CreateVolume / DeleteVolume / ControllerPublishVolume Node: NodeStageVolume / NodePublishVolume(DaemonSet) 后端:Ceph RBD / iSCSI / 云盘 / NFS 调用 动态供给 PVC 提出需求 → StorageClass 决定供给方式 → PV 承载实际资源 → VolumeAttachment 绑定节点 → CSI 驱动执行后端操作

三、动态供给与静态供给的差异

理解这五个对象后,就能区分两种典型模式。动态供给时,管理员只创建StorageClass,开发者创建PVC,Provisioner自动创建PV并完成绑定。静态供给时,管理员先手动创建PV,再让PVC去匹配。两者的关键差异在于:动态供给的PV由CSI驱动创建,volumeHandle由驱动生成;静态供给的PV由管理员填写后端细节,CSI驱动只负责挂载。

在OpenShift中,默认StorageClass会影响未指定storageClassName的PVC。如果集群存在多个默认StorageClass,PVC可能绑定到非预期存储。因此生产环境通常只保留一个默认StorageClass,其余显式指定。

四、VolumeAttachment与WaitForFirstConsumer

当StorageClass的bindingMode为WaitForFirstConsumer时,PVC不会立即绑定PV,而是等到Pod被调度后才触发。这样做的好处是:PV可以在Pod所在节点附近创建,避免跨可用区挂载失败。此时VolumeAttachment的创建时机与Pod调度结果强相关。如果Pod一直Pending,需要检查PVC是否处于WaitForFirstConsumer状态、调度器是否无法找到满足卷拓扑的节点。

排查提示:当PVC处于Pending时,先看Events。常见原因包括:StorageClass不存在、Provisioner未运行、容量不足、访问模式不匹配、拓扑限制无法满足。当Pod处于ContainerCreating时,重点看VolumeAttachment是否创建成功、CSI Node插件是否在目标节点运行。

五、一个可参考的PVC与StorageClass示例

下面给出一个教学示例,假设集群已部署CSI驱动,StorageClass名为fast-ssd,后端为Ceph RBD。参数仅为示例,实际值需根据环境调整。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: rbd.csi.ceph.com
parameters:
  clusterID: openshift-storage
  pool: replicapool
  imageFeatures: layering
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-data
  namespace: app-db
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 5Gi

创建后,可以用以下命令观察状态变化:

oc get pvc -n app-db
oc get pv
oc get volumeattachment
oc describe pvc db-data -n app-db

预期现象是:PVC先处于Pending,Pod调度后变为Bound,同时出现对应的PV和VolumeAttachment。如果PVC长时间Pending,describe输出中的Events会给出具体原因。

六、五个对象的职责边界

对象 作用域 核心职责 谁创建
StorageClass 集群级 定义存储类型与供给策略 管理员
PVC 命名空间级 声明容量、访问模式、存储类 开发者
PV 集群级 承载实际存储资源与后端标识 管理员或Provisioner
VolumeAttachment 集群级 记录卷与节点的挂载关系 AttachDetach控制器
CSI驱动 集群级+节点级 执行创建、删除、挂载、卸载 存储厂商或社区

把这五个对象的关系理顺后,后续学习动态供给、卷扩容、快照、拓扑感知调度时,就能快速定位问题发生在哪一层。存储集成的核心不是记住命令,而是理解每个对象在链路中的位置和职责边界。