假设有如下三个节点的 K8S 集群:
k8s31master 是控制节点
k8s31node1、k8s31node2 是工作节点
容器运行时是 containerd
一、什么是 PV 与 PVC
1.1、什么是 PV(PersistentVolume 持久卷)
PV 是集群中由管理员配置的一段网络存储,它是一个集群级别的资源,就像集群中的一块可用磁盘空间,与具体的 Pod 无关。可以将 PV 看作是物理存储在 K8S 中的抽象表示。
1.2、什么是 PVC(PersistentVolumeClaim 持久卷声明|持久卷申领)
PVC 是用户对存储资源的请求,它消耗 PV 提供的资源。可以理解为 Pod 需要使用存储时,向集群提出的一个 "申请",描述了应用程序对存储的需求,如存储容量、访问模式等。
PVC 是 namespace级别的资源,即它只能在特定的命名空间内使用。用户通过 PVC 来使用存储,而不需要关心底层实际的存储实现细节,Kubernetes 会负责将 PVC 与合适的 PV 进行绑定。
当用户创建一个 PVC 时,Kubernetes 会在集群中寻找满足 PVC 需求(如容量、访问模式等)的 PV,并将它们进行绑定。一旦绑定成功,PVC 就可以被 Pod 挂载使用。
1.3、持久卷供应方式
PV 持久卷的供应有两种方式:静态供应或动态供应。
静态供应:
集群管理员事先创建若干 PV 卷。这些卷对象带有真实存储的细节信息,并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
动态供应:
通过 StorageClass 自动创建 PV,无需管理员预先创建。当用户创建 PVC 时,Kubernetes 根据 StorageClass 的配置动态创建 PV。
1.4、持久卷回收策略( persistentVolumeReclaimPolicy**)**
bash
kubectl explain pv.spec.persistentVolumeReclaimPolicy
保留(Retain 手动回收):
回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released )"。
由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。
管理员可以通过下面的步骤来手动回收该卷:
1、删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产在 PV 删除之后仍然存在。
2、根据情况,手动清除所关联的存储资产上的数据。
3、手动删除所关联的存储资产。
如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。
删除(Delete 自动回收):
对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施中移除所关联的存储资产。动态制备的卷会继承其 StorageClass 中设置的回收策略,该策略默认为 Delete。
回收(Recycle):已废弃。
1.5、持久卷访问模式(accessModes)
ReadWriteOnce
卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式仍然可以在同一节点上运行的多个 Pod 访问(读取或写入)该卷。
ReadOnlyMany
卷可以被多个节点以只读方式挂载。
ReadWriteMany
卷可以被多个节点以读写方式挂载。
ReadWriteOncePod
卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOncePod
1.6、持久卷阶段
每个持久卷会处于以下阶段(Phase)之一:
- Available
卷是一个空闲资源,尚未绑定到任何声明(PVC)。
- Bound
该卷已经绑定到某声明(PVC)。
- Released
所绑定的声明已被删除,但是关联存储资源尚未被集群回收。
- Failed
卷的自动回收操作失败。
1.7、PV、PVC 使用流程
研发人员无须向他们的 pod 中添加特定技术的卷,而是由集群管理员设置底层存储,然后通过 Kubernetes API 服务器创建持久卷并注册。
在创建持久卷时,管理员可以指定其大小 和所支持 的访问模式。 当集群用户需要在其 pod 中使用持久化存储时,他们首先创建持久卷声明(PersistentVolumeClaim,简称PVC)清单,指定所需要的最低容量要求 和访问模式,然后用户将持久卷声明清单提交给 Kubernetes API服务器,Kubernetes 将找到可匹配的持久卷并将其绑定到持久卷声明。
持久卷声明可以当作 pod 中的⼀个卷来使用,其他用户不能使用相同的持久卷,除非先通过删除持久卷声明绑定来释放。
二、基于 NFS 的持久化卷(PV)
如何搭建 NFS,以及如何在 K8S 中使用 NFS 可以看我这篇博文。
2.1、静态供应
在实验之前,先在 k8s31master
- 创建共享目录:
bash
[root@k8s31master ~]# mkdir -p /data/nfs/rwo
[root@k8s31master ~]# mkdir -p /data/nfs/rox
[root@k8s31master ~]# mkdir -p /data/nfs/rwx
- 编辑配置文件 /etc/exports:
bash
/data/nfs/rwo 192.168.40.0/24(rw,sync,no_root_squash)
/data/nfs/rox 192.168.40.0/24(rw,sync,no_root_squash)
/data/nfs/rwx 192.168.40.0/24(rw,sync,no_root_squash)
- 重新加载配置:
bash
exportfs -arv
2.1.1、RWO(ReadWriteOnce)
1)创建持久卷
javascript
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-rwo-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce # 单节点读写访问
persistentVolumeReclaimPolicy: Retain # 回收策略:保留数据
nfs:
server: 192.168.40.10 # NFS 服务器地址
path: "/data/nfs/rwo" # NFS 共享目录
- capacity.storage:定义持久卷的大小。
- accessModes:可以被单个节点挂载为读写模式。
- persistentVolumeReclaimPolicy=ReadWriteOnce:当声明(PVC)被释放后,PersistentVolume 将会被保留(不清理和删除)。默认选项。
- nfs:配置 NFS 服务器地址和共享目录。
在创建持久卷时,管理员需要告诉 Kubernetes 其对应的容量需求以及它是否可以由单个节点或多个节点同时读取或写入。管理员还需要告诉 Kubermetes 如何处理 PersistentVolume (当持久卷声明的绑定被删除时)。最后,无疑也很重要的事情是,管理员需要指定持久卷支持的实际存储类型、位置和其他属性。
2)查看持久卷
bash
kubectl apply -f pv.yaml
# pv 是 PersistentVolume 的简写
kubectl get pv
STATUS=Available:持久卷为可用。
CLAIN=空:因为还没创建持久卷声明。
注意:持久卷不属于任何命名空间,它跟节点⼀样是集群层面的资源。
3)创建持久卷声明
javascript
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-rwo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi # 请求的存储容量需小于等于 PV
metadata.name=nfs-rwo-pvc:声明的名称--稍后将声明当作 pod 的卷使用时需要用到。
accessModes=ReadWriteOnce:允许单个节点读取和写入操作。
storage=1Gi:申请 1GiB 的存储空间。
当创建好声明,Kubernetes 就会找到适当的持久卷并将其绑定到声明,持久卷的容量 必须足够大以满足声明的需求,并且卷的访问模式必须包含声明中指定的访问模式。在该示例中,声明请求1 GiB的存储空间和 ReadWriteOnce 访问模式。之前创建的持久卷符合刚刚声明中的这两个条件,所以它被绑定到对应的声明中。
4)查看持久卷声明
bash
kubectl apply -f pvc.yaml
# pvc 是 PersistentVolumeClaim 简称
kubectl get pvc
STATUS=Bound:PVC 与 PV 的状态都是 Bound 表示 持久卷 nfs-rwo-pv 已绑定到 声明 default/nfs-rwo-pvc 上。这个 default 部分是声明所在的命名空间(在默认命名空间中创建的声明),我们之前有提到过持久卷 PV 是集群范围的,因此不能在特定的命名空间中创建,但是持久卷声明 PVC 又只能在特定的命名空间创建,所以持久卷和持久卷声明只能被同一命名空间内的 pod 创建使用。
5)在 Pod 中使用持久卷声明
持久卷现在已经可用了,除非先释放掉卷,否则没有人可以申明相同的卷。要在pod中使用持久卷,需要在pod的卷中引用持久卷声明的名称。
javascript
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-rwo-deploy
spec:
replicas: 2
selector:
matchLabels:
app: nfs-rwo-pod
template:
metadata:
labels:
app: nfs-rwo-pod
spec:
volumes:
- name: nfs-rwo-volumes
persistentVolumeClaim:
claimName: nfs-rwo-pvc # 引用之前创建的 PVC
containers:
- name: nginx
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-rwo-volumes # 引用上面卷名称
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
protocol: TCP
创建了2个副本,都挂载相同的 PVC。所以它们是共享存储。
6)验证 RWO
查看 pod,发现它们分别被调度到 node1、node2:
bash
kubectl get pod -owide
分别进入两个 pod /usr/share/nginx/html 目录创建 a.txt、b.txt:
bash
kubectl exec -it nfs-rwo-deploy-84ccc4c5f5-q7vwf -- /bin/sh
# cd /usr/share/nginx/html
# touch a.txt
kubectl exec -it nfs-rwo-deploy-84ccc4c5f5-rxhct -- /bin/sh
# cd /usr/share/nginx/html
# touch b.txt
可以看到:不管是 node1 节点上的 pod-84ccc4c5f5-q7vwf 还是 node2 节点上的 pod-84ccc4c5f5-rxhct 都能访问本地挂载的目录 /usr/share/nginx/html,且都能执行写操作,它们创建的文件 a.txt、b.txt 也都能被同步到 NFS 服务的共享目录下。
NFS 存储环境下,K8S PV 的 RWO 失效了,它并没有实现它所声明的 卷可以被一个节点以读写方式挂载。
具体原因,大家可以参考下面这几篇文章:
Why ReadWriteOnce is working on different nodes?
总的来说,原因就是:
- accessModes 是 Kubernetes 对存储系统的请求/声明,而非强制约束
- 某些存储系统(如 NFS)天然支持多节点读写,因此会忽略 RWO 限制
- Kubernetes 的准入控制器不会因为 accessMode 拒绝 NFS 的多节点挂载
7)NFS 环境下 RWO 实现
可以编辑 /etc/exports,限定 NFS 共享目录只能由一个节点 IP 读写,这样在创建 pod 的时候,别的节点就会报没有权限访问共享目录从而实现严格 RWO,但其实这样也违背了 NFS 这种共享文件系统的设计初衷。
bash
/data/nfs/rwo 192.168.40.20(rw,sync,no_root_squash)
总之:
accessModes 是 K8S 用来标记底层存储实现能提供什么能力。它非强制性约束。
8)数据持久性验证
删除上面的部署:
bash
kubectl delete -f deploy.yaml
此时的 PV 和 PVC 还是属于绑定状态:
重新运行部署:
bash
kubectl apply -f deploy.yaml
查看数据还在:
这也说明了 PV 是一种持久化的数据存储方式。
9)Retain 验证
删除上面的部署:
bash
kubectl delete -f deploy.yaml
删除 PVC:
bash
kubectl delete -f pvc.yaml
此时的 PV 处于 Released 状态:
如果此时再次创建持久卷声明会怎样?它是否会被绑定到持久卷?
这个持久卷声明的状态显示为 Pending。之前创建声明的时候,它立即绑定到了持久卷,那么为什么现在不绑定呢?
原因在于之前已经使用过这个卷,所以它可能包含前一个声明人的数据,如果集群管理员还没来得及清理,那么不应该将这个卷绑定到全新的声明中。
10)手动回收持久卷
删除 PVC:
bash
kubectl delete -f pvc.yaml
删除 PV:
bash
kubectl delete -f pv.yaml
删除 NFS 共享路径数据:
bash
[root@k8s31master rwo]# rm -rf a.txt b.txt
11)Delete 验证
pv.yaml:
javascript
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-rwo-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce # 单节点读写访问
persistentVolumeReclaimPolicy: Delete # 回收策略:删除数据
nfs:
server: 192.168.40.10 # NFS 服务器地址
path: "/data/nfs/rwo" # NFS 共享目录
pvc.yaml:
javascript
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-rwo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi # 请求的存储容量需小于等于 PV
deploy.yaml:
javascript
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-rwo-deploy
spec:
replicas: 2
selector:
matchLabels:
app: nfs-rwo-pod
template:
metadata:
labels:
app: nfs-rwo-pod
spec:
volumes:
- name: nfs-rwo-volumes
persistentVolumeClaim:
claimName: nfs-rwo-pvc # 引用之前创建的 PVC
containers:
- name: nginx
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-rwo-volumes
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
protocol: TCP
bash
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deploy.yaml
- 进入 Pod 创建测试文件:
- 删除 deploy、pvc:
PV 状态为 Failed。
- 查看 pv 描述信息:
bash
kubectl describe pv persistentvolume/nfs-rwo-pv
Warning VolumeFailedDelete 3m31s persistentvolume-controller error getting deleter volume plugin for volume "nfs-rwo-pv": no deletable volume plugin matched
Delete 是一种自动回收策略,依赖卷插件的支持。很显然,当前 NFS 插件并不支持 Delete 策略。
- 手动回收
bash
kubectl delete -f pv.yaml
[root@k8s31master rwo]# rm -rf dl.txt
2.1.2、ROX(ReadOnlyMany)
1)部署
javascript
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-rox-pv
spec:
capacity:
storage: 2Gi
accessModes:
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/nfs/rox
server: 192.168.40.10
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-rox-pvc
spec:
resources:
requests:
storage: 1Gi
accessModes:
- ReadOnlyMany
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-rox-pvc
spec:
resources:
requests:
storage: 1Gi
accessModes:
- ReadOnlyMany
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-rox-deploy
spec:
replicas: 2
selector:
matchLabels:
app: nfs-rox-pod
template:
metadata:
labels:
app: nfs-rox-pod
spec:
volumes:
- name: nfs-rox-volumes
persistentVolumeClaim:
claimName: nfs-rox-pvc
containers:
- name: nginx
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-rox-volumes
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
protocol: TCP
2)ROX 验证
在 NFS 存储下,ROX 也没有实现被多个节点只读挂载。
3)NFS 环境下 ROX 实现
修改 deploy,将 persistentVolumeClaim 设置为只读:
javascript
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-rox-deploy
spec:
replicas: 2
selector:
matchLabels:
app: nfs-rox-pod
template:
metadata:
labels:
app: nfs-rox-pod
spec:
volumes:
- name: nfs-rox-volumes
persistentVolumeClaim:
claimName: nfs-rox-pvc
readOnly: true # 设置为只读
containers:
- name: nginx
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-rox-volumes
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
protocol: TCP
声明式更新:
bash
kubectl apply -f deploy.yaml
进入 pod 尝试创建文件:
会发现只读限制。
2.1.3、RWX(ReadWriteMany)
NFS 存储下默认都是 RWX,这里就不重复举例了。
2.2、动态供应
1)什么是动态供应
上面介绍的静态供应模式都需要集群管理员手动创建 PV 资源,预先配置存储系统(如 NFS、iSCSI、云磁盘等)并将其注册到 Kubernetes 中。Kubernetes 提供了一种通过 StorageClass 自动创建 PV 的机制,无需管理员预先创建。当用户创建 PVC 时,Kubernetes 根据 StorageClass 的配置动态创建 PV。
StorageClass 也是 Kubernetes 中的一种资源:
bash
# sc StorageClass 简称
kubectl explain sc
- provisioner:指定用于创建 PV 的存储供给器(也叫供应商),是 StorageClass 必需的参数。常见的供给器有 kubernetes.io/aws-ebs(用于 Amazon Elastic Block Store)、kubernetes.io/gce-pd(用于 Google Compute Engine Persistent Disk)、local.csi.io(用于本地存储的 Container Storage Interface 驱动)等。不同的供给器对应不同的存储后端实现。供应商有内部、外部之分。下图打勾的是 K8S 提供了内部供应商。
- parameters:用于传递给供给器的参数,以配置存储的具体特性。例如,对于 kubernetes.io/aws-ebs 供给器,可以设置 type 参数来指定 EBS 卷的类型(如 gp2、io1 等);对于 local.csi.io 供给器,可以设置 storage.csi.io/node-topology 参数来指定存储在节点上的位置等。
- reclaimPolicy:定义当 PVC 被删除时,与之绑定的 PV 的回收策略。可选值有 Delete(默认)和 Retain。Delete 表示 PV 会被自动删除;Retain 表示 PV 会被保留,需要管理员手动处理。
- mountOptions:指定挂载 PV 时使用的挂载选项,例如 nfs 存储的挂载选项 nolock,tcp,rw 等。
- volumeBindingMode:控制 PVC 与 PV 的绑定模式,有 Immediate(默认)和 WaitForFirstConsumer 两种。Immediate 模式下,PVC 创建后会立即尝试绑定合适的 PV;WaitForFirstConsumer 模式下,PVC 创建后不会立即绑定 PV,而是等到有 Pod 使用该 PVC 时才进行绑定,这种模式适用于一些需要更灵活调度的场景,比如基于节点亲和性等条件来选择合适的 PV。
2)nfs-subdir-external-provisioner 介绍
我们今天使用 NFS 作为底层存储,各种不同存储的外部供应商如下:
找到 nfs-client 已过期,现已迁移到
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
- 英文介绍:
Kubernetes NFS Subdir External Provisioner
NFS subdir external provisioner is an automatic provisioner that use your existing and already configured NFS server to support dynamic provisioning of Kubernetes Persistent Volumes via Persistent Volume Claims. Persistent volumes are provisioned as ${namespace}-${pvcName}-${pvName}
.
Note: This repository is migrated from https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client. As part of the migration:
- The container image repository and name has changed to
registry.k8s.io/sig-storage
andnfs-subdir-external-provisioner
respectively. - To maintain backward compatibility with earlier deployment files, the naming of NFS Client Provisioner is retained as
nfs-client-provisioner
in the deployment YAMLs.
- 中文翻译如下:
FS Subdir External Provisioner 是一个自动配置程序,它利用您现有且已配置的 NFS 服务器 ,通过 Persistent Volume Claims 来支持 Kubernetes 持久卷的动态配置。持久卷被配置为 $ {namespace}- $ {pvcName}- $ {pvName}
的形式。请注意,该存储库已从 https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client 迁移而来。作为迁移的一部分:
- 容器镜像的存储库和名称已更改为
registry.k8s.io/sig-storage
和nfs-subdir-external-provisioner
。 - 为了保持与早期部署文件的向后兼容性,NFS 客户端配置程序在部署 YAML 中的命名仍保留为
nfs-client-provisioner
。
3)镜像准备
docker hub 上找一个国内好访问的镜像站拉取镜像
bash
docker pull kubesphere/nfs-subdir-external-provisioner:v4.0.2
导出镜像文件:
bash
docker save -o nfs-subdir-external-provisioner-v4.0.2.tar.gz kubesphere/nfs-subdir-external-provisioner:v4.0.2
上传到 node1、node2 导入镜像:
bash
scp nfs-subdir-external-provisioner-v4.0.2.tar.gz 192.168.40.20:/root/
scp nfs-subdir-external-provisioner-v4.0.2.tar.gz 192.168.40.30:/root/
# node1 执行
[root@k8s31node1 ~]# ctr -n=k8s.io images import nfs-subdir-external-provisioner-v4.0.2.tar.gz
[root@k8s31node1 ~]# ctr -n=k8s.io images ls|grep nfs
# node2 执行
[root@k8s31node2 ~]# ctr -n=k8s.io images import nfs-subdir-external-provisioner-v4.0.2.tar.gz
[root@k8s31node2 ~]# ctr -n=k8s.io images ls|grep nfs
4)创建共享文件夹
bash
# 在 NFS 服务器上创建共享文件夹
mkdir -p /data/nfs/dynamic
vim /etc/exports
# 添加一条
/data/nfs/dynamic 192.168.40.0/24(rw,sync,no_root_squash)
# 导出配置
exportfs -arv
5)创建 ServiceAccount 账号
nfs-client-provisioner 需要与 Kubernetes API 交互,所以需要创建 ServiceAccount 账号、集群角色、角色账号绑定。
javascript
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
6)创建集群角色
javascript
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
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"]
注意加上 endpoints 不然 nfs-client-provisioner 会报错:
7)创建角色账号绑定
javascript
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
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
8)部署 nfs-client-provisioner
javascript
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: kubesphere/nfs-subdir-external-provisioner:v4.0.2 # 修改为本地镜像
imagePullPolicy: IfNotPresent # 修改镜像拉取策略
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.40.10 # 配置你的NFS服务器IP
- name: NFS_PATH
value: /data/nfs/dynamic # 配置你的NFS服务器共享目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.40.10 # 配置你的NFS服务器IP
path: /data/nfs/dynamic # 配置你的NFS服务器共享目录
9)创建 StorageClass
javascript
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "false"
注意:
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
provisioner 的名字应该跟部署 nfs-client-provisioner 的 env PROVISIONER_NAME 的 value值保持一致。
查看 StorageClass:
bash
# sc StorageClass 简写
kubectl get sc managed-nfs-storage
10)创建 PVC,通过 StorageClass 动态生成 PV
javascript
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
查看 PVC、PV:
可以看到 PVC 已经绑定了 PV,而这个 PV,是由 StorageClass 调用 nfs-client-provisioner 自动生成的。
11)创建 Pod,挂载 PVC
javascript
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox:1.28 # 使用本地镜像
imagePullPolicy: IfNotPresent # 修改镜像拉取策略
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim
这个 pod 的作用,是在容器启动的时候,往容器 /mnt 目录下创建一个 SUCCESS 文件。
该 pod 只运行一次。
由于 容器 /mnt 已经挂载了 PVC,而 PVC 由挂载到 NFS 服务器共享目录 /data/nfs/dynamic,所以我们在 NFS 服务器共享目录也能看到 SUCCESS 文件。
PV 的名字为
${namespace}-${pvcName}-${pvName}
12)Delete 自动回收验证
动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。
我们删除 PVC,看看 nfs-client-provisioner 有没有帮我们自动删除 PV,并清理存储资源。
bash
# 删除 pod
kubectl delete -f test-pod.yaml
# 删除 PVC
kubectl delete -f test-claim.yaml
# 查看 PVC、PV
kubectl get pvc, pv
空
查看 NFS 服务器共享目录,会发现资源已经帮我们清理了。