PV、PVC、StorageClass的区别和联系原创
在 Kubernetes 中,持久化存储是一个重要的概念,它通过 PersistentVolume (PV)、PersistentVolumeClaim (PVC) 和 StorageClass 来实现。下面我们详细介绍它们的区别和联系。
版本说明
本文写于 2025-03。
重要更新:所有 kubernetes.io/* 形式的 in-tree 存储插件(如 kubernetes.io/aws-ebs、kubernetes.io/gce-pd)自 1.23 起进入 CSI Migration,到 1.27 已从 kubelet/controller-manager 中彻底移除。现行集群必须使用对应的 CSI driver 名(AWS EBS 为 ebs.csi.aws.com,GCE PD 为 pd.csi.storage.gke.io)。本文示例已按 CSI 写法更新。
# 1. PersistentVolume (PV)
定义: PersistentVolume 是 Kubernetes 中的一种资源对象,它表示集群中的一个存储卷。PV 是由管理员预先创建的,或者通过 StorageClass 动态创建的。
特点:
- PV 是集群范围的资源。
- PV 的生命周期独立于 Pod。
- PV 定义了存储的容量、访问模式、回收策略等。
示例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: "" # 静态 PV 建议留空,避免被动态供应逻辑重复匹配
hostPath:
path: /mnt/data
2
3
4
5
6
7
8
9
10
11
12
13
hostPath只适合单节点或测试环境:它指的是「Pod 恰好被调度到的那个节点上的路径」,Pod 一旦漂移到别的节点,读到的就是一个空目录。多节点集群要用本地盘请改local类型的 PV,并配上nodeAffinity把 PV 钉在特定节点上。
# 2. PersistentVolumeClaim (PVC)
定义: PersistentVolumeClaim 是 Kubernetes 中的一种资源对象,它表示用户对存储的申请。PVC 用于绑定到 PV,并为 Pod 提供存储。
特点:
- PVC 是用户的存储需求声明。
- PVC 会自动绑定到符合条件的 PV。
- PVC 通过
accessModes和storageClassName来匹配 PV。
示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
2
3
4
5
6
7
8
9
10
11
# 3. StorageClass
定义: StorageClass 是 Kubernetes 中的一种资源对象,它定义了存储卷的动态配置方式。通过 StorageClass,可以动态地创建 PV,而无需管理员手动创建。
特点:
- StorageClass 定义了存储的提供方式。
- StorageClass 可以指定不同的存储类型(如 NFS、Ceph、AWS EBS 等)。
- StorageClass 可以设置
provisioner和parameters来配置存储。
示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: ebs.csi.aws.com # CSI driver 名;旧的 kubernetes.io/aws-ebs 已在 1.27 移除
volumeBindingMode: WaitForFirstConsumer # 见下文「坑与边界」,多可用区集群必须设
reclaimPolicy: Delete
parameters:
type: gp3
2
3
4
5
6
7
8
9
# 4. 区别
| 资源对象 | 定义 | 作用 |
|---|---|---|
| PersistentVolume (PV) | 表示集群中的存储卷 | 由管理员或 StorageClass 创建,用于存储数据 |
| PersistentVolumeClaim (PVC) | 用户的存储需求声明 | 用户通过 PVC 申请存储,并绑定到 PV |
| StorageClass | 动态配置存储卷的方式 | 定义存储卷的类型和配置,用于动态创建 PV |
# 5. 联系
PV 与 PVC 的联系:
- PVC 是用户的存储需求声明,PV 是存储的实际实现。
- PVC 会自动绑定到符合条件的 PV。
StorageClass 与 PV 的联系:
- StorageClass 定义了动态创建 PV 的方式。
- 如果使用 StorageClass,PV 会根据 StorageClass 的配置动态生成。
StorageClass 与 PVC 的联系:
- PVC 可以通过
storageClassName字段指定使用哪个 StorageClass。 - PVC 的
storageClassName决定了绑定的 PV 的类型。
- PVC 可以通过
# 6. 使用场景
静态存储: 如果集群管理员已经预先创建了 PV,用户可以通过 PVC 绑定到这些 PV。
动态存储: 如果使用了 StorageClass,用户可以通过 PVC 动态创建 PV,而无需管理员手动干预。
# 7. 示例流程
以下是一个使用 StorageClass 动态创建 PV 的完整流程:
- 创建 StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: io1
iopsPerGB: "10"
fsType: ext4
2
3
4
5
6
7
8
9
10
- 创建 PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fast-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fast-storage
2
3
4
5
6
7
8
9
10
11
- 检查自动创建的 PV:
kubectl get pvc fast-pvc
kubectl get pv
2
期望输出:kubectl get pvc 里 STATUS 是 Bound,VOLUME 一列出现一个 pvc-<uuid> 形式的 PV 名——这个名字由 provisioner 自动生成,能看到它就说明动态供应真的发生了。
如果 PVC 长期停在 Pending,不要去看 PVC 本身,要看它的事件:
kubectl describe pvc fast-pvc | tail -20
三种最常见的事件与含义:
| 事件信息 | 含义 |
|---|---|
no persistent volumes available for this claim and no storage class is set | PVC 没指定 storageClassName,集群也没有默认 StorageClass |
storageclass.storage.k8s.io "fast-storage" not found | StorageClass 名字写错,或根本没创建 |
| 长时间无任何事件 | provisioner 名写错(例如仍在用已被移除的 kubernetes.io/aws-ebs),没有任何控制器在监听这个 StorageClass |
# 坑与边界
1. volumeBindingMode 不设 WaitForFirstConsumer,多可用区集群会挂不上。 默认值是 Immediate:PVC 一创建就立刻在某个可用区把云盘开出来。等 Pod 真正被调度时,调度器可能把它放到另一个可用区,而块存储(EBS/PD)是不能跨 AZ 挂载的,结果就是 Pod 永远卡在 Pending,事件是 volume node affinity conflict。WaitForFirstConsumer 把开盘动作推迟到 Pod 调度决定之后,让存储跟着 Pod 走。
2. persistentVolumeReclaimPolicy 决定 PVC 删掉之后数据还在不在。 Delete(动态供应的默认值)会连底层云盘一起删除,误删 PVC 等于数据蒸发;Retain 会保留 PV 和数据,但 PV 进入 Released 状态后不会被自动重新绑定——必须手工清掉 PV 的 spec.claimRef 才能给新 PVC 用。生产上的库存数据建议 Retain,代价是要人工回收。
3. PVC 的容量请求是「下限」,不是「精确值」。 静态绑定时,一个请求 5Gi 的 PVC 完全可能绑到一个 100Gi 的 PV 上,PVC 会独占整个 PV,多余容量白白浪费。静态 PV 池要按容量分档准备,不要指望它切分。
4. 扩容能扩不能缩。 StorageClass 需要 allowVolumeExpansion: true 才允许改 PVC 的 resources.requests.storage,且只能变大;缩容 Kubernetes 完全不支持,只能新建 PVC 再迁数据。
5. accessModes 是「PV 声明自己支持什么」,不是 Kubernetes 强制的。 块存储(EBS、PD、大部分 CSI 云盘)实际只支持 ReadWriteOnce——同一时刻只能被一个节点挂载。要多 Pod 跨节点同时读写,必须用文件存储(NFS、CephFS、EFS)并声明 ReadWriteMany。把一个 RWO 的 PVC 挂给多副本 Deployment,是最常见的翻车方式。
通过以上流程,PVC 会自动绑定到由 StorageClass 动态创建的 PV。
# 总结
- PV 是存储的实际实现。
- PVC 是用户的存储需求声明。
- StorageClass 提供了动态创建 PV 的方式。
- 三者共同构成了 Kubernetes 中的持久化存储体系。