
本质: 你不再需要手动创建 PersistentVolume (PV),而是通过 StorageClass 和 Provisioner 自动生成。
本文将详细介绍如何在 Kubernetes 集群中,使用 NFS 作为后端存储,结合 nfs-subdir-external-provisioner 这款官方推荐的 云原生 动态存储控制器,来实现 StorageClass 的动态存储功能。
前期准备:NFS服务器配置及客户端安装
首先,需要准备好 NFS 服务器,并确保所有 Kubernetes 节点都已安装 NFS 客户端工具。
- NFS Server:
11.0.1.8
- 共享目录:
/data/nfs-server/sc
在 NFS 服务器上执行以下命令:
# 创建共享存储路径
mkdir -p /data/nfs-server/sc
chmod -R 777 /data/nfs-server/sc
echo “/data/nfs-server/sc *(rw,no_root_squash,sync)” >> /etc/exports
exportfs -r
1. 创建名称空间 (Namespace)
我们将所有相关资源部署在一个独立的名称空间中,便于管理。
# 名称空间资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# cat > 01-ns.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: nfs-provisioner
EOF
2. 创建 RBAC 授权
Provisioner 作为一个控制器 Pod,需要相应的权限来操作 Kubernetes 的存储资源(如 PV、PVC、StorageClass 等)。
# rbac资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# cat > 02-rbac.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: nfs-provisioner
---
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: [“endpoints“]
verbs: [“get“, “list“, “watch“, “create“, “update“]
- apiGroups: [““]
resources: [“events“]
verbs: [“create“, “update“, “patch“]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-provisioner
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
EOF
3. 部署 Provisioner (Deployment)
这里我们采用经典的 nfs-subdir-external-provisioner 方案,它是一个官方维护的控制器,能监听 PVC 请求并自动创建对应的 PV 目录。
注意: 以下 YAML 中的 NFS 服务器 IP 和共享路径需要根据你的实际环境修改。
# deploy资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy.yaml]
# cat > 03-deploy.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: nfs-provisioner
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.cn-hangzhou.aliyuncs.com/google_containers/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: dinginx/nfs
- name: NFS_SERVER
value: 11.0.1.8 #⚠️ 修改你的 NFS IP
- name: NFS_PATH
value: /data/nfs-server/sc #⚠️ 修改存储目录
volumes:
- name: nfs-client-root
nfs:
server: 11.0.1.8 #⚠️ 修改你的 NFS IP
path: /data/nfs-server/sc #⚠️ 修改存储目录
EOF
4. 创建动态存储类 (StorageClass)
StorageClass 是动态存储的核心,它定义了使用哪个 Provisioner 以及创建 PV 时的参数。
# 创建sc资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy.yaml]
# cat > 04-sc.yaml <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc
# provisioner: fuseim.pri/ifs # or choose another name, must match deployment‘s env PROVISIONER_NAME’
provisioner: dinginx/nfs
parameters:
# 注意哈,仅对“reclaimPolicy: Delete“时生效,如果回收策略是“reclaimPolicy: Retain”,则无视此参数!
# 如果设置为false,删除数据后,不会在存储卷路径创建“archived-*”前缀的目录哟!
# archiveOnDelete: “false”
# 如果设置为true,删除数据后,会在存储卷路径创建“archived-*”前缀的目录哟
archiveOnDelete: “true”
# 声明PV回收策略,默认值为Delete
reclaimPolicy: Retain
volumeBindingMode: Immediate
EOF
5. 创建 PVC 进行测试
现在我们可以创建一个 PersistentVolumeClaim (PVC) 来触发动态供给流程。
# 编写测试PVC资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy.yaml]
# cat > 05-pvc.yaml <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dinginx-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-sc
resources:
requests:
storage: 1Gi
EOF
6. 创建所有资源并验证
将前面创建的所有 YAML 文件应用,并查看资源状态。
# 创建所有资源
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl apply -f .
# 查看资源
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl -n nfs-provisioner get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-7ccdbfcf8c-bpbpj 1/1 Running 0 5m6s
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl apply -f 05-pvc.yaml
persistentvolumeclaim/dinginx-pvc created
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-e7a0be94-faa3-4f61-8917-78a1d91a8dd0 1Gi RWX Retain Bound default/dinginx-pvc nfs-sc <unset> 4s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/dinginx-pvc Bound pvc-e7a0be94-faa3-4f61-8917-78a1d91a8dd0 1Gi RWX nfs-sc <unset> 4s
可以看到,一个名为 dinginx-pvc 的 PVC 已经创建成功,并且系统自动创建了一个对应的 PV 与之绑定。
7. 创建一个 Deployment 来使用这个 PVC
让我们创建一个应用 Deployment,将上一步创建的 PVC 挂载到其 Pod 中。
# 创建deploy资源清单
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# cat 06-deploy-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dinginx-deploy-pvc001
spec:
replicas: 3
selector:
matchLabels:
app: dinginx001
template:
metadata:
labels:
app: dinginx001
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: dinginx-pvc
containers:
- name: shop01
image: harbor.dinginx.org/global-shopping/global-shopping:v4
volumeMounts:
- name: data
mountPath: /dinginx-pvc
8. 测试数据持久性
启动应用,并在容器内创建测试数据,然后删除资源以验证数据是否会丢失。
# 查看资源
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl get pods
NAME READY STATUS RESTARTS AGE
dinginx-deploy-pvc001-5986bff9d7-4vzm9 1/1 Running 0 6s
dinginx-deploy-pvc001-5986bff9d7-7r7k5 1/1 Running 0 6s
dinginx-deploy-pvc001-5986bff9d7-dqcjp 1/1 Running 0 6s
# 进到容器生成测试数据
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl exec -it pods/dinginx-deploy-pvc001-5986bff9d7-4vzm9 -- bash
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:/usr/share/nginx/html
# cd
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:~
# ls /dinginx-pvc/
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:~
# mkdir /dinginx-pvc/dinginx
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:~
# cd /dinginx-pvc/dinginx/
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:/dinginx-pvc/dinginx
# touch SUCCESSED
root@dinginx-deploy-pvc001-5986bff9d7-4vzm9:/dinginx-pvc/dinginx
# exit
exit
# nfs服务端测试数据
[root@harbor-club /data/nfs-server/sc]
# tree default-dinginx-pvc-pvc-81a0a868-7b0a-49c1-936a-f712979e1051
default-dinginx-pvc-pvc-81a0a868-7b0a-49c1-936a-f712979e1051
└── dinginx
└── SUCCESSED
1 directory, 1 file
# 验证删除pod资源,数据不会丢失
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl delete -f 05-pvc.yaml -f 06-deploy-pvc.yaml
persistentvolumeclaim “dinginx-pvc” deleted from default namespace
deployment.apps “dinginx-deploy-pvc001” deleted from default namespace
[root@k8s-master01 /data/manifests/project/06-nfs-pvc-deploy]
# kubectl get pvc,po
No resources found in default namespace.
# nfs服务端验证数据未丢失
[root@harbor-club /data/nfs-server/sc]
# tree default-dinginx-pvc-pvc-81a0a868-7b0a-49c1-936a-f712979e1051 ; date
default-dinginx-pvc-pvc-81a0a868-7b0a-49c1-936a-f712979e1051
└── dinginx
└── SUCCESSED
1 directory, 1 file
Wed Apr 15 05:50:36 CST 2026
如上所示,即使在 Kubernetes 中删除了 PVC 和应用 Pod,存储在 NFS 服务器上的数据依然完好无损。这得益于我们在 StorageClass 中设置的 reclaimPolicy: Retain 策略。
总结
通过以上步骤,我们成功地在 Kubernetes 中搭建了一套基于 NFS 的动态存储方案。这套方案极大地简化了持久化存储的管理,开发者只需声明所需存储的容量和访问模式(通过 PVC),系统就能自动按需创建 PV,非常适合在 DevOps 流程和需要频繁创建有状态应用的场景中使用。如果你想探索更多类似的 云原生 存储方案或与其他开发者交流实践经验,欢迎访问云栈社区进行深度讨论。