Kubernetes 存储管理:PV/PVC与StorageClass

Kubernetes 存储管理:PV/PVC与StorageClass

_
top.gif

Recycle回收策略依靠专用回收Pod清理存储文件,该Pod运行依赖debian-base:bookworm-v1.0.4镜像。集群若无外网访问权限,会出现镜像拉取失败问题,可通过云链接离线下载镜像包,手动导入集群节点使用。

链接: https://pan.baidu.com/s/17IhaH9O5cF3GzlcjZZDDRg?pwd=dtbn

提取码: dtbn

一、PV与PVC

1. PV定义

PV,全称PersistentVolume(持久化存储卷)。属于集群级存储资源对象,一般由管理员提前创建,对接底层各类真实的存储介质,包含本地磁盘、云硬盘、NAS、NFS、iSCSI等。PV生命周期独立于Pod,不会随着Pod删除而销毁。PV如同集群内的节点,是集群基础资源,属于存储卷插件。逻辑与Pod内普通volume类似,但是拥有独立的生命周期。管理员负责配置、供给后端存储空间,而无需干预业务Pod如何使用这块存储。

可以简单将PV类比为:仓库里现成的硬盘/存储空间。

1.1 PV核心特点

  • 资源供给方:管理员提前划分存储空间、定义访问模式、存储大小、回收策略

  • 独立生命周期:Pod删除、重建,PV数据不会消失

  • 无需绑定容器/Pod,只和PVC绑定

  • 完整存储属性:容量、访问模式、存储类、回收策略

Kubernetes提供两种卷模式(volumeMode):Filesystem文件系统模式、Block块设备模式。当volumeMode配置为Filesystem时,存储卷会以挂载形式接入容器指定目录。若后端存储为空白裸块设备,Kubernetes会在首次挂载前自动为设备格式化并创建文件系统。

1.2 访问模式

访问模式

说明

ReadWriteOnce

仅支持单个节点挂载读写,同一节点可允许多个Pod共用,跨节点无法挂载

ReadOnlyMany

允许集群内多个节点同时挂载,只读权限

ReadWriteMany

允许集群内多个节点同时挂载,读写权限

ReadWriteOncePod

卷可以被单个节点以读写方式挂载。如果你想确保整个集群中只有一个Pod可以读取或写入该PVC,请使用ReadWriteOncePod访问模式。

1.3 PV回收策略

目前仅NFS和HostPath支持Recycle策略。AES EBS、GCE PD、Azure Disk和Cinder卷都支持Delete策略。

回收策略

说明

Retain

保留策略,默认策略。PV解绑PVC后数据与存储资源不会自动清除,PV状态变更为“Released”,需管理员手动清理。

Recycle

