2. 容器持久化基础
容器本身是“无状态”设计的:容器一旦被删除或重建,其可写层中的数据也随之消失。对于数据库、消息队列、日志归档、AI 训练数据集等有状态应用,必须把数据写到容器生命周期之外的存储上。Kubernetes 为此抽象出一套持久化对象模型,而 CSI(Container Storage Interface,容器存储接口)则负责把外部存储阵列(如 Dell PowerStore、PowerScale、PowerFlex、Unity XT)的能力接入这套模型。理解 PV、PVC、StorageClass 与 CSI 之间的分工,是后续在 Dell 企业存储上做持久化集成的前提。
一、四个核心对象的分工
Kubernetes 的持久化模型可以概括为“声明—绑定—供给—挂载”四层:
- PV(PersistentVolume,持久卷):集群级别的存储资源对象,代表一块真实可用的存储(如一个 LUN、一个 NFS 导出、一个文件系统)。它由管理员预先创建,或由系统动态创建,生命周期独立于任何 Pod。
- PVC(PersistentVolumeClaim,持久卷声明):命名空间级别的“存储申请单”。用户只描述“我要多大、什么访问模式、什么存储类”,不关心底层是哪台阵列、哪个卷。
- StorageClass(存储类,SC):描述“用哪套存储、按什么参数供给”的模板。它引用一个 CSI Provisioner,并携带阵列侧参数(如存储池、协议、精简配置策略)。
- CSI 驱动:运行在集群中的插件,把 Kubernetes 的卷操作翻译成 Dell 阵列的 REST API 调用,实现创建、挂载、扩容、快照等动作。
四者的关系可以用一句话概括:PVC 提出需求,StorageClass 决定由谁供给,CSI 驱动执行供给,PV 是供给出来的结果,最终被 Pod 挂载使用。
二、静态供给与动态供给的机制差异
静态供给(Static Provisioning)是“先有 PV,后有 PVC”。管理员先在 Dell 阵列上创建卷,再编写 PV YAML,把阵列卷的标识(如 volumeHandle)写进去,最后用户创建 PVC 去匹配。它的优点是可控性强,适合已有存储资源复用;缺点是每次申请都要人工介入,规模一大就难以维护。
动态供给(Dynamic Provisioning)是“先有 PVC,自动出 PV”。用户只需在 PVC 中写 storageClassName,CSI Provisioner 就会读取 StorageClass 中的参数,调用 Dell 阵列 API 创建卷,并自动生成对应的 PV 对象,随后完成绑定。这是生产环境的主流方式。
下面是一个动态供给的 PVC 示例,申请 100Gi 的块存储,使用 ReadWriteOnce 访问模式:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data
namespace: prod
spec:
accessModes:
- ReadWriteOnce
storageClassName: powerstore-block
resources:
requests:
storage: 100Gi
对应的 StorageClass 需要提前创建,关键字段是 provisioner 和 parameters:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: powerstore-block
provisioner: csi.powerstore.dell.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
parameters:
arrayIP: "10.10.20.30"
storagePool: "SP_Block"
protocol: "iSCSI"
thinProvision: "true"
其中 reclaimPolicy: Delete 表示 PVC 删除后 PV 与阵列卷一并回收;allowVolumeExpansion: true 允许后续在线扩容;volumeBindingMode: WaitForFirstConsumer 表示延迟绑定,等 Pod 调度到具体节点后再创建卷,避免跨可用区挂载失败。
三、访问模式与绑定状态
PVC 的 accessModes 决定了卷能被多少个节点以何种方式挂载,常见取值如下:
| 访问模式 | 缩写 | 典型场景 | Dell 存储对应 |
|---|---|---|---|
| ReadWriteOnce | RWO | 单节点读写,如 MySQL 主库 | iSCSI / FC 块卷 |
| ReadOnlyMany | ROX | 多节点只读共享 | NFS 只读导出 |
| ReadWriteMany | RWX | 多节点同时读写,如共享文件 | PowerScale NFS / PowerFlex File |
| ReadWriteOncePod | RWOP | 单 Pod 独占读写 | 块卷(K8s 1.29+) |
PVC 与 PV 的绑定状态可通过 kubectl get pvc 观察:Pending 表示尚未匹配到 PV 或等待调度;Bound 表示已成功绑定;Lost 表示底层 PV 已丢失。动态供给场景下,PVC 从 Pending 变为 Bound 通常只需数秒到数十秒,具体取决于阵列 API 响应速度。
powerstore-gold(高性能池)、powerstore-silver(容量池),通过 storagePool 参数隔离,避免所有业务挤在同一存储池中互相影响。
四、从 PVC 到挂载的完整链路
当 Pod 引用 PVC 后,实际发生的过程是:kubelet 调用 CSI Node 插件执行 NodeStageVolume(把卷挂到节点全局目录),再执行 NodePublishVolume(把卷绑定到 Pod 的 /var/lib/kubelet/pods/<uid>/volumes/ 路径)。对于 iSCSI 协议,Node 插件还会先登录 target、扫描 LUN、识别设备文件(如 /dev/sdb),再格式化为 ext4/xfs 并挂载。若挂载失败,可用 kubectl describe pod 查看事件,常见原因包括:阵列侧未开放 initiator IQN、CHAP 认证失败、多路径配置缺失。
理解这条链路后,就能明白为什么 CSI 驱动必须同时部署 Controller 和 Node 两类组件:Controller 负责与阵列 API 交互创建/删除卷,Node 负责在每台工作节点上完成实际的设备挂载。二者配合,才让“PVC 一提交,存储自动就绪”成为可能。