当前位置: 首页 > news >正文

K8S PV 与 PVC 快速开始、入门实战

假设有如下三个节点的 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

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

  • 创建共享目录:
[root@k8s31master ~]# mkdir -p /data/nfs/rwo
[root@k8s31master ~]# mkdir -p /data/nfs/rox
[root@k8s31master ~]# mkdir -p /data/nfs/rwx
  • 编辑配置文件 /etc/exports:
/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)
  • 重新加载配置:
exportfs -arv

2.1.1、RWO(ReadWriteOnce)

1)创建持久卷
apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-rwo-pv
spec:capacity:storage: 1GiaccessModes:- 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)查看持久卷
kubectl apply -f pv.yaml
# pv 是 PersistentVolume 的简写
kubectl get pv

STATUS=Available:持久卷为可用。

CLAIN=空:因为还没创建持久卷声明。

 注意:

持久卷不属于任何命名空间,它跟节点⼀样是集群层面的资源。

3)创建持久卷声明
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nfs-rwo-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1Gi  # 请求的存储容量需小于等于 PV

metadata.name=nfs-rwo-pvc:声明的名称--稍后将声明当作 pod 的卷使用时需要用到。

accessModes=ReadWriteOnce:允许单个节点读取和写入操作。

storage=1Gi:申请 1GiB 的存储空间。

当创建好声明,Kubernetes 就会找到适当的持久卷并将其绑定到声明,持久卷的容量必须足够大以满足声明的需求,并且卷的访问模式必须包含声明中指定的访问模式。在该示例中,声明请求1 GiB的存储空间和 ReadWriteOnce 访问模式。之前创建的持久卷符合刚刚声明中的这两个条件,所以它被绑定到对应的声明中。

 4)查看持久卷声明
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的卷中引用持久卷声明的名称。

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-rwo-deploy
spec:replicas: 2selector:matchLabels:app: nfs-rwo-podtemplate:metadata:labels:app: nfs-rwo-podspec:volumes:- name: nfs-rwo-volumespersistentVolumeClaim:claimName: nfs-rwo-pvc # 引用之前创建的 PVCcontainers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: nfs-rwo-volumes # 引用上面卷名称mountPath: /usr/share/nginx/htmlports:- containerPort: 80protocol: TCP

创建了2个副本,都挂载相同的 PVC。所以它们是共享存储。

6)验证 RWO

查看 pod,发现它们分别被调度到 node1、node2:

kubectl get pod -owide

分别进入两个 pod /usr/share/nginx/html 目录创建 a.txt、b.txt:

kubectl exec -it nfs-rwo-deploy-84ccc4c5f5-q7vwf -- /bin/sh
# cd /usr/share/nginx/html
# touch a.txtkubectl 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 失效了,它并没有实现它所声明的 卷可以被一个节点以读写方式挂载。

具体原因,大家可以参考下面这几篇文章:

ReadWriteOnce 的访问模式不如预期工作的问题

Why ReadWriteOnce is working on different nodes?

Kubernetes 存储卷 PV 访问模式解析

总的来说,原因就是:

  • accessModes 是 Kubernetes 对存储系统的请求/声明,而非强制约束
  • 某些存储系统(如 NFS)天然支持多节点读写,因此会忽略 RWO 限制
  • Kubernetes 的准入控制器不会因为 accessMode 拒绝 NFS 的多节点挂载
 7)NFS 环境下 RWO 实现

可以编辑 /etc/exports,限定 NFS 共享目录只能由一个节点 IP 读写,这样在创建 pod 的时候,别的节点就会报没有权限访问共享目录从而实现严格 RWO,但其实这样也违背了 NFS 这种共享文件系统的设计初衷。

/data/nfs/rwo 192.168.40.20(rw,sync,no_root_squash)

总之:

accessModes 是 K8S 用来标记底层存储实现能提供什么能力。它非强制性约束。

8)数据持久性验证

删除上面的部署:

kubectl delete -f deploy.yaml

此时的 PV 和 PVC 还是属于绑定状态:

 重新运行部署:

kubectl apply -f deploy.yaml

 查看数据还在:

这也说明了 PV 是一种持久化的数据存储方式。

 9)Retain 验证

删除上面的部署:

kubectl delete -f deploy.yaml