回收策略。删除PVC后,集群自动创建回收Pod,执行“rm -rf /mountdir/*”清空PV内所有文件,清空完成后PV状态变更为“Available”,此时可被新PVC复用。仅兼容NFS、HostPath等简易存储。

Delete

删除策略。删除绑定的PVC时,不仅解除PV绑定,同时连带删除底层后端存储资源,如云硬盘(Azure Disk、OpenStack Cinder)的云储存设备。

1.4 PV卷阶段(Phase)

PV卷状态

说明

Available(可用)

PV处于空闲状态,尚未与PVC建立绑定关系,可接收新PVC的绑定申请

Bound(已绑定)

PV已绑定到指定PVC

Released(已释放)

关联PVC已被删除,PV解除绑定,底层资源尚未回收

Failed(失败)

执行Recycle、Delete等自动回收流程时出现异常,回收操作执行失败

2. PVC定义

PVC,全称PersistentVolumeClaim(持久化存储申请)。PVC由开发人员或运维人员创建,相当于存储资源申请凭证,用于声明业务所需要的存储容量、读写访问权限等需求。使用者仅需要将PVC挂载到容器,无需关心底层PV是依托NFS、云盘还是何种存储技术实现。

PVC是面向用户的存储资源申请载体,类似于Pod这种资源消耗逻辑。Pod占用节点内存、CPU这类计算资源;PVC则占用PV存储资源。Pod可以请求指定CPU、内存资源;PVC可以指定存储容量大小、访问模式等存储需求。

可以简单将PVC类比为:向仓库申请领用硬盘的申请表。

2.1 核心特点

  • 资源需求方:只提需求(大小、访问模式、StorageClass),无需关心底层存储硬件

  • 调度匹配机制:k8s自动筛选符合条件的空闲PV,完成一对一绑定

  • PVC挂载到Pod,Pod读写实际走绑定后的PV

  • 命名空间隔离:PVC属于namespace,PV是集群全局资源

2.2 PVC状态

PVC状态

说明

Pending

PVC尚未绑定到PV,处于等待匹配状态;创建原因为无符合容量、访问模式的空闲PV,或配置参数不匹配

Bound

PVC已成功与对应PV完成绑定,可正常挂载至Pod投入业务使用

Lost

PVC原本绑定的PV丢失、被删除或失联,存储关联无效,无法正常读写数据

3. PV与PVC完整工作流程

  1. 管理员创建PV,提前准备好存储资源

  2. 业务人员重建PVC,写清楚需要的存储空间与条件,如10Gi、可单节点读写

  3. 集群控制器自动匹配满足条件的空闲PV,二者绑定

  4. Pod中通过“volumeClaimTemplates”或直接引用PVC名称挂载

  5. Pod删除 → PVC仍存在、PV和数据完整保留

  6. 删除PVC,根据PV的回收策略(Retain/Delete/Recycle)决定PV数据是否清理

4. 移除volumeName后PVC仍可匹配PV

当PVC配置中删除“volumeName”参数后依旧能够成功申请PV,依靠的是Kubernetes内置的PV-PVC自动绑定匹配机制。控制器会依次执行多轮筛选来锁定适配PV,完整匹配筛选流程如下:

  1. 依据PVC声明的存储容量要求、访问模式(如ReadWriteOnce、ReadOnlyMany等),剔除参数不满足的PV

  2. 如果PVC填写了“volumeName”会按照该字段精准筛选指定PV,未填写则跳过这一步

  3. 核对存储类(StorageClass)配置,过滤掉存储类不匹配的PV

  4. 最后检查标签、节点亲和性等其他约束条件,从剩余符合全部要求的空闲PV(Available 状态)中完成配对绑定

二、StorageClass

1. StorageClass定义

传统静态PV模式需要管理员提前手动创建大量PV,当集群Pod数量多、存储需求经常变化时,手动维护PV工作量极大,不符合自动化运维思想。StorageClass是Kubernetes提供的存储抽象资源,核心作用是实现PV动态供给,统一定义存储的创建规则、后端驱动、回收策略等,消除手动创建PV的繁琐操作,实现存储自动化。

StorageClass是Kubernetes中用于动态供应持久化存储卷(PV)的核心资源对象,核心作用如下:

  • 自动创建PV:接收PV存储申请后,调度对应存储供给器Provisioner自动生成匹配的PV,省去管理员手动批量预创建PV的操作;

  • 统一抽象存储规格:集中定义存储介质类型、性能指标、数据回收策略等通用配置,为业务提供标准化、差异化存储能力;

  • 分层分级管理存储:可以创建多套不同规格的StorageClass(如高性能黄金级、低成本白银级),业务可以根据自身需求灵活选用对应存储方案。

5.1 核心组件

Provisioner是对接底层存储的驱动程序,是动态创建PV的执行者。官方内置AWS EBS、GCE PD、Cinder等云厂商存储驱动。第三方扩展NFS、本地存储、Ceph、MinIO等部署对应Provisioner组件,如nfs-provisioner。PVC发起存储申请后,storageClass会调用绑定的Provisioner,自动在底层存储创建目录/磁盘,并生成对应PV。

2. StorageClass特性

特性

说明

配置示例

动态供应

按需创建PV,避免预先分配浪费资源

provisioner: kubernetes.io/aws-ebs

存储策略定制

定义存储类型(SSD/HDD)、访问模式(ReadWriteOnce)、压缩算法等

parameters: type=gp3, compression=none

资源回收策略

控制PV释放后的处理方式(Delete自动删除、Retain保留数据)

reclaimPolicy: Delete

卷扩展支持

允许在线扩展PV容量,需要后端存储支持

allowVolumeExpansion: true

绑定模式

控制PV绑定时机(Immediate立即绑定、WaitForFirstConsumer延迟至Pod调度时绑定)

volumeBindingMode: WaitForFirstConsumer

存储类优先级

定义多个StorageClass的优先级,通过“priorityClassName”实现资源调度策略

priorityClassName: high-performance

注解与标签

为StorageClass添加元数据,用于监控、自动化运维

annotations: {"storage-type": "ssd"}

允许卷快照

支持通过CSI插件创建卷快照,需要后端存储支持快照功能

volumeSnapshotClassName: csi-snapshot

3. NFS外部存储供给器

NFS外部供给器是nfs-subdir-external-provisioner是兼容Kubernetes存储规范的动态卷供给器,属于第三方运维组件,以Pod形式部署在集群内部。该组件对接外部独立NFS服务端,配合StorageClass实现PV自动化动态创建。

当接收到PVC存储申请后,它会自动在NFS共享根目录创建专属子目录并生成匹配PV,供PVC直接绑定。删除PVC时,还可以根据预设策略自动清理对应的NFS子目录。

k8s官方内置供给器仅适配AWS EBS等云厂商块存储,原生不支持NFS共享存储的动态PV供给,因此需要基于NFS实现StorageClass动态卷能力,必须额外部署该第三方组件作为中间驱动。

NFS外部供给器官方GitHub仓库地址

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

三、Recycle PV案例配置

1. NFS模拟存储设备

[Step1] 本次环境中缺少独立存储设备,利用RedHat服务器部署NFS共享来模拟仿真持久化存储,依托一个200G的虚拟磁盘承载NFS共享数据。

[Step2] 将新添加的进行分区,输入“n”新建分区,其余保持默认,最后输入“w”保存分区表。

[root@redhat ~]# fdisk /dev/nvme0n2
​
Welcome to fdisk (util-linux 2.37.4).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
​
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x2dbaa303.
​
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):
​
Using default response p.
Partition number (1-4, default 1):
First sector (2048-419430399, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-419430399, default 419430399):
​
Created a new partition 1 of type 'Linux' and of size 200 GiB.
​
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

[Step3] 格式化文件系统,格式为xfs。

[root@redhat ~]# mkfs.xfs /dev/nvme0n2p1

[Step4] 创建本地挂载目录并写入fstab文件实现开机自动挂载。

[root@redhat ~]# mkdir -p /data/nfs-share
[root@redhat ~]# echo '/dev/nvme0n2p1 /data/nfs-share xfs defaults 0 0' >> /etc/fstab
[root@redhat ~]# mount -a

[Step5] 验证:查看挂载情况。

[root@redhat ~]# df -h

[Step6] 安装NFS服务及相关组件。

[root@redhat ~]# dnf install -y nfs-utils rpcbind

[Step7] 编辑NFS配置文件,仅允许k8s-master读写。

[root@redhat ~]# vim /etc/exports
​
# 写入下列内容
/data/nfs-share 192.168.8.0/24(rw,no_root_squash)

[Step8] 启动NFS服务并开机自启。

[root@redhat ~]# systemctl enable --now nfs-server.service

[Step9] 在所有工作节点上安装NFS组件,分别验证共享。

root@k8s-worker1:~# apt-get install -y nfs-common
root@k8s-worker2:~# apt-get install -y nfs-common
root@k8s-worker1:~# showmount -e 192.168.8.10

2. 创建PV持久化存储卷

[Step1] 因为PV和PVC要编写的YAML文件比较多,这里创建一个目录用于存储文件。

root@k8s-master:~# mkdir pv-pvc
root@k8s-master:~# cd pv-pvc/

[Step2] 本地新建YAML清单,定义回收策略为Recycle的PV持久卷。

root@k8s-master:~/pv-pvc# vim pv.yml

# 写入下列内容
apiVersion: v1			# 固定配置
kind: PersistentVolume	# 资源类型
metadata:
  name: mypv
spec:
  capacity:			# 定义PV的容量
    storage: 10Gi	# 分配存储空间大小
  accessModes:		# 访问模式
    # 仅支持单个节点挂载读写,同一节点可允许多个Pod共用,跨节点无法挂载
    - ReadWriteOnce
  # 指定PV的回收策略,集群自动创建回收Pod,清空PV内所有文件
  persistentVolumeReclaimPolicy: Recycle
  nfs:	# 配置NFS服务器信息
    path: /data/nfs-share	# NFS共享目录
    server: 192.168.8.10	# NFS服务器地址

[Step3] 根据YAML清单向Kubernetes集群声明式地创建PV持久化存储卷。输出的告警信息提示Recycle回收策略已被官方弃用,不在推荐静态PV手动清空数据的回收方式。

root@k8s-master:~/pv-pvc# kubectl create -f pv.yml

[Step4] 验证:查看当前集群中的所有PV卷资源。

root@k8s-master:~/pv-pvc# kubectl get persistentvolume
root@k8s-master:~/pv-pvc# kubectl get pv

3. 创建PVC持久化存储申请

[Step1] 本地新建YAML清单,定义PVC持久化存储申请。

root@k8s-master:~/pv-pvc# vim pvc.yml

# 写入下列内容
apiVersion: v1					# 固定配置
kind: PersistentVolumeClaim		# 资源类型
metadata:
  name: mypvc
spec:
  accessModes:			# 匹配的卷访问模式,需要与PV匹配
    - ReadWriteOnce		# 匹配访问模式为ReadWriteOnce的PV
  volumeName: mypv		# 绑定指定名称的PV,匹配mypv
  resources:			# 存储资源申请配额
    requests:	# 指定PV容量,如果不存在满足该容量需求的PV,则PVC无法绑定任何PV
      storage: 5Gi		# 申请的存储空间

[Step2] 根据YAML清单向Kubernetes集群声明式地创建PVC持久化存储申请。

root@k8s-master:~/pv-pvc# kubectl create -f pvc.yml

[Step3] 验证:查看当前集群中的所有PVC资源,可以看到分配存储容量显示为10G,而非PVC申请的5G。根源在于k8s静态NFS PV内配置的容量数值仅作为调度匹配标识,而非存储空间隔离配额。绑定规则仅要求PV标注容量≥PVC申请容量,集群中仅有一块标注10G的PV,满足5G的容量匹配条件。因此,该PV直接于PVC完成绑定,容器可使用整个10G的存储空间。

root@k8s-master:~/pv-pvc# kubectl get pvc

[Step4] 验证:查看当前集群中的所有PV卷资源,可以看到STATUS从“Available”变更为“Bound”。

root@k8s-master:~/pv-pvc# kubectl get pv

4. 创建Pod关联PVC

[Step1] 本地新建YAML清单,定义Pod资源描述模板,并关联mypvc。

root@k8s-master:~/pv-pvc# vim mypvc-pod.yml

# 写入下列内容
apiVersion: v1		# 固定配置
kind: Pod			# 资源类型
metadata:
  name: mypvc-pod
spec:
  containers:
    - name: mypvc-kylin-pod
      image: kylin-server-10-sp3b23-x86_64-20230324:b23
      imagePullPolicy: IfNotPresent
      args:		# 更改容器默认启动命令,自定义运行指令
        - /bin/sh
        - -c
        - sleep 10000
      volumeMounts:				# 存储卷挂载配置
        - mountPath: "/tmp"		# 容器内挂载目录
          name: kylin-pvc		# Volume卷名称
  volumes:		# 定义存储卷
    # 自定义存储卷名称,与[containers.volumeMounts.name]对应
    - name: kylin-pvc
      persistentVolumeClaim:	# 绑定持久化存储申请PVC
        claimName: mypvc		# 绑定名为mypvc的PVC存储申请

[Step2] 根据YAML清单向Kubernetes集群声明式地创建Pod资源。

root@k8s-master:~/pv-pvc# kubectl create -f mypvc-pod.yml

[Step3] 验证:查看当前集群中的所有的Pod资源。

root@k8s-master:~/pv-pvc# kubectl get pods

[Step4] 验证:登录到mypvc-pod,查看/tmp目录的挂载来源。可以看到NFS服务器IP地址与共享目录,代表PVC已成功挂载到Pod。这里看到可用空间是200G,而非PV标注的10G、PVC申请的5G。原因是NFS无软配额隔离,PV的10G、PVC的5G仅用于k8s调度,不会限制实际读写空间。

root@k8s-master:~/pv-pvc# kubectl exec -it mypvc-pod -- /bin/bash
[root@mypvc-pod /]# df -h /tmp

[Step5] 验证:生成11G大容量文件,写入过程无报错且文件正常创建。说明PV和PVC配置的容量限制仅作为调度匹配标识,不会对实际读写空间施加限制。只有当后端NFS共享空间写满才会报错。

[root@mypvc-pod /]# dd if=/dev/zero of=/tmp/big bs=1G count=11
[root@mypvc-pod /]# ls -lh /tmp/big
[root@mypvc-pod /]# exit

5. 数据持久化测试

[Step1] 删除mypvc-pod后重新创建Pod。

root@k8s-master:~/pv-pvc# kubectl delete pod mypvc-pod
root@k8s-master:~/pv-pvc# kubectl create -f mypvc-pod.yml

[Step2] 验证:查看mypvc-pod的/tmp目录,可以看到数据仍然存在。

root@k8s-master:~/pv-pvc# kubectl exec mypvc-pod -- ls -lh /tmp

[Step3] 登录到NFS服务器,同样可以在共享目录下看到该文件。

[root@redhat ~]# ll -h /data/nfs-share/

6. PV回收

在上述创建PV时选择的回收策略是“Recycle”,删除PVC时Kubernetes会启动清理PV内的数据,数据清理流程通过一个单独创建专门用于回收的Pod执行。

需要注意的是:回收Pod依赖镜像bookworm-v1.0.4,如果无法正常拉取可以访问我的百度云链接下载。

[Step1] 这里需要先将占用PVC的Pod删除,防止因为Pod僵死阻塞流程。然后删除PVC,删除完毕后可以看到存在一个回收Pod。

root@k8s-master:~# kubectl delete pod mypvc-pod
root@k8s-master:~# kubectl delete pvc mypvc
root@k8s-master:~# kubectl get pod

[Step2] 验证:此时查看集群中的PV,可以看到“mypv”的状态为“Available”。需要等待控制器自动创建的回收 Pod,挂载存储并清空全部文件。

root@k8s-master:~# kubectl get pv

[Step3] 验证:登录到NFS服务,可以看到共享目录的文件已清空。

[root@redhat ~]# ll /data/nfs-share/

四、Retain PV案例配置

1. NFS模拟存储设备

[Step1] 这里继续使用上述配置好NFS服务器,分别在工作节点验证共享。

root@k8s-worker1:~# showmount -e 192.168.8.10
root@k8s-worker2:~# showmount -e 192.168.8.10

2. 创建PV持久化存储卷

[Step1] 因为PV和PVC要编写的YAML文件比较多,这里创建一个目录用于存储文件。

root@k8s-master:~# mkdir retain-pv-pvc
root@k8s-master:~# cd retain-pv-pvc/

[Step2] 本地新建YAML清单,定义回收策略为Retain的PV持久卷。

root@k8s-master:~/retain-pv-pvc# vim retain-pv.yml

# 写入下列内容
apiVersion: v1			# 固定配置
kind: PersistentVolume	# 资源类型
metadata:
  name: retain-pv
spec:
  capacity:			# 定义PV的容量
    storage: 10Gi	# 分配存储空间大小
  accessModes:		# 访问模式
    # 仅支持单个节点挂载读写,同一节点可允许多个Pod共用,跨节点无法挂载
    - ReadWriteOnce
  #  指定PV的回收策略,不删除,需要管理员手动回收
  persistentVolumeReclaimPolicy: Retain
  nfs:	# 配置NFS服务器信息
    path: /data/nfs-share	# NFS共享目录
    server: 192.168.8.10	# NFS服务器地址

[Step3] 根据YAML清单向Kubernetes集群声明式地创建PV持久化存储卷。

root@k8s-master:~/retain-pv-pvc# kubectl create -f retain-pv.yml

[Step4] 验证:查看当前集群中的所有PV卷资源。

root@k8s-master:~/retain-pv-pvc# kubectl get pv

3. 创建PVC持久化存储申请

[Step1] 本地新建YAML清单,定义PVC持久化存储申请。

root@k8s-master:~/retain-pv-pvc# vim retain-pvc.yml

# 写入下列内容
apiVersion: v1					# 固定配置
kind: PersistentVolumeClaim		# 资源类型
metadata:
  name: retain-pvc
spec:
  accessModes:			# 匹配的卷访问模式,需要与PV匹配
    - ReadWriteOnce		# 匹配访问模式为ReadWriteOnce的PV
  volumeName: retain-pv	# 绑定指定名称的PV,匹配retain-pvc
  resources:			# 存储资源申请配额
    requests:	# 指定PV容量,如果不存在满足该容量需求的PV,则PVC无法绑定任何PV
      storage: 5Gi		# 申请的存储空间

[Step2] 根据YAML清单向Kubernetes集群声明式地创建PVC持久化存储申请。

root@k8s-master:~/retain-pv-pvc# kubectl create -f retain-pvc.yml

[Step3] 验证:查看当前集群中的所有PVC资源。

root@k8s-master:~/retain-pv-pvc# kubectl get pvc

[Step4] 验证:查看当前集群中的所有PV卷资源,可以看到retain-pv的状态变更为“Bound”。

root@k8s-master:~/retain-pv-pvc# kubectl get pv

4. 创建Pod关联PVC

[Step1] 本地新建YAML清单,定义Pod资源描述模板,并关联mypvc。

root@k8s-master:~/retain-pv-pvc# vim retain-pod.yml
retain-pvc 
# 写入下列内容
apiVersion: v1		# 固定配置
kind: Pod			# 资源类型
metadata:
  name: retain-pod
spec:
  containers:
    - name: retain-pod
      image: kylin-server-10-sp3b23-x86_64-20230324:b23
      imagePullPolicy: IfNotPresent
      args:		# 更改容器默认启动命令,自定义运行指令
        - /bin/sh
        - -c
        - sleep 10000
      volumeMounts:				# 存储卷挂载配置
        - mountPath: "/tmp"		# 容器内挂载目录
          name: retain-pvc		# Volume卷名称
  volumes:		# 定义存储卷
    # 自定义存储卷名称,与[containers.volumeMounts.name]对应
    - name: retain-pvc
      persistentVolumeClaim:	# 绑定持久化存储申请PVC
        claimName: retain-pvc	# 绑定名为retain-pvc的PVC存储申请

[Step2] 根据YAML清单向Kubernetes集群声明式地创建Pod资源。

root@k8s-master:~/retain-pv-pvc# kubectl create -f retain-pod.yml

[Step3] 验证:查看当前集群中的所有的Pod资源。

root@k8s-master:~/retain-pv-pvc# kubectl get pods

[Step4] 验证:登录到mypvc-pod,查看/tmp目录的挂载来源。可以看到NFS服务器IP地址与共享目录,代表PVC已成功挂载到Pod。

root@k8s-master:~/retain-pv-pvc# kubectl exec -it retain-pod -- /bin/bash
[root@retain-pod /]# df -h /tmp

[Step5] 验证:生成11G大容量文件,写入过程无报错且文件正常创建。

[root@retain-pod /]# dd if=/dev/zero of=/tmp/big bs=1G count=11
[root@retain-pod /]# ls -lh /tmp/big
[root@retain-pod /]# exit

5. 数据持久化测试

[Step1] 删除retain-pod后重新创建Pod。

root@k8s-master:~/retain-pv-pvc# kubectl delete pod retain-pod
root@k8s-master:~/retain-pv-pvc# kubectl create -f retain-pod.yml

[Step2] 验证:查看retain-pod的/tmp目录,可以看到数据仍然存在。

root@k8s-master:~/retain-pv-pvc# kubectl exec retain-pod -- ls -lh /tmp

[Step3] 登录到NFS服务器,同样可以在共享目录下看到该文件。

[root@redhat ~]# ll -h /data/nfs-share/

6. PV回收

[Step1] 这里需要先将占用PVC的Pod删除,防止因为Pod僵死阻塞流程,随后删除PVC。

root@k8s-master:~/retain-pv-pvc# kubectl delete pod retain-pod
root@k8s-master:~/retain-pv-pvc# kubectl delete pvc retain-pvc

[Step2] 删除PVC后,PV的数据仍然存在,此时PV的状态为“Released”。

root@k8s-master:~/retain-pv-pvc# kubectl get pv

[Step3] 登录到NFS服务器,可以看到文件仍然存在。

[root@redhat ~]# ll -h /data/nfs-share/

[Step4] 采用Retain回收策略的PV,删除关联PVC后,后端存储数据不会被清理。同时PV会保留指向原PVC的claimRef绑定引用,状态变更为Released。受到该残留引用限制,PV无法对接新PVC,需要手动删除claimRef参数以解决绑定限制。

# 方式1:一键删除
root@k8s-master:~/retain-pv-pvc# kubectl patch pv retain-pv -p '{"spec":{"claimRef":null}}'

# 方式2:手动删除,删除下列整块内容
root@k8s-master:~/retain-pv-pvc# kubectl edit pv retain-pv
claimRef:
  name: retain-pvc
  namespace: default
  uid: xxxxxx

[Step5] 查看当前集群中的PV状态,可以看到retain-pv状态变更为“Available”。

root@k8s-master:~/retain-pv-pvc# kubectl get pv

五、StorageClass案例配置

1. 安装与配置外部存储供给器

如果无法正常下载nfs-subdir-external-provisioner.git和nfs-subdir-external-provisioner:v4.0.2,可以访问我的百度云链接获取。

链接: https://pan.baidu.com/s/1SV3Q_dYH_xfnE58Coq7oNA?pwd=9qgu

提取码: 9qgu

[Step1] 克隆仓库,内置全套RBAC、Deployment、StorageClass部署YAML。

root@k8s-master:~# git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner.git

[Step2] 批量替换rbac.yaml和deployment.yaml文件中“namespace”字段值为default。缺省情况下,文件内“namespace”字段值为default,无需任何操作。

root@k8s-master:~# cd nfs-subdir-external-provisioner/
root@k8s-master:~/nfs-subdir-external-provisioner# vim deploy/rbac.yaml
root@k8s-master:~/nfs-subdir-external-provisioner# vim deploy/deployment.yaml

# 将文件内所有 [namespace: default] 修改为default,默认情况下文件的namespace全为default,不需任何修改
:%s/namespace:.*/namespace: default/g		// 批量替换
:x											// 保存并退出

