在 Kubernetes(K8s)中,StorageClass、PersistentVolume(PV) 和 PersistentVolumeClaim(PVC) 是用于管理持久化存储的核心概念。它们之间的关系可以用“供需模型”来类比:
一、基本概念与关系
| 组件 |
角色 |
类比 |
| StorageClass |
定义存储的“类型”或“类别”,支持动态供应 |
存储供应商提供的“套餐”(如 SSD、HDD、NFS 等) |
| PersistentVolume (PV) |
集群中实际可用的存储资源(由管理员预配或通过 StorageClass 动态创建) |
“仓库里的货物” |
| PersistentVolumeClaim (PVC) |
用户对存储资源的请求(声明需要多少、什么类型的存储) |
“用户下的订单” |
关系流程:
- 用户创建一个 PVC,声明所需存储大小和 StorageClass。
- 如果该 PVC 指定的 StorageClass 支持动态供应(Dynamic Provisioning),Kubernetes 会自动创建一个对应的 PV。
- PVC 与 PV 绑定(Bound) 后,Pod 就可以通过 PVC 使用该 PV。
⚠️ 注意:如果没有 StorageClass 或未启用动态供应,则需要管理员手动创建 PV,然后 PVC 才能与之匹配绑定。
二、使用 NFS 时如何动态创建 PV?
默认情况下,Kubernetes 不原生支持 NFS 的动态供应(即没有内置的 NFS provisioner)。但可以通过以下方式实现:
方案:部署 NFS Subdir External Provisioner(社区推荐)
这是一个外部 provisioner,它监听带有特定 StorageClass 的 PVC,并自动在 NFS 服务器上创建子目录作为 PV。
三、具体操作示例
步骤 1:准备 NFS 服务器
假设你有一台 NFS 服务器,IP 为 192.168.1.100,共享目录为 /nfs/data。
1 2 3 4
| sudo mkdir -p /nfs/data echo "/nfs/data *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports sudo exportfs -ra
|
步骤 2:部署 NFS Subdir External Provisioner
使用 Helm 或 YAML 部署。这里用 YAML 示例(来自 kubernetes-sigs/nfs-subdir-external-provisioner):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner namespace: default spec: replicas: 1 selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: k8s-sigs.io/nfs-subdir-external-provisioner - name: NFS_SERVER value: 192.168.1.100 - name: NFS_PATH value: /nfs/data volumes: - name: nfs-client-root nfs: server: 192.168.1.100 path: /nfs/data --- apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io
|
应用:
1
| kubectl apply -f nfs-provisioner.yaml
|
步骤 3:创建 StorageClass
1 2 3 4 5 6 7 8
| apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-client provisioner: k8s-sigs.io/nfs-subdir-external-provisioner parameters: archiveOnDelete: "false"
|
1
| kubectl apply -f nfs-sc.yaml
|
步骤 4:创建 PVC(触发动态创建 PV)
1 2 3 4 5 6 7 8 9 10 11 12
| apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-nfs-pvc spec: accessModes: - ReadWriteMany storageClassName: nfs-client resources: requests: storage: 5Gi
|
1
| kubectl apply -f my-pvc.yaml
|
✅ 此时,NFS provisioner 会自动在 NFS 服务器的 /nfs/data 下创建一个子目录(如 default-my-nfs-pvc-pvc-<uuid>),并生成对应的 PV。
步骤 5:在 Pod 中使用 PVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: nginx volumeMounts: - name: nfs-storage mountPath: /data volumes: - name: nfs-storage persistentVolumeClaim: claimName: my-nfs-pvc
|
四、总结
- StorageClass 定义了存储类型和 provisioner。
- PVC 是用户对存储的请求。
- PV 是实际的存储资源,可静态预配或通过 StorageClass 动态创建。
- NFS 默认不支持动态供应,但可通过 nfs-subdir-external-provisioner 实现。
- 动态创建后,每个 PVC 对应 NFS 服务器上的一个唯一子目录,实现多租户隔离。
💡 提示:生产环境中建议使用更成熟的 CSI 驱动(如 NFS CSI Driver),但 nfs-subdir-provisioner 适合轻量级或测试场景。