Skip to content

持久化存储:PVC 到 Linux VFS ​

专题索引 / 上一篇:NetworkPolicy:声明访问意图到节点执行

容器中的一个目录不是天然持久的。Kubernetes 用 PVC/PV/StorageClass 表达“我需要什么存储”;CSI 把这个声明连接到具体存储系统;Linux 最后把设备、网络文件系统或内存对象挂进容器的 mount namespace。

text
PVC 声明容量、访问模式、StorageClass
  -> PV binding 或动态 provision
  -> scheduler 选择能使用该卷的 Node
  -> CSI controller 创建或 attach 卷
  -> kubelet / CSI node plugin stage、publish、mount
  -> 容器看到 volumeMount 路径
  -> write() -> VFS -> 文件系统 / 块设备 / 网络存储

这条链的每一步都有独立状态。PVC=Bound 只表示声明与 PV 已绑定;它不证明目标 Node 已挂载,也不证明应用数据已经持久化到介质。

1. 四个对象分别说什么 ​

对象归属与含义不承诺什么
PVC工作负载请求的容量、访问模式、StorageClass某个云盘或目录已经存在
PV可绑定的具体存储资源及其声明状态已经可被任意 Node 挂载
StorageClass动态供给器和参数、绑定策略、回收策略某次 provision 已成功
VolumeAttachment某些 CSI 驱动的控制面 attach 状态kubelet 已完成 mount

PVC 是应用的稳定契约,PV 是集群侧资源。动态供给时,external-provisioner 通过 CSI CreateVolume 创建后端资源并创建或更新 PV;静态供给时,管理员事先提供 PV,binder 负责匹配 PVC 与 PV。

2. 先放置,还是先建卷 ​

StorageClass.volumeBindingMode 决定一项很重要的时序:

text
Immediate:
PVC 创建后尽早 provision / bind

WaitForFirstConsumer:
等待有使用该 PVC 的 Pod 被调度
-> 根据候选 Node 的拓扑再 provision / bind

后者解决拓扑耦合问题,例如云盘只能挂到同一可用区的 Node。这里的架构取舍是:把卷绑定推迟到拥有消费位置的信息之后,避免先创建一个 scheduler 根本无法使用的资源。

3. 从 Pod 调度到容器可见路径 ​

Pod 被调度到某个 Node 后,kubelet 的 volume manager 负责让声明中的 volume 对该 Pod 可用。CSI 的常见分工如下,具体调用组合取决于驱动能力和卷类型:

text
Controller 侧:
CreateVolume -> ControllerPublishVolume(需要 attach 的驱动)

Node 侧:
NodeStageVolume -> NodePublishVolume
  -> kubelet 将 volume 挂到 Pod volume 目录
  -> 容器 runtime 将其 bind mount 到 volumeMount

NodeStageVolume 通常把卷准备到 Node 上的全局 staging 路径;NodePublishVolume 再把它暴露给某个 Pod。对不需要 attach 的网络存储、驱动直接挂载或内联卷,调用细节不同,但“控制面准备资源”和“Node 本地让 Pod 可用”的边界仍然存在。

容器有自己的 mount namespace。runtime 常用 bind mount 把 kubelet 已准备好的路径带入容器,因此容器内的 /data 看似普通目录,背后可能是 ext4/XFS 块设备、NFS、CephFS、tmpfs 或驱动的其他实现。应用不能通过路径名字判断持久性、延迟、原子性或多写者语义。

4. 写入真正经过 Linux 的哪一层 ​

卷挂载成功后,Kubernetes 不会参与每次文件写入。应用的系统调用进入 Linux:

text
write(fd, bytes)
  -> VFS 根据 mount 与 inode 找到文件系统实现
  -> page cache / 文件系统元数据与日志
  -> 块层与设备驱动,或网络文件系统客户端
  -> 本地磁盘、云块设备、远端存储集群

write() 返回、数据进入 page cache、文件系统 journal 提交、设备完成 flush 是不同完成点。数据库或关键业务日志不能把“函数返回”直接当作断电后一定可恢复;需要依据文件系统、挂载选项、存储系统与应用自身的 fsync/WAL 策略定义持久化语义。

这与前文的 GPU fence、Service endpoint 是同一类架构问题:上游接受请求、下游完成工作、最终对外可见,都是不同状态,必须用明确契约连接。

5. 访问模式与回收策略的边界 ​

PV 的 ReadWriteOnce、ReadOnlyMany、ReadWriteMany 描述 Kubernetes 期望的访问方式和驱动能力。它们不是一套跨存储后端完全相同的锁协议;特别是 RWO 通常约束可读写挂载的 Node,而不必然等价于“该 Node 上只能有一个 Pod 使用”。应以所选 CSI 驱动的实际能力与 Kubernetes 版本文档为准。

persistentVolumeReclaimPolicy 则决定 PVC 释放后如何处理后端资源:Delete 通常让供给器删除资源,Retain 保留给管理员复核和手动回收。它不是备份策略,也不能替代应用级删除保护。

6. 按阶段排查“数据卷不可用” ​

阶段证据常见断点
声明与绑定PVC/PV phase、StorageClass、eventsclass 不存在、容量/访问模式不匹配
拓扑与调度Pod node、WaitForFirstConsumer、Node affinity卷在错误可用区,Pod 无可用 Node
控制面准备CSI controller 日志、VolumeAttachment、后端卷状态provision 或 attach 失败
Node 挂载kubelet、CSI node plugin、mount 事件设备未出现、凭据/格式/挂载失败
应用写入容器内 mount、权限、I/O 错误、持久化语义路径只读、UID/GID 不符、空间满、延迟或数据一致性错误

不要只用 kubectl get pvc 收尾。它最多覆盖第一段;应用看见路径、能写入、数据在故障模型下如何保存,属于后续不同的证据层。

7. 从 CSI 学到的架构规则 ​

  1. Kubernetes API 只表达可移植的存储需求,CSI 把需求适配到厂商和后端差异。
  2. 控制器和 Node plugin 分工:前者管理全局资源,后者处理与机器有关的 attach、mount 和生命周期。
  3. 延迟绑定把不可逆资源创建推迟到拓扑信息充分之后。
  4. “请求被接受”“资源已绑定”“Node 已挂载”“数据已持久化”必须作为不同验收点。

资料与源码入口 ​

本文解释的是 PVC/PV/CSI 和 Linux VFS 的职责关系。任何存储后端的性能、故障域、快照、一致性和多写者语义,都必须以目标 CSI 驱动和实际存储系统文档、压测与故障演练为准。

学习规划与研究资料库