CFN Cloud
2025-10-09

Kubernetes PV 与 PVC:持久化存储基础入门

理解 PersistentVolume 与 PersistentVolumeClaim 的绑定关系、生命周期以及 Kubernetes 存储排障思路。

Kubernetes 一旦进入存储主题,难度就会明显上升。原因很简单:Pod 天生可以被替换,但数据通常不行。PV 和 PVC 这套模型,就是 Kubernetes 用来把“应用需要什么存储”和“底层怎么提供存储”分开的方式。

PV 和 PVC 分别是什么

  • PV:集群里可供使用的一块存储资源。
  • PVC:工作负载发出的存储申请。
  • 绑定:Kubernetes 把一个 PVC 匹配到一个合适的 PV。
  • StorageClass:描述存储供给策略的规则。

你可以把它理解成:应用只说“我需要什么”,集群决定“具体怎么给”。

这套抽象为什么重要

如果没有 PV / PVC,应用 YAML 就得直接依赖底层存储实现,业务和基础设施会耦合得很死。Kubernetes 把需求和实现分开,是为了让应用声明容量、访问模式、类别,而具体的卷创建、绑定和回收由平台处理。

但这也意味着:一旦存储出问题,Pod 往往只是表面现象,真正的根因常常藏在 PVC、PV、StorageClass 或 provisioner 里。

基本工作流

  1. 应用通过 PVC 提出存储需求。
  2. Kubernetes 找一个匹配的 PV,或者通过 StorageClass 动态创建一个。
  3. PVC 进入 Bound 状态。
  4. Pod 挂载这个 claim。
  5. 应用开始对挂载路径读写数据。

如果你使用的是动态供给,那么真正串起这条链路的是 StorageClass -> provisioner:PVC 只负责声明 storageClassName,StorageClass 再决定让哪个 provisioner 或 CSI 驱动去创建底层卷。

动态供给和静态供给

动态供给

这是大多数现代集群的默认方式。PVC 指向一个 StorageClass,由后端自动创建卷。

换句话说,PVC 本身并不会直接“造盘”,而是通过 StorageClass 把请求交给对应的 provisioner 去完成。

适合场景:

  • 集群里已经有可用的存储 provisioner
  • 希望减少人工干预
  • 希望开发和测试环境流程更统一

静态供给

管理员先创建 PV,应用再用 PVC 去绑定。

适合场景:

  • 必须预先准备好特定存储
  • 正在迁移已有卷
  • 需要对某些存储卷做更严格的人工控制

最小示例

下面是最值得先理解的一组最小 YAML:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard
---
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: nginx:1.25
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: app-data

先看懂这组 YAML,再去看 StatefulSet 或数据库类工作负载会轻松很多。

访问模式到底意味着什么

  • ReadWriteOnce(RWO):通常允许一个节点以读写方式挂载。
  • ReadWriteMany(RWX):多个节点可以共享读写。
  • ReadOnlyMany(ROX):多个节点可以共享只读。

访问模式不是随便填的偏好项,而是底层存储能力的真实映射。你填了 RWX,不代表后端就一定支持。

回收策略:删除后会发生什么

这个细节很多人直到出事才会重视。

  • Delete:PVC 删除后,底层卷也跟着删除。
  • Retain:PVC 删除后,底层卷保留,等待人工处理或复用。

如果你的数据不能轻易丢,Retain 就不是一个可有可无的参数,而是很重要的安全边界。

扩容与快照

很多存储后端支持 PVC 扩容,但“改大容量”不一定等于“应用马上就能看到更大文件系统”。有时候文件系统本身还要扩展。

快照则是另外一回事。卷能持久化,不等于你有备份体系。备份、恢复、演练,仍然要有自己独立的流程。

用一句话理解 StorageClass

StorageClass 就是集群里关于存储的默认策略描述。它可能决定:

  • 用哪个 provisioner
  • 用什么性能等级
  • 如何回收卷
  • 用什么绑定模式
  • 是否受可用区拓扑约束

如果说 PV/PVC 是存储契约,那 StorageClass 就是契约背后的策略引擎。

所以这篇最好和 kubernetes-quickstart-storageclass.md 对着看:那篇更偏平台视角,会讲清楚 provisioner、默认类、回收策略和社区里常见的 CSI 驱动是怎么串到 PVC 动态供给里的。

最常见的失败模式

PVC 一直 Pending

最常见原因包括:

  • StorageClass 不存在
  • provisioner 没有正常工作
  • 请求的容量或访问模式不被支持
  • 可用区 / 拓扑约束不满足

Pod 因为 claim 没绑定而 Pending

这类问题最容易误导人,因为表面上看到的是 Pod 没起来,实际上根因可能完全在存储层。

卷已经存在,但挂载失败

这时候问题可能已经从绑定阶段进入节点执行阶段,比如 CSI 挂载失败、权限不对、文件系统异常等。

一套实用的排障顺序

遇到存储问题时,别先盯着 Pod,先走这套顺序:

kubectl get pvc -n demo
kubectl describe pvc app-data -n demo
kubectl get pv
kubectl describe pv <pv-name>
kubectl get storageclass
kubectl get events -n demo --sort-by=.metadata.creationTimestamp

然后依次问:

  1. PVC 是不是 Bound
  2. 引用的 StorageClass 对不对?
  3. 后端是否支持这个容量和访问模式?
  4. 是否被 zone / topology 约束卡住?
  5. 如果已经绑定,是否在节点挂载阶段失败?

拓扑与可用区问题

很多存储后端是按可用区工作的。如果 Pod 被调度到错误的 zone,卷可能永远挂不上。这也是为什么一旦进入多可用区集群,存储就不再只是“容量问题”,而变成了“调度问题”。

性能与成本取舍

存储并不只是“够不够大”的问题:

  • 本地盘或 SSD 更快,但灵活性通常差一些
  • 网络存储更容易统一管理,但延迟可能更高
  • 高性能类存储很容易把成本抬上去

选择 StorageClass 时,最好基于真实 IO 模式,而不是凭直觉“多买一点保险”。

什么场景下别急着上 PVC

并不是所有数据都值得一开始就做持久化。

  • 可重建缓存可以不持久化
  • 临时工作目录可能用 emptyDir 就够
  • 一些无状态服务的数据本就不需要跨 Pod 保留

持久化很重要,但它也意味着你要承担备份、迁移、恢复和清理这些额外责任。

FAQ

Q: Pod 一直 Pending,是不是先看 Pod 就够了? A: Pod 要看,但如果涉及存储,PVC 往往更值得优先看。很多问题本质上是 claim 没绑定,而不是 Pod YAML 本身出错。

Q: PV 在不在,就等于我有备份了吗? A: 不是。PV 只解决持久化,不解决备份、恢复和灾备演练。

Q: 什么时候最该关心 RetainDeleteA: 只要你在意误删后的数据命运,就应该明确评估回收策略。它会直接影响删除后的恢复空间。

下一步阅读

  • 接着读 kubernetes-quickstart-storageclass.md
  • 再读 kubernetes-quickstart-statefulset.md
  • 如果你要跑数据库,再继续读 MySQL 相关 QuickStart

收个尾

存储问题表面上常常表现为“Pod 没起来”,但真正的关键往往在 claim、class、provisioner 和拓扑约束。先把存储链路看清,再去怀疑应用本身,通常效率更高。

参考链接