删除 PVC:

kubectl delete -f pvc.yaml

此时的 PV 处于  Released 状态:

 如果此时再次创建持久卷声明会怎样?它是否会被绑定到持久卷?

这个持久卷声明的状态显示为 Pending。之前创建声明的时候,它立即绑定到了持久卷,那么为什么现在不绑定呢?

原因在于之前已经使用过这个卷,所以它可能包含前一个声明人的数据,如果集群管理员还没来得及清理,那么不应该将这个卷绑定到全新的声明中。

10)手动回收持久卷

删除 PVC:

kubectl delete -f pvc.yaml

 删除 PV:

kubectl delete -f pv.yaml 

 删除 NFS 共享路径数据:

[root@k8s31master rwo]# rm -rf a.txt b.txt
11)Delete 验证

pv.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-rwo-pv
spec:capacity:storage: 1GiaccessModes:- ReadWriteOnce  # 单节点读写访问persistentVolumeReclaimPolicy: Delete # 回收策略:删除数据nfs:server: 192.168.40.10  # NFS 服务器地址path: "/data/nfs/rwo"   # NFS 共享目录

 pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nfs-rwo-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1Gi  # 请求的存储容量需小于等于 PV

 deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-rwo-deploy
spec:replicas: 2selector:matchLabels:app: nfs-rwo-podtemplate:metadata:labels:app: nfs-rwo-podspec:volumes:- name: nfs-rwo-volumespersistentVolumeClaim:claimName: nfs-rwo-pvc # 引用之前创建的 PVCcontainers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: nfs-rwo-volumesmountPath: /usr/share/nginx/htmlports:- containerPort: 80protocol: TCP
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deploy.yaml

  •  进入 Pod 创建测试文件:

  •  删除 deploy、pvc:

PV 状态为 Failed

  •  查看 pv 描述信息:
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 策略。

  •  手动回收
kubectl delete -f pv.yaml 
[root@k8s31master rwo]# rm -rf dl.txt

2.1.2、ROX(ReadOnlyMany)

1)部署
apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-rox-pv
spec:capacity: storage: 2GiaccessModes:- ReadOnlyManypersistentVolumeReclaimPolicy: Retainnfs:path: /data/nfs/roxserver: 192.168.40.10
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: name: nfs-rox-pvc
spec:resources:requests:storage: 1GiaccessModes:- ReadOnlyMany
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: name: nfs-rox-pvc
spec:resources:requests:storage: 1GiaccessModes:- ReadOnlyMany
[root@k8s31master rox]# cat deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-rox-deploy
spec:replicas: 2selector:matchLabels:app: nfs-rox-podtemplate:metadata:labels:app: nfs-rox-podspec:volumes:- name: nfs-rox-volumespersistentVolumeClaim:claimName: nfs-rox-pvccontainers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: nfs-rox-volumesmountPath: /usr/share/nginx/htmlports:- containerPort: 80protocol: TCP
 2)ROX 验证

在 NFS 存储下,ROX 也没有实现被多个节点只读挂载。

  3)NFS 环境下 ROX 实现

修改 deploy,将 persistentVolumeClaim 设置为只读:

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-rox-deploy
spec:replicas: 2selector:matchLabels:app: nfs-rox-podtemplate:metadata:labels:app: nfs-rox-podspec:volumes:- name: nfs-rox-volumespersistentVolumeClaim:claimName: nfs-rox-pvcreadOnly: true # 设置为只读containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: nfs-rox-volumesmountPath: /usr/share/nginx/htmlports:- containerPort: 80protocol: TCP

声明式更新:

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 中的一种资源:

# 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 and nfs-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 上找一个国内好访问的镜像站拉取镜像

docker pull kubesphere/nfs-subdir-external-provisioner:v4.0.2

 导出镜像文件:

docker save -o nfs-subdir-external-provisioner-v4.0.2.tar.gz kubesphere/nfs-subdir-external-provisioner:v4.0.2

 上传到 node1、node2 导入镜像:

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)创建共享文件夹
# 在 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 账号、集群角色、角色账号绑定。

serviceaccount.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisioner
6)创建集群角色

clusterrole.yaml:

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)创建角色账号绑定