[Step3] NFS动态存储供给器需要调用Kubernetes接口实现PV自动创建,必须配置RBAC权限赋予其操作集群存储资源的资格。加载rbac.yaml文件即可加载并部署rbac.yaml内的权限配置,完成供给器运行所需要的权限初始化。

root@k8s-master:~/nfs-subdir-external-provisioner# kubectl create -f deploy/rbac.yaml

[Step4] 验证:查看RBAC文件所创建的相关资源。

root@k8s-master:~/nfs-subdir-external-provisioner# kubectl get sa
root@k8s-master:~/nfs-subdir-external-provisioner# kubectl get role
root@k8s-master:~/nfs-subdir-external-provisioner# kubectl get rolebindings.rbac.authorization.k8s.io

[Step5] 修改供给器Deployment配置,对接外部NFS服务。“env”字段负责provisioner程序运行逻辑,用于创建子PV目录。“volumes”字段负责宿主机kubelet挂载NFS到Pod,底层挂载,不会读取env。

root@k8s-master:~/nfs-subdir-external-provisioner# vim deploy/deployment.yaml

# 修改下列内容
env:
  - name: PROVISIONER_NAME
    value: k8s-sigs.io/nfs-subdir-external-provisioner
  - name: NFS_SERVER
    value: 192.168.8.10			# 替换为NFS服务器IP地址
  - name: NFS_PATH
    value: /data/nfs-share		# 替换为NFS共享目录
