简介

PersistentVolume(PV)是 K8S 集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。 PV 是容量插件,如 Volumes,但其生命周期独立于使用 PV 的任何单个 Pod。 此 API 对象捕获存储实现的详细信息,包括 NFS,iSCSI 或特定于云提供程序的存储系统。

PersistentVolumeClaim(PVC)是 K8S 中由用户进行存储的请求。 它类似于 Pod。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU和内存)。声明可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)。

通过 storageClassName 参数来指定使用对应名字的 StorageClass,也就是 PVC 与 PV 具有相同的storageClassName,PVC 才能绑定到这个对应的 PV。PVC 可以不指定storageClassName,或者将该值设置为空,如果打开了准入控制插件,并且指定一个默认的 StorageClass,则 PVC 会使用默认的 StorageClass,否则就绑定到没有 StorageClass 的 PV 上。


生命周期

PV 是 K8S 群集中的资源。PVC 是对这些资源的请求,并且还充当对资源的检查。PV 和 PVC 之间的相互作用遵循以下生命周期:

Provisioning ——> Binding ——> Using ——> Releasing ——> Recycling

供应准备 Provisioning—通过集群外的存储系统或者云平台来提供存储持久化支持。

– 静态提供 Static:集群管理员创建多个 PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于 Kubernetes API 中,可用于消费

– 动态提供 Dynamic:当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses:PVC 必须请求存储类,并且管理员必须创建并配置该类才能进行动态创建。声明该类为 “” 可以有效地禁用其动态配置。

绑定 Binding—用户创建 PVC 并指定需要的资源和访问模式。在找到可用 PV 之前,PVC 会保持未绑定状态。

使用 Using—用户可在 Pod 中像 Volume 一样使用 PVC。

释放 Releasing—用户删除 PVC 来回收存储资源,PV 将变成 released 状态。由于还保留着之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他 PVC 使用。

回收 Recycling—PV 可以设置三种回收策略:保留(Retain)、回收(Recycle)、删除(Delete)。

– 保留策略:允许人工处理保留的数据。

– 删除策略:将删除 PV 和外部关联的存储资源,需要插件支持。

– 回收策略:将执行清除操作,之后可以被新的 PVC 使用,需要插件支持。

注:目前只有 NFS 和 HostPath 类型卷支持回收策略,AWS EBS、GCE PD、Azure Disk、Cinder 支持删除 (Delete) 策略。

实验环境

# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
test01 Ready master 17d v1.14.0 192.168.127.133 CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://18.9.3
test02 Ready 17d v1.14.0 192.168.127.135 CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://18.9.3

PV 支持的类型很多,比如:RBD、CephFS、Glusterfs、NFS 等,这里使用 nfs 类型的 PV,并使用 Job 对象来验证 PV 创建以及 PVC 资源的申请与绑定。

实践操作

1. 在 K8S master(192.168.127.133)上准备 nfs 服务器,用于提供存储。

# yum install nfs-utils rpcbind -y
# mkdir -p /data/nfs
# vim /etc/exports
/data/nfs *(rw,sync)
# systemctl start nfs-server.service
并在 K8S node 上安装 nfs 客户端:
# yum install nfs-utils -y

2. 使用以下配置创建 PV 存储。

编辑 PV 资源的配置文件:

# cat pv_nfs.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1
spec:
# 指定 PV 的容量为1Gi
capacity:
storage: 1Gi
# 指定访问模式
accessModes:
# PV 能以 readwrite 模式 mount 到单个节点
- ReadWriteOnce
# 指定 PV 的回收策略,即 PVC 资源释放后的事件,recycle (不建议,使用动态供给代替)删除 PVC 的所有文件
persistentVolumeReclaimPolicy: Recycle
# 指定 PV 的 class 为 mynfs,相当于为 PV 分类,PVC 将指定 class 申请 PV
storageClassName: mynfs
# 指定 PV 为 nfs 服务器上对应的目录
nfs:
path: /data/nfs
server: 192.168.127.133

应用 PV 配置:

# kubectl apply -f pv_nfs.yml
persistentvolume/nfspv1 created
查看 PV 资源:
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfspv1 1Gi RWO Recycle Available mynfs 28s

3. 使用以下配置创建 PVC,通过指定 storageClassName 名称与指定 PV 进行绑定。

编辑 PVC 资源配置文件:

# cat pvc_nfs.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfspvc1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: mynfs

应用 PVC 配置:

# kubectl apply -f pvc_nfs.yml
persistentvolumeclaim/nfspvc1 created

查看 PVC 资源,通过下面的结果可以看到 nfspvc1 已经与 PV 为 nfspv1 绑定:

# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfspvc1 Bound nfspv1 1Gi RWO mynfs 25s

4. 使用以下配置创建 Job,使用 PVC 存储。

编辑应用资源配置文件:

# cat pvjob.yml
apiVersion: batch/v1
kind: Job
metadata:
name: pvjob
spec:
template:
spec:
containers:
- name: bbox1
image: busybox
command: ["/bin/sh","-c","echo 'hello pv' > /mydata/hello"]
volumeMounts:
- mountPath: "/mydata"
name: mydata
restartPolicy: Never
volumes:
- name: mydata
persistentVolumeClaim:
claimName: nfspvc1

该 job 将在 nfs 的 volume 创建一个 hello 文件,打印 hello pv 字符串。

应用该 Job 配置文件:

# kubectl apply -f pvjob.yml

job.batch/pvjob created

确认容器状态是 Completed 状态:

# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-nginx-684b85cd79-kv5sg 1/1 Running 2 10d 10.244.1.14 test02
pvjob-gljl7 0/1 Completed 0 39s 10.244.1.33 test02
确认 master 上 nfs 的 /data/nfs 目录,检查实验结果:
# cat /data/nfs/hello
hello pv

排错

在这次实验中出现了几个错误:

1. 使用 PVC 存储的 Pod 一直处于 pending 状态,无法启动,通过命令 kubectl describe pod podname查看对应报错的 Pod 的信息,部分报错信息如下:

# dmesg | tail or so.
Warning FailedMount 118s kubelet, test02 MountVolume.SetUp failed for volume "nfspv1" : mount failed: exit status 32
Mounting command: systemd-run
Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/d25895f2-8220-11e9-820c-000c295ab742/volumes/kubernetes.io~nfs/nfspv1 --scope -- mount -t nfs 192.168.127.133:/data/nfs /var/lib/kubelet/pods/d25895f2-8220-11e9-820c-000c295ab742/volumes/kubernetes.io~nfs/nfspv1
Output: Running scope as unit run-15184.scope.
mount: wrong fs type, bad option, bad superblock on 192.168.127.133:/data/nfs,
missing codepage or helper program, or other error
(for several filesystems (e.g. nfs, cifs) you might
need a /sbin/mount. helper program

注意到报错信息:wrong fs type, bad option, bad superblock on 192.168.127.133:/data/nfs,如果没有安装 nfs-utils 软件包,无法识别 nfs 类型的文件系统,也无法作为 nfs 的客户端使用。

解决方案:

安装 nfs-utils 软件包,删除之前创建失败的 Job 资源并重新创建。

2. 如果 Pod 状态是 Error 的:
# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-nginx-684b85cd79-kv5sg 1/1 Running 2 10d 10.244.1.14 test02
pvjob-4dp29 0/1 Error 0 3m10s 10.244.1.35 test02
pvjob-f6lpp 0/1 Error 0 2m33s 10.244.1.37 test02
pvjob-l8q6z 0/1 Error 0 2m13s 10.244.1.38 test02
pvjob-qbxx6 0/1 Error 0 53s 10.244.1.39 test02
pvjob-zcx6l 0/1 Error 0 2m56s 10.244.1.36 test02

通过查看执行失败 Pod 的 log 日志,发现是容器没有权限写文件到挂载的 nfs 目录中:

# kubectl logs pvjob-4dp29
/bin/sh: can't create /mydata/hello: Permission denied

解决方案:

Permission denied 多见于普通用户执行高权限命令失败,不过 busybox 容器本身使用的就是 root 用户,因此不存在这个问题。在 nfs 中,nfs 服务端没有权限访问挂载的目录也会导致这个问题。

更改目录属主为 nfsnobody:

# chown nfsnobody /data/nfs
删除原来 Job 并重新创建后,Job 就能执行成功:
# kubectl delete -f pvjob.yml
# kubectl apply -f pvjob.yml
# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
deploy-nginx-684b85cd79-kv5sg 1/1 Running 2 10d 10.244.1.14 test02
pvjob-gljl7 0/1 Completed 0 39s 10.244.1.33 test02

参考:

https://staight.github.io/2018/08/18/%E8%AE%B0k8s%E7%9A%84%E4%B8%80%E6%AC%A1pv&pvc%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B/