StorageClass、PV、PVC之间的关系

在 Kubernetes(K8s)中,StorageClassPersistentVolume(PV)PersistentVolumeClaim(PVC) 是用于管理持久化存储的核心概念。它们之间的关系可以用“供需模型”来类比:


一、基本概念与关系

组件 角色 类比
StorageClass 定义存储的“类型”或“类别”,支持动态供应 存储供应商提供的“套餐”(如 SSD、HDD、NFS 等)
PersistentVolume (PV) 集群中实际可用的存储资源(由管理员预配或通过 StorageClass 动态创建) “仓库里的货物”
PersistentVolumeClaim (PVC) 用户对存储资源的请求(声明需要多少、什么类型的存储) “用户下的订单”

关系流程:

  1. 用户创建一个 PVC,声明所需存储大小和 StorageClass。
  2. 如果该 PVC 指定的 StorageClass 支持动态供应(Dynamic Provisioning),Kubernetes 会自动创建一个对应的 PV
  3. 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
# 在 NFS 服务器上(以 Linux 为例)
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
# nfs-provisioner.yaml
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
# nfs-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 必须与上面 Deployment 中的 PROVISIONER_NAME 一致
parameters:
archiveOnDelete: "false" # 删除 PVC 时是否保留数据(设为 "true" 会重命名目录而非删除)
1
kubectl apply -f nfs-sc.yaml

步骤 4:创建 PVC(触发动态创建 PV)

1
2
3
4
5
6
7
8
9
10
11
12
# my-pvc.yaml
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 适合轻量级或测试场景。