volumes:
  - name: nfs-client-root
    nfs:
      server: 192.168.8.10		# 替换为NFS服务器IP地址
      path: /data/nfs-share		# 替换为NFS共享目录

[Step6] 根据YAML清单向Kubernetes集群声明式地创建NFS供给器Pod。需要注意的是,这个Pod依赖nfs-subdir-external-provisioner:v4.0.2镜像,如果无法正常拉取,可以访问我提供的百度云链接下载并导入。

root@k8s-master:~/nfs-subdir-external-provisioner# kubectl apply -f deploy/deployment.yaml

[Step7] 验证:查看当前集群中的所有Deployment资源。

root@k8s-master:~/nfs-subdir-external-provisioner# kubectl get deployments.apps

[Step8] 验证:查看当前集群中的所有Pod资源。

root@k8s-master:~# kubectl get pods

2. 创建StorageClass资源

[Step1] 如果需要支持NFS,需要在所有工作节点都按照NFS组件,这里我们在上述的操作已安装。本地新建YAML清单,创建StorageClass动态供应卷。

root@k8s-master:~# vim storageclass.yml

# 写入下列内容
apiVersion: storage.k8s.io/v1	# 固定配置
kind: StorageClass				# 资源类型
metadata:
  name: nfs-client		# 存储类名称,创建PVC时通过storageClassName:nfs-storage引用
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner	# 指定存储供给器
allowVolumeExpansion: true		# 开启存储卷在线扩容功能
# 延迟绑定机制:不会在PVC创建时立即生成并绑定PV,等到Pod调度就绪、真正使用该存储时,才触发provisioner创建NFS目录与PV
volumeBindingMode: WaitForFirstConsumer
parameters:		# 自定义供给器参数
  # 自定义NFS共享下目录命名规则,例如:default命名空间下名为retain-pvc的PVC,自动创建目录default-retain-pvc
  pathPattern: "${.PVC.namespace}-${.PVC.name}"	
  onDelete: delete		# PVC删除策略

