1. 为什么 Kubernetes 要把“存储提供”和“存储使用”拆开
在 Kubernetes 里,计算资源和存储资源不是同一类问题。Pod、Deployment 负责应用运行,但存储往往来自云盘、NAS、本地盘、分布式块存储等外部系统。官方文档对 PersistentVolume 子系统的定义非常明确:它通过一组 API,把“存储如何被提供”与“存储如何被消费”解耦。::cite[212]
这件事非常重要。因为对应用开发者来说,我只想说“给我一块 20Gi 的可读写存储”;但对平台管理员来说,我关心的是“这块存储来自什么类型的后端、性能级别如何、是否支持扩容、删除后是否回收”。PV / PVC / StorageClass 就是 Kubernetes 为这两个视角提供的协作机制。::cite[212]
2. 三个核心对象先记住
| 对象 | 站在谁的视角 | 主要职责 |
|---|---|---|
| PV(PersistentVolume) | 集群/平台管理员 | 描述一块可用存储资源 |
| PVC(PersistentVolumeClaim) | 应用使用者 | 申请一块符合条件的存储 |
| StorageClass | 平台管理员 | 定义某类存储能力与动态供给方式 |
一句话理解:
- PV 是“存储本体”
- PVC 是“存储申请单”
- StorageClass 是“存储套餐说明书”::cite[212]
3. PV 与 PVC 的角色分工
3.1 PV:平台侧准备好的存储资源
PV 是集群中的一类资源对象,它描述容量、访问模式、回收策略、底层卷类型等信息。它并不直接绑定某个 Pod,而是等待 PVC 来匹配。::cite[212]
常见字段包括:
capacity.storage:容量大小accessModes:访问模式,比如ReadWriteOncepersistentVolumeReclaimPolicy:回收策略storageClassName:所属存储类csi/nfs/hostPath等:底层存储实现
3.2 PVC:应用侧发起的存储请求
PVC 由应用侧提交,它不关心底层具体是哪个云盘、哪台存储阵列,只描述“我要多大、我需要什么访问模式、最好属于哪个 StorageClass”。Kubernetes 会把 PVC 与合适的 PV 绑定起来。::cite[212]
PVC 常见字段包括:
resources.requests.storage:申请容量accessModes:期望访问模式storageClassName:期望的存储类volumeName:如果你想显式绑定某个 PV,也可以指定
3.3 Pod 如何使用 PVC
Pod 不直接挂载 PV,而是挂载 PVC。也就是说,应用永远通过“声明需求”来拿存储,而不是直接操作底层卷对象。这种设计让应用 YAML 更稳定,也让平台可以在后端自由切换实现。::cite[212]
4. 静态供给:先有 PV,再来 PVC
静态供给(Static Provisioning)适合这样一种场景:管理员已经提前准备好存储,并显式创建了 PV;开发者只需要通过 PVC 去认领它。
4.1 静态供给的工作流程
- 管理员创建 PV
- 开发者创建 PVC
- 控制器根据容量、访问模式、StorageClass 等条件进行匹配
- Pod 通过 PVC 使用存储
4.2 完整示例:静态 PV + PVC + Pod
下面用 hostPath 做一个最小化演示。注意,它只是为了帮助理解 PV/PVC 绑定机制,不代表生产推荐方案。
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv-static
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /data/k8s/demo-pv
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc-static
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manual
---
apiVersion: v1
kind: Pod
metadata:
name: demo-pod-static
spec:
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: app-data
mountPath: /usr/share/nginx/html
volumes:
- name: app-data
persistentVolumeClaim:
claimName: demo-pvc-static
执行步骤:
kubectl apply -f pv-pvc-static-demo.yaml
kubectl get pv
kubectl get pvc
kubectl describe pvc demo-pvc-static
如果看到 PV 和 PVC 都进入 Bound 状态,就说明绑定成功。::cite[212]
5. 动态供给:PVC 一提交,系统自动创建卷
随着云原生场景普及,管理员不可能为每一个业务都手工准备 PV。于是 Kubernetes 引入了动态供给(Dynamic Provisioning)机制:只要 PVC 指定了某个 StorageClass,系统就可以通过对应的 Provisioner 自动创建实际存储,再生成 PV 并完成绑定。官方文档把这视为 StorageClass 的核心能力之一。::cite[214]
5.1 动态供给的工作流程
- 管理员定义 StorageClass
- 应用创建引用该 StorageClass 的 PVC
- 外部或内置 Provisioner 创建底层卷
- Kubernetes 自动生成 PV
- PVC 与新 PV 自动绑定
- Pod 挂载 PVC 使用该卷
5.2 为什么动态供给更符合现代平台化
因为它把“存储申请”变成了自助式体验:
- 开发者不需要知道底层存储实现
- 平台可以通过不同 StorageClass 暴露不同存储档位
- 自动化程度更高,更适合多租户集群
6. StorageClass:如何定义存储能力
官方文档对 StorageClass 的定义很清楚:它用来描述管理员提供的存储类别,不同类别可以映射到不同 QoS、备份策略或其他平台策略。::cite[214]
6.1 一个 StorageClass 里通常会定义什么
provisioner:由谁来创建卷parameters:后端存储相关参数reclaimPolicy:PVC 删除后怎么处理卷allowVolumeExpansion:是否支持扩容volumeBindingMode:何时绑定卷
6.2 示例:定义一个 StorageClass
下面是一个典型的 CSI 场景写法。provisioner 的名称需要根据你的实际驱动来调整。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: csi.example.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
parameters:
type: ssd
fsType: ext4
这里可以重点看三件事:
Delete表示卷跟随 PVC 生命周期自动删除allowVolumeExpansion: true表示该类卷允许扩容WaitForFirstConsumer表示等 Pod 真正被调度时再完成卷绑定,有利于拓扑感知存储正确落位::cite[214]
7. 动态供给完整示例:StorageClass + PVC + Pod
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: demo-csi-sc
provisioner: csi.example.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
parameters:
type: standard
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc-dynamic
spec:
accessModes:
- ReadWriteOnce
storageClassName: demo-csi-sc
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
name: demo-pod-dynamic
spec:
containers:
- name: app
image: nginx:1.27
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: demo-pvc-dynamic
执行步骤:
kubectl apply -f dynamic-storage-demo.yaml
kubectl get sc
kubectl get pvc
kubectl get pv
kubectl describe pvc demo-pvc-dynamic
如果你的集群里已经安装并配置好对应的 CSI 驱动,这个 PVC 会触发动态创建底层卷,再自动生成 PV 完成绑定。::cite[214]
8. 存储绑定:PVC 到底是怎么找到 PV 的
绑定过程可以理解为“需求匹配供给”。PVC 会根据下面几项条件寻找合适 PV:
- 容量是否满足申请
- 访问模式是否兼容
storageClassName是否一致- 是否已经被其他 PVC 占用
一旦绑定成功,这个绑定关系通常具有排他性,尤其在 ReadWriteOnce 的场景下更明显。官方文档建议把 PV/PVC 视为一组面向存储的声明式匹配机制,而不是手工挂载动作。::cite[212]
8.1 volumeBindingMode 为什么重要
StorageClass 中的 volumeBindingMode 会影响“何时做出绑定决策”。其中 WaitForFirstConsumer 很关键,因为它允许调度器先综合 Pod、节点拓扑、卷可用区等信息,再做更合理的绑定与供给。对于跨可用区集群,这往往比“立刻绑定”更安全。::cite[214]
9. 回收策略:PVC 删掉以后,卷怎么办
PV 的 persistentVolumeReclaimPolicy 决定资源释放后的处理方式。常见策略如下:
| 策略 | 含义 | 适用场景 |
|---|---|---|
Retain |
保留数据,人工处理 | 重要业务数据、审慎删除 |
Delete |
自动删除底层卷 | 临时环境、自动化资源回收 |
在生产里,数据库等关键系统常常更偏向 Retain,避免误删 PVC 导致真实数据立即消失;而测试环境、临时环境更适合 Delete,减少资源残留。官方文档也强调,回收策略属于平台能力设计的一部分。::cite[212]
10. 扩容机制:卷满了以后怎么办
v1.32 文档中,StorageClass 可以通过 allowVolumeExpansion 声明是否支持卷扩容。如果底层驱动支持,用户只需修改 PVC 的 requests.storage,系统即可触发扩容流程。::cite[214]
10.1 PVC 扩容示例
先创建一个支持扩容的 PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: resize-demo-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: demo-csi-sc
resources:
requests:
storage: 10Gi
当你需要扩容时,直接修改为更大容量:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: resize-demo-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: demo-csi-sc
resources:
requests:
storage: 20Gi
执行命令:
kubectl apply -f resize-demo-pvc.yaml
kubectl describe pvc resize-demo-pvc
扩容能否成功,取决于两个条件:
- StorageClass 允许扩容
- 底层 CSI 或存储后端真正支持扩容::cite[214]
11. 实战中最容易踩的坑
11.1 申请了 PVC,却一直 Pending
常见原因有:
- 没有可匹配的 PV
storageClassName不一致- 集群没有可用的默认 StorageClass
- 对应 Provisioner/CSI 驱动未安装或异常
- 容量、访问模式不匹配
排查顺序建议:
kubectl get pvc
kubectl describe pvc <pvc-name>
kubectl get pv
kubectl get sc
11.2 误把 hostPath PV 当生产持久化
这种写法在单机测试环境很方便,但在真正的多节点集群中缺乏高可用、调度弹性和可靠恢复能力。它更适合做教学与实验,不适合作为平台标准存储方案。
11.3 误以为删除 PVC 就一定删除数据
不一定。真正决定是否删除底层卷的,是 PV 或 StorageClass 上的回收策略。如果是 Retain,PVC 删除后数据可能仍然保留,需要人工清理。::cite[212]
12. 记忆方法:把三者串起来
你可以把整个过程记成一句话:
开发者通过 PVC 提出需求,平台通过 StorageClass 定义规则,系统最终用 PV 表示实际存储结果。
如果是静态供给,PV 先存在;如果是动态供给,PVC 会驱动系统自动创建 PV。理解了这一层,后面再看 StatefulSet、数据库、CSI,就会非常顺。
13. 小结
本章的重点不是背 YAML,而是理解 Kubernetes 存储抽象的设计思想:
- PV 表示“已有或将被创建的存储资源”
- PVC 表示“应用侧声明的存储需求”
- StorageClass 表示“平台暴露给租户的存储能力模板”
- 静态供给适合手工管理资源
- 动态供给适合平台化和规模化使用
- 绑定、回收、扩容是生产环境必须理解的三件事
下一章我们继续进入有状态工作负载的核心对象:StatefulSet。
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!