clusterrolebinding.yaml:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: run-nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: default
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: rbac.authorization.k8s.io
8)部署 nfs-client-provisioner

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisionernamespace: default
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisionerimage: kubesphere/nfs-subdir-external-provisioner:v4.0.2 # 修改为本地镜像imagePullPolicy: IfNotPresent # 修改镜像拉取策略volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner- name: NFS_SERVERvalue: 192.168.40.10 # 配置你的NFS服务器IP- name: NFS_PATHvalue: /data/nfs/dynamic # 配置你的NFS服务器共享目录volumes:- name: nfs-client-rootnfs:server: 192.168.40.10 # 配置你的NFS服务器IPpath: /data/nfs/dynamic # 配置你的NFS服务器共享目录
 9)创建 StorageClass

class.yaml:

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:

# sc StorageClass 简写
kubectl get sc managed-nfs-storage

10)创建 PVC,通过 StorageClass 动态生成 PV

test-claim.yaml:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: test-claim
spec:storageClassName: managed-nfs-storageaccessModes:- ReadWriteManyresources:requests:storage: 1Mi

查看 PVC、PV:

可以看到 PVC 已经绑定了 PV,而这个 PV,是由 StorageClass 调用 nfs-client-provisioner 自动生成的。

11)创建 Pod,挂载 PVC

test-pod.yaml:

kind: Pod
apiVersion: v1
metadata:name: test-pod
spec:containers:- name: test-podimage: busybox:1.28 # 使用本地镜像imagePullPolicy: IfNotPresent # 修改镜像拉取策略command:- "/bin/sh"args:- "-c"- "touch /mnt/SUCCESS && exit 0 || exit 1"volumeMounts:- name: nfs-pvcmountPath: "/mnt"restartPolicy: "Never"volumes:- name: nfs-pvcpersistentVolumeClaim: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,并清理存储资源。

# 删除 pod
kubectl delete -f test-pod.yaml
# 删除 PVC
kubectl delete -f test-claim.yaml
# 查看 PVC、PV
kubectl get pvc, pv
空

 查看 NFS 服务器共享目录,会发现资源已经帮我们清理了。

三、基于本地存储的持久化卷

相关文章:

K8S PV 与 PVC 快速开始、入门实战

假设有如下三个节点的 K8S 集群: ​ k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、什么是 PV 与 PVC 1.1、什么是 PV(PersistentVolume 持久卷) PV 是集群中由管理员配置的一段网络存储&#xf…...

5月6(信息差)

一、经济与贸易 中美关税谈判进展 美方近期多次主动向中方传递谈判信号,中方回应称“谈的大门始终敞开”,但强调美方需先取消单边加征关税等错误做法78。 美国第一季度GDP环比下降0.3%,为2022年第二季度以来新低,经济压力或推动其加快谈判进程78。 全球贸易政策变动 特朗普…...

购物|电商购物小程序|基于微信小程序的购物系统设计与实现(源码+数据库+文档)

电商购物小程序 目录 基于微信小程序的购物系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户前台功能实现 2、管理员后台功能实现 四、数据库设计 1、实体ER图 2、具体的表设计如下所示: 五、核心代码 六、论文参考 七、最新计算机毕设选题…...

基于k8s的Jenkins CI/CD平台部署实践(三):集成ArgoCD实现持续部署

基于k8s的Jenkins CI/CD平台部署实践(三):集成ArgoCD实现持续部署 文章目录 基于k8s的Jenkins CI/CD平台部署实践(三):集成ArgoCD实现持续部署一、Argocd简介二、安装Helm三、Helm安装ArgoCD实战1. 添加Arg…...

Python实例题:高德API+Python解决租房问题

目录 Python实例题 题目 python-amap-rental结合高德 API 和 Python 解决租房问题的脚本 代码解释 get_geocode 函数: search_rentals 函数: 主程序: 运行思路 注意事项 Python实例题 题目 高德APIPython解决租房问题 python-ama…...

WIN10 系统增加MYSQL环境变量示例