[Step2] 根据YAML清单向Kubernetes集群声明式地创建StorageClass动态供应卷。

root@k8s-master:~# kubectl create -f storageclass.yml

[Step3] 验证:查看当前集群中的所有StorageClass动态供应卷资源。

root@k8s-master:~# kubectl get storageclasses.storage.k8s.io

3. 创建PVC自动生成PV

[Step1] 本地新建YAML清单,定义PVC持久化存储申请。

root@k8s-master:~# vim nfs-pvc.yml

# 写入下列内容
apiVersion: v1					# 固定配置
kind: PersistentVolumeClaim		# 资源类型
metadata:
  name: nfs-pvc
spec:
  # 和StorageClass中的name对应
  storageClassName: nfs-client
  accessModes:			# 匹配的卷访问模式,需要与PV匹配
    - ReadWriteMany		# 匹配访问模式为ReadWriteMany的PV
  resources:			# 存储资源申请配额
    requests:			# 指定PV容量,如果不存在满足该容量需求的PV,则PVC无法绑定任何PV
      storage: 5G		# 申请的存储空间

[Step2] 根据YAML清单向Kubernetes集群声明式地创建PVC持久化存储申请。

root@k8s-master:~# kubectl create -f nfs-pvc.yml

[Step3] 验证:查看PVC状态,可以看到状态变更为“Pending”。这是因为我们在StorageClass中使用的是“WaitForFirstConsumer”,需要创建一个Pod关联PVC后,才会触发provisioner去NFS创建目录、生成PV、绑定PV,状态才会变更为“Bound”。

root@k8s-master:~# kubectl get pvc

4. 创建Pod关联PVC

[Step1] 本地新建YAML清单,定义PVC持久化存储申请。

root@k8s-master:~# vim nfs-pod.yml

# 写入下列内容
apiVersion: v1		# 固定配置
kind: Pod			# 资源类型
metadata:
  name: mypvc-pod
spec:
  containers:
    - name: mypvc-kylin-pod
      image: kylin-server-10-sp3b23-x86_64-20230324:b23
      imagePullPolicy: IfNotPresent
      args:			# 更改容器默认启动命令,自定义运行指令
        - /bin/sh
        - -c
        - sleep 10000
      volumeMounts:					# 存储卷挂载配置
        - name: kylin-pvc			# Volume卷名称
          mountPath: "/data"		# 容器内挂载目录
  volumes:		# 定义存储卷
    # 自定义存储卷名称,与[containers.volumeMounts.name]对应
    - name: kylin-pvc
      persistentVolumeClaim:		# 绑定持久化存储申请PVC
        claimName: nfs-pvc			# 绑定名为nfs-pvc的PVC存储申请

[Step2] 根据YAML清单向Kubernetes集群声明式地创建Pod资源。

root@k8s-master:~# kubectl create -f nfs-pod.yml

[Step3] 验证:查看当前集群中的所有的Pod资源。