说明: 由于安装MYSQL需要添加到环境变量后才能启动运行,这里记录一下添加mysql环境变量的过程。 1、进入我的电脑-属性 2、找到高级设置 3、找到环境变量 4、找到PATH 5、双击进入后通过新建添加对应MYSQL的安装路径(.exe所在的bin路径&…...

NetApp SAS 连接线:铜缆与光缆的全面介绍

写在前面 NetApp 的磁盘扩展柜,主要是12GB的shelf,如DS212C,224C,460C等,这些shelf中间的互联或者控制器到shelf的连接都是通过12Gb的SAS线来连接的,以实现高速数据传输。SAS 连接线是这一过程中的核心组件。但是NetApp的SAS连接…...

CSS中的@import指令

一、什么是import指令&#xff1f; import 是CSS提供的一种引入外部样式表的方式&#xff0c;允许开发者在CSS文件中引入其他CSS文件&#xff0c;或者在HTML的<style>标签中引入外部样式。与常见的<link>标签相比&#xff0c;import 提供了一种更“CSS原生”的样式…...

tinyrenderer笔记(上)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码参考笔记&#xff1a;从零构建光栅器&#xff0c;tinyrenderer笔记&#xff08;上&#xff09; - 知乎 第 1 课&#xff1a;Bresenham 画线算法 Bresenham 画线算法&#xff1a;Bresenham 直线算法 - 知乎 第一…...

VS2022 Qt配置Qxlsx

目录 1、下载QXlsx&#xff0c;并解压文件夹 ​编辑2、打开VS2022配置QXlsx 3、VS配置Qxslx库 方法一&#xff1a;常规方法 方法二&#xff1a;直接使用源码 方法三&#xff1a;将QXlsx添加到Qt安装目录&#xff08;暂时尝试未成功&#xff09; 1、下载QXlsx&#xff0c;…...

C++ 渗透 数据结构中的二叉搜索树

欢迎来到干货小仓库 "沙漠尽头必是绿洲。" --面对技术难题时&#xff0c;坚持终会看到希望。 1.二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一颗空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; a、若它的左子树不为空&#xff0c;则…...

JavaScript学习教程,从入门到精通,jQuery 单击页面显示自定义动画、元素删除操作、随机抽奖、随机选图并放大语法知识点(37)

jQuery 单击页面显示自定义动画、元素删除操作、随机抽奖、随机选图并放大语法知识点 1. jQuery 基础语法 1.1 引入 jQuery 在使用 jQuery 之前&#xff0c;需要先引入 jQuery 库。可以通过 CDN 引入&#xff0c;也可以下载到本地使用。 <!-- 通过 CDN 引入 jQuery -->…...

5.6 react组件化开发基础

react 组件开发基础 组件分类与组件使用 组件传参 父传子 【函数数据传值 实参 形参对应关系】 子传父 插槽 透传 useContext 上下文&#xff08;作用域&#xff09; 跨层级调用方法 通过子组件的实例对象useRef 直接调用子组件的方法 和数据 状态管理&#xff08;非常多…...

react-14defaultValue(仅在首次渲染时生效)和value(受 React 状态控制)

在 React 中&#xff0c;defaultChecked/checked 和 defaultValue/value 是用于处理表单元素初始值和受控值的属性对。区别在于表单元素是否受 React 组件状态控制。 1. defaultValue 作用&#xff1a;设置表单元素的初始值&#xff08;仅在首次渲染时生效&#xff09;。特点…...

HarmonyOS 5.0 低时延音视频开发​​

大家好&#xff0c;我是 V 哥。 在HarmonyOS 5.0的开发中&#xff0c;支持低时延音视频开发&#xff0c;为了确保语法正确&#xff0c; V 哥以下代码符合HarmonyOS NEXT API 14的规范。为了方便初学者更好入门&#xff0c;V 哥伙同2位小伙伴花了1年时间&#xff0c;搞了三本鸿蒙…...

视频智能分析网关助力小区/住宅/街道智慧社区管理服务全面升级

一、引言​ 随着信息技术的飞速发展&#xff0c;智慧社区建设已成为提升居民生活质量、优化社区管理的重要趋势。智能分析网关作为智慧社区的核心技术支撑之一&#xff0c;凭借其强大的数据处理和智能分析能力&#xff0c;在社区的安防监控、人员车辆管理、环境卫生检测等多个…...

ShardingJdbc-水平分表

ShardingJdbc-水平分表 水平分表 表结构相同表数据记录不同多张分表记录数和才是总的记录数通常根据主键ID进行分表&#xff0c;这里采用奇偶策略 案例 建立库 sharding-demo建立表 user_1、user_2 表结构相同id 为主键&#xff0c;bigint 类型分表规则 id 为偶数的记录存储…...

AI应用爆发或将进入临界点

在科技发展的长河中,总有一些时刻如惊雷般震撼世界,预示着新时代的到来。如今,AI应用似乎正站在这样一个关键节点上,一场前所未有的变革风暴或许即将席卷而来,AI应用的爆发或将进入临界点。 当我们回顾科技发展的历程,不难发现每一次重大的技术突破都曾引发社会的巨大变…...

Javase 基础加强 —— 05 Map集合

本系列为笔者学习Javase的课堂笔记&#xff0c;视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程&#xff0c;java零基础入门到大牛一套通关》&#xff0c;章节分布参考视频教程&#xff0c;为同样学习Javase系列课程的同学们提供参考。 01 概述 Ma…...

LINUX——例行性工作

单一执行的例行性工作 仅处理一次的工作&#xff0c;可用于在特定时间执行工作 at命令的工作过程&#xff1a; at命令使用时的权限控制&#xff1a;通过两个文件/etc/at.allow和/etc/at.deny来控制哪些用户可以使用at命令。如果这两个文件都不存在&#xff0c;那么只有root用户…...

天线测试报告解读学习

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、无源测试和有源测试二、无源测试报告1.驻波2.回损3.史密斯圆图4.效率5.增益6.天线方向图7.天线隔离度8.无源测试总结 三、有源测试报告1.TRP与TIS2.测试指标…...

Kotlin Android开发过渡指南

为了帮助Java开发者顺利过渡到Kotlin进行Android开发,以下是一本指南的详细大纲设计,涵盖关键知识点、迁移策略和实践案例: 《Kotlin for Android开发:从Java到Kotlin的平滑过渡指南》大纲 第一部分:为什么选择Kotlin? Kotlin的优势 简洁性、安全性、与Java的互操作性Go…...

Hadoop架构再探讨

文章目录 1.Hadoop的优化与发展1.1Hadoop的局限与不足1.2针对Hadoop的改进与提升 2.HDFS2.0新特性2.1HDFS HA1.HDFS 1.0 组件及功能回顾​2. HDFS 1.0 的单点故障问题​3. HDFS HA&#xff08;高可用&#xff09;解决方案​ 2.2HDFS Federation1.HDFS1.0中存在的问题2.HDFS Fed…...

ffmpeg录音测试

ffmpeg ffmpeg 是一个强大的多媒体处理工具&#xff0c;可以用于录音、音频处理、视频录制等多种功能。以下是使用 ffmpeg 进行录音的详细指令和参数说明。 基本录音指令 以下是一个简单的 ffmpeg 录音命令&#xff0c;将音频录制为 WAV 格式文件&#xff1a; ffmpeg -f …...

Kotlin-解构声明

我们在使用对象时可能需要访问它们内部的一些属性: class Student(var name: String, var age: Int) fun main() {val student Student("小明", 18)println(student.name)println(student.age) }这样看起来不太优雅,我们可以像下面这样编写: class Student(var na…...

【MCP Node.js SDK 全栈进阶指南】专家篇(2):MCP多模型支持架构

引言 在实际应用中,单一模型往往难以满足所有业务需求,这就需要一种灵活的架构来支持多模型集成和智能调度。Model Context Protocol (MCP) 作为连接应用与AI模型的标准协议,为多模型支持提供了理想的基础架构。 本文作将深入探讨如何基于MCP构建多模型支持架构,包括多LL…...

使用阿里AI的API接口实现图片内容提取功能

参考链接地址&#xff1a;如何使用Qwen-VL模型_大模型服务平台百炼(Model Studio)-阿里云帮助中心 在windows下&#xff0c;使用python语言测试&#xff0c;版本&#xff1a;Python 3.8.9 一. 使用QVQ模型解决图片数学难题 import os import base64 import requests# base 64 …...

mapbox基础,加载Fog云雾效果

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️fog 云雾 api1.3.1 ☘️配置项二、🍀…...

数据可视化与分析

数据可视化的目的是为了数据分析&#xff0c;而非仅仅是数据的图形化展示。 项目介绍 项目案例为电商双11美妆数据分析&#xff0c;分析品牌销售量、性价比等。 数据集包括更新日期、ID、title、品牌名、克数容量、价格、销售数量、评论数量、店名等信息。 1、数据初步了解…...

git的push.default配置详解

Git的push.default配置用于定义执行git push时未指定远程和分支的默认行为。以下是各选项的详解及使用场景&#xff1a; 1. simple&#xff08;默认值&#xff0c;Git ≥2.0&#xff09; 行为&#xff1a;仅推送当前分支到与其关联的上游分支&#xff08;即remote-tracked分支…...

高频电流探头:通信测试中的隐形守护者

在5G基站调试现场&#xff0c;工程师李工正手持一个火柴盒大小的装置贴近电路板&#xff0c;示波器屏幕上瞬间显示出精确的电流波形——这个看似普通的场景&#xff0c;背后折射出高频电流探头在通信测试中的关键作用。随着通信技术迈入毫米波时代&#xff0c;这类精密测试工具…...

淘宝拍立淘 API 接口探秘:如何通过图片挖掘海量商品信息

在互联网技术飞速发展的当下&#xff0c;传统的文字搜索商品方式已无法完全满足用户日益增长的个性化需求。基于图像识别技术的以图搜物模式应运而生&#xff0c;成为电商行业的新宠。淘宝拍立淘 API 接口作为这一技术的重要载体&#xff0c;能够帮助开发者和企业通过一张图片&…...

第二章:langchain文本向量化(embed)搭建与详细教程-本地服务方式(下)

文章目录 前言一、本地构建模型与服务端搭建代码1、完整代码2、结果示例 二、基于flask服务端构建langchain调用的向量方法代码1、完整代码2、结果示例 总结 前言 在上篇文章介绍了langchain源码embed方法与基于api key方式构建向量模型。然而&#xff0c;某些情况&#xff0c…...

飞牛云如何开启及使用ssh:小白用户上手指南-家庭云计算专家

最近很多用户希望提供飞牛云下的可道云onlyoffice的安装服务,但是很多都是萌新小白,不知道怎么启用ssh和使用ssh客户端.这里提供简单的方法,统一答复: 重要的事情说3遍:一定不要自己发挥,全部按说明的来;一定要在内网环境用内网ip访问;不要用域名和端口号,谢谢各位萌新了!!! 一…...

12.模方ModelFun工具-立面修整

摘要&#xff1a;本文主要介绍模方ModelFun修模工具——立面修整的操作方法。 点击工具栏即可找到立面修整工具&#xff0c;点击可打开并使用该工具&#xff0c;如下图&#xff1a; 图 工具菜单栏 &#xff08;1&#xff09;截面绘制&#xff1a; 快速绘制竖直矩形&#xff1…...

消失的两个数字 --- 位运算

目录 一&#xff1a;题目 二&#xff1a;算法原理 三&#xff1a;代码实现 一&#xff1a;题目 题目链接&#xff1a;面试题 17.19. 消失的两个数字 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法原理 只出现一次的数字III&#xff1a;常见位运算总结-CSDN…...

centos的根目录占了大量空间怎么办

问题 当根目录磁盘不够时&#xff0c;就必须删除无用的文件了 上面的&#xff0c;如果删除/usr 或/var是可以释放出系统盘的 定位占空间大的文件 经过命令&#xff0c;一层层查哪些是占磁盘的。 du -sh /* | sort -rh | head -n 10 最终排查&#xff0c;是有个系统日志占了20…...

Protobuf的速成之旅

注意事项:本文使用Linux下的Ubuntu C/C 一.Protobuf的安装 在安装Protobuf前需要先安装protobuf的依赖库 sudo apt-get install autoconf automake libtool curl make g unzip -y Protobuf的安装链接:https://github.com/protocolbuffers/protobuf/releases 这里根据自己的环…...

使用 Python 监控系统资源

使用 Python 监控系统资源 在开发分布式系统或性能敏感的应用时&#xff0c;实时监控系统资源&#xff08;如 CPU、内存、磁盘、网络和 GPU 使用率&#xff09;至关重要。本文介绍一个基于 Python 的 SystemMonitor 类&#xff0c;它以单例模式持续采集系统资源信息&#xff0…...

【Qt4】Qt4中实现PDF预览

方案一&#xff1a; 在Qt4中预览PDF文件&#xff0c;你可以使用多种方法&#xff0c;但最常见和简单的方法之一是使用第三方库。Qt本身并没有内置直接支持PDF预览的功能&#xff0c;但你可以通过集成如Poppler、MuPDF等库来实现这一功能。下面我将展示如何使用Poppler库在Qt4中…...

从试错到智能决策:Python与强化学习优化自动驾驶策略

从试错到智能决策:Python与强化学习优化自动驾驶策略 一、引言:自动驾驶如何更聪明? 自动驾驶技术的发展,已经从简单的感知与规则控制,迈向更加智能化的强化学习(Reinforcement Learning,RL)决策优化时代。过去,自动驾驶更多依赖 传统算法(如A、Dijkstra路径规划)…...

【免费下载】全国范围的城市用地类型数据

该数据以路网及水系切分得到的交通小区为最小地块&#xff0c;并基于卫星影像、夜间灯光数据、POI数据、手机信令数据对地块进行分类。 需要这份数据&#xff0c;请在文末查看下载方法。 一、数据介绍 该数据的用地类型可分为居住用地、商务办公用地、商业服务用地、工业用地…...

小程序问题(记录版)

1、样式不生效 在h5上生效 但是 小程序上没反应 解决办法&#xff1a;解除组件样式隔离 1、isolated 表示启用样式隔离&#xff0c;在自定义组件内外&#xff0c;使用 class 指定的样式将不会相互影响&#xff08;一般情况下的默认值&#xff09; 2、apply-shared 表示页面 wxs…...

STL之stackqueue

stack的介绍&#xff08;可以想象成栈&#xff09; 1.stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作 2.stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特点类封装作为其…...

学习海康VisionMaster之间距检测

一&#xff1a;进一步学习了 今天学习下VisionMaster中的间距检测工具&#xff1a;主要类似于卡尺工具&#xff0c;测量物体的长度或者宽度或者间距 二&#xff1a;开始学习 1&#xff1a;什么是间距检测&#xff1f; 间距测量模块用于检测两特征边缘之间的间距&#xff0c;首…...

【SpringBoot教程】SpringBoot自定义注解与AOP实现切面日志

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 文章目录 1.前言2.SpringAOP实现切面日志快速入门1.1 创建SpringBoot项目1.2 依赖配置pom.xml1.3 自定义日志注解1.4 配置 AOP 切面1.5 怎么使用呢&#xff1f;1.6 实…...

C++学习之路,从0到精通的征途:stack_queue的模拟实现及deque原理介绍

目录 一.容器适配器 1.什么是适配器 2.STL标准库中stack和queue的底层结构 3.deque的原理介绍 deque是如何借助其迭代器维护其假想连续的结构呢&#xff1f; 头插 尾插 遍历 4.deque的优缺点 二.stack的模拟实现 三.queue的模拟实现 一.容器适配器 1.什么是适配…...

文件上传漏洞篇:upload-labs靶场搭建

一、文件上传漏洞简述 文件上传漏洞是一种常见的Web安全漏洞&#xff0c;当网站或应用程序允许用户上传文件&#xff08;如图片、文档等&#xff09;时&#xff0c;若未对上传的文件进行充分的安全检查&#xff0c;攻击者可能利用此漏洞上传恶意文件&#xff08;如Web Shell、…...

TikTok 矩阵账号运营实操细节:打造爆款矩阵

在 TikTok 的流量版图里&#xff0c;打造 TikTok 矩阵账号能显著提升影响力与吸粉能力。而借助 AI 工具&#xff0c;更可为 TikTok 矩阵运营效率的提升赋能&#xff0c;让运营如虎添翼。下面就为大家详细讲讲其中的实操细节&#xff0c;并结合一些伪代码示例辅助理解。 一、矩…...

Nginx安全防护与HTTPS部署实战

一.核心安全配置 1.编译安装Nginx &#xff08;1&#xff09;安装支持软件 Nginx的配置及运行需要pcre、zlib等软件包的支持&#xff0c;因此应预先安装这些软件的开发包&#xff08;devel&#xff09;&#xff0c;以便提供相应的库和头文件&#xff0c;确保Nginx的安装顺利…...