root@k8s-master:~# kubectl get pods

[Step4] 验证:查看当前集群中的PV,可以看到PVC自动生成一条PV,状态为“Bound”。

root@k8s-master:~# kubectl get pv

[Step5] 验证:查看PVC状态,可以看到成功绑定了一个PV,与「Step4」中查询到的PV名称一致。

root@k8s-master:~# kubectl get pvc

[Step6] 验证:登录到NFS服务器,同样可以在共享目录下看到自动生成的目录。目录的命名参照定义StorageClass资源时我们定义的“pathPattern”字段,格式为「命名空间+PVC」名称。

[root@rocky9 ~]# ll /data/nfs-share/

5. 数据读写测试

[Step1] 登录到mypvc-pod,查看/data目录的挂载来源。可以看到NFS服务器IP地址与共享目录,代表PVC已成功挂载到Pod。

root@k8s-master:~# kubectl exec -it mypvc-pod -- /bin/bash
[root@mypvc-pod /]# df -h /data

[Step2] 生成11G大容量文件,写入过程无报错且文件正常创建。

[root@mypvc-pod /]# dd if=/dev/zero of=/tmp/big bs=1G count=11
[root@mypvc-pod /]# ls -lh /tmp/big
[root@mypvc-pod /]# exit

Kubernetes 存储管理:EmptyDir、hostPath 2026-10-27
Kubernetes ConfigMap与Secret配置管理 2026-10-27

评论区