K8s存储管理—volume、pv、pvc

时间:2022-11-18 21:55:55

介绍

---------来自官方文档

     

存储的管理是一个与计算实例的管理完全不同的问题。PersistentVolume 子系统为用户
和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来。为了实现
这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。

       

持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应
,或者 使用存储类(Storage Class)来动态供应。 持久卷是集群资源,就像节点也
是集群资源一样。PV 持久卷和普通的 Volume 一样,也是使用 卷插件来实现的,只是
它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现
细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

       

    持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上
与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求
特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式
(例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。

       

   尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源,常见的情况是针对不同
的 问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管
理员需要能够提供不同性质的 PersistentVolume,并且这些 PV 卷之间的差别不
仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足
这类需求,就有了 存储类(StorageClass) 资源。

前言

容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题
。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失二容器以子净的状态
(碗像最初的状态)重新启动。其次,在pod中同时运行多个容器时,这些容器之间通常
需要共享文件。Kubernetes中的Volume抽象就很好的解决了这些问题。Pod中的容器通
过Pause容器共享Volume 。


容器中的文件是临时存储在磁盘上的,数据卷的引入主要是为了解决以下几个问题:

     

a.当容器升级或者崩溃时,kubelet会重建容器,容器内原有的文件会丢失;
b.一个Pod中运行的多个容器需要共享文件。

 常用的数据卷(Volume)有:

   

本地:例如hostPath、emptyDir;
网络:例如NFS、Ceph、GlusterFS;
k8s资源:例如configmap、secret。


emptyDir存储卷

1.
当Pod被分配给节点时,首先创建emptyDir卷,并且只要该Pod在该节点上运行,
该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入
emptyDir都中的相问文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。
当出于任何原因从节点中删除Pod时,emptyDir中的数据将被水久删除。

       2、emptyDir 的使用场景:      

缓存空间,例如基于磁盘的归并排序。

为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。

在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
默认情况下, emptyDir 卷存储在支持该节点所使用的介质上;这里的介质可以是磁盘
或 SSD 或网络存储,这取决于您的环境。 但是,您可以将 emptyDir.medium 字段
设置为 “Memory”,以告诉 Kubernetes 为您安装 tmpfs(基于内存的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同。 tmpfs 在节点重启时会被清除,并且您所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。

    注:容器崩溃不会导致Pod被删除,因此容器崩溃期间emptyDir中的数据是安全的。emptyDir可以实现Pod中容器之间的数据共享。尽管Pod中的容器挂载emptyDir卷的路径可能不同,这些容器都可以读写emptyDir卷中相同的文件。

emptyDir卷创建

创建目录volumes,编辑vol1.yaml文件;
containers: %一个pod中创建两个容器共享volumes
mountPath: /cache %vm1中的卷挂载到容器内的/cache
mountPath: /usr/share/nginx/html %vm2中的卷挂载到容器内的/usr/share/nginx/html
medium: Memory %使用内存介质
sizeLimit: 100Mi %可以使用100M内存

K8s存储管理—volume、pv、pvc

应用配置

K8s存储管理—volume、pv、pvc

真机访问测试,vol1这个pod 的 ip 是 10.244.1.93;

403错误是因为容器没有发布内容

K8s存储管理—volume、pv、pvc

进入vol1的vm1容器,在/cache中写入发布内容

K8s存储管理—volume、pv、pvc

再次访问测试成功

K8s存储管理—volume、pv、pvc

进入vol1的vm2容器,在/usr/share/nginx/html中可以看到发布内容和vm1中的一样,说明vm1和vm2共用卷资源;

然后在原有内容的基础上再追加发布内容

K8s存储管理—volume、pv、pvc

连接进入vol1的vm1容器,看到了追加的内容,进一步证明了同一个pod内的两个容器vm1和vm2共用卷资源;

测试写入200M的文件,由于超过了限定的100M内存,可以看到pod会损坏

K8s存储管理—volume、pv、pvc

K8s存储管理—volume、pv、pvc

查看pod详细信息,可以看到文件超过限制后,则1-2分钟后会被kubelet evict掉,而不是“立即”被evict,这是因为kubelet是定期进行检查的,这里会有一个时间差

K8s存储管理—volume、pv、pvc

emptydir缺点:
不能及时禁止用户使用内存。虽然过1-2分钟kubelet会将Pod挤出,但是这个时间内,其实对node还是有风险的;
影响kubernetes调度,因为empty dir并不涉及node的resources,这样会造成Pod“偷偷”使用了node的内存,但是调度器并不知晓;
用户不能及时感知到内存不可用

编辑yaml文件 

apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- image: centos
name: writer
command: ["bash","-c","for i in {1..100}; do echo $i >> /data/tmpfile; sleep 1; done"]
volumeMounts:
- mountPath: /data
name: data

- image: centos
name: reader
command: ["bash", "-c", "tail -f /data/tmpfile"]
volumeMounts:
- mountPath: /data

volumes:
- name: data
emptyDir: {}

emptyDir挂在的容器目录位于Pod所在节点的以下路径:

/var/lib/kubelet/pods/${container_ID}/volumes/kubernetes.io~empty-dir

hostPath存储卷(本地):节点数据卷

1.hostPath卷能将Pod所在宿主机节点文件系统上的文件或目录挂载到Pod中。具有相同
配置(例如基于同一PodTemplate创建)的多个Pod,会由于节点上文件的不同,而在
不同节点上有不同的行为。

2、hostPath 的一些用法有:
运行一个需要访问 Docker 引擎内部机制的容器,挂载 /var/lib/docker 路径。
在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys。
允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及
应该以什么方式存在。

3、除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type。

K8s存储管理—volume、pv、pvc

注:

HostPath卷存在许多安全风险,最佳做法是尽可能避免使用HostPath。 当必须使用HostPath卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。HostPath卷可能会暴露特权系统凭据(例如Kubelet)或特权API(例如容器运行时套接字),可用于容器逃逸或攻击集群的其他部分


当使用这种类型的卷时要小心,因为:

具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而
在不同节点上有不同的行为。当 Kubernetes 按照计划添加资源感知的调度时,这类
调度机制将无法考虑由 hostPath 使用的资源。基础主机上创建的文件或目录只能由
root 用户写入。您需要在 特权容器 中以 root 身份运行进程,或者修改主机上的
文件权限以便容器能够写入 hostPath 卷。

创建host.yaml 文件,

mountPath: /test-pd %把卷挂载到容器内的/test-pd

path: /data %卷的路径在/data

type: DirectoryOrCreate %如果在指定路径上不存在,那么根据需要创建空目录,权限为0755,具有与kubelet相同的组和所有权

apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
spec:
containers:
- image: nginx
name: myapp1
#定义容器挂载内容
volumeMounts:
#使用的存储卷名称,如果跟下面volume字段name值相同,则表示使用volume的这个存储卷
- name: html
#挂载至容器中的哪个目录
mountPath: /usr/share/nginx/html
#读写挂载方式,默认为读写模式false
readOnly: false
#volume字段定义了pause容器关联的宿主机或分布式文件系统存储卷
volumes:
#名称
- name: html
#路径:为宿主机存储路径
hostPath:
#在宿主机上目录的路径
path: /data/pod/volumes
#定义类型:这表示如果宿主机没有此目录则会自动创建
type: DirectoryOrCreate


NFS共享存储卷:(网络数据卷)

提供对NFS挂载支持,可以自动将NFS共享路径挂载到Pod中。

搭建NFS文件共享服务器:

[root@k8s-node1 ~]# yum install nfs-utils -y
[root@k8s-node1 ~]# vi /etc/exports
/home/data *(rw,sync,no_root_squash)
[root@k8s-node1 ~]# mkdir -p /home/data/
[root@k8s-node1 ~]# systemctl start nfs
[root@k8s-node1 ~]# systemctl enable nfs

所有Node节点上都要安装nfs-utils包。在其他节点挂载NFS路径

[root@k8s-node2 ~]# yum install nfs-utils -y
[root@k8s-node2 ~]# mkdir -p /home/data/
#手动挂载
[root@k8s-node2 ~]# mount -t nfs 192.168.243.130:/home/data /home/data/
#开机自动挂载
[root@k8s-node2 ~]# echo '192.168.243.130:/home/data/ /home/data/ nfs4 defaults 0 0' >> /etc/fstab
[root@k8s-node2 ~]# mount -a
#移除挂载
[root@k8s-node2 ~]# umount -v /home/data/

将nginx网站程序根目录持久化到NFS存储,为多个Pod提供网站程序文件。

apiVersion: apps/v1
kind: Deployment
metadata:
name: webserver
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: wwwroot
mountPath: /usr/share/nginx/html
volumes:
- name: wwwroot
nfs:
server: 192.168.243.130
path: /home/data

2.

[root@localhost ~]# mkdir -p /data/volume
[root@localhost ~]# cd /data/volume/
[root@localhost /data/volume]# chmod 777 /data/volume/

设置共享策略
[root@localhost /data/volume]# vim /etc/exports
[root@localhost /data/volume]# systemctl start rpcbind nfs
[root@localhost /data/volume]# showmount -e
Export list for localhost.localdomain:
/data/volume 192.168.243.0/24

[root@localhost /data/volume]# echo "this is nfs volume" > index.html
[root@localhost /data/volume]# ls
index.html

在其他节点上查看

[root@master01 /opt/Volumes]# showmount -e 192.168.37.108
Export list for 192.168.243.130:
/data/volume 192.168.243.0/24

编写yaml文件

apiVersion: v1
kind: Pod
metadata:
name: pod-nfs
spec:
containers:
- image: nginx
name: myapp1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: false
volumes:
- name: html
nfs:
path: /data/volume
#nfs服务的地址
server: 192.168.243.130
[root@master01 /opt/Volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emptydir 2/2 Running 0 64m 10.244.1.16 node01 <none> <none>
pod-hostpath 1/1 Running 0 34m 10.244.2.8 node02 <none> <none>
pod-nfs 1/1 Running 0 42s 10.244.2.9 node02 <none> <none>
[root@master01 /opt/Volumes]# curl http://10.244.2.9
this is nfs volume

查看详细信息:

[root@master01 /opt/Volumes]# kubectl describe pod pod-nfs

K8s存储管理—volume、pv、pvc

在容器中添加数据

[root@master01 /opt/Volumes]# kubectl exec -it pod-nfs -c myapp1 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@pod-nfs:/# cd /usr/share/nginx/html/
root@pod-nfs:/usr/share/nginx/html# ls
index.html
root@pod-nfs:/usr/share/nginx/html# cat index.html
this is nfs volume
root@pod-nfs:/usr/share/nginx/html# echo "this is one nfs!" >>index.html

在nfs服务器查看。

[root@localhost /data/volume]# cat index.html
this is nfs volume
this is one nfs!

以上三种是解决无状态应用共享存储卷

PersistentVolume持久卷(PV)

持久卷(PersistentVolume, PV)是对存储资源创建和使用的抽象,使得存储可以作为
集群中的资源被管理

持久卷的类型

持久卷是通过插件的形式实现的。目前k8s支持的插件包括:

   

awsElasticBlockStore 、azureDisk 、cephfs 、csi 容器存储接口 、
hostPath卷 、iscsi(iSCSI存储) 、local 节点上挂载的本地存储设备 、
nfs 网络文件系统存储 、vsphereVolume(vSphere VMDK卷)

volumeMode:卷模式

       

    k8s支持两种卷模式(volumeModes):Filesystem(文件系统)和Block(块存储)。
volumeMode是一个可选的API参数。如果该参数被省略,默认的卷模式是Filesystem。

volumeMode属性设置为Filesystem的卷会被Pod挂载到某个目录。如果卷的存储来自
某块设备而该设备目前为空,k8s会在第一次挂载卷之前在设备上创建文件系统。

也可以将volumeMode设置为Block,以便将卷作为原始块设备来使用。这类卷以块设备
的方式交给Pod使用,其上没有任何文件系统,使用前需要格式化。

accessModes:访问模式

PV支持的访问模式(accessModes)有:

ReadWriteOnce(RWO):卷可以被一个节点以读写方式挂载。
ReadWriteOnce访问模式也允许运行在同一节点上的多个 Pod 访问卷。

ReadOnlyMany(ROX):卷可以被多个节点以只读方式挂载。
ReadWriteMany(RWX):卷可以被多个节点以读写方式挂载。
ReadWriteOncePod:卷可以被单个Pod以读写方式挂载。

如果想确保整个集群中只有一个Pod可以读取或写入该PVC, 要使用ReadWriteOncePod访问模式。这只支持CSI卷以及需要Kubernetes 1.22以上版本。

Phase:状态阶段

每个卷会处于以下阶段(Phase)之一:
Available(可用):卷是一个空闲资源,尚未绑定到任何申领;
Bound(已绑定):该卷已经绑定到某申领;
Released(已释放):所绑定的申领已被删除,但是资源尚未被集群回收;
Failed(失败):卷的自动回收操作失败。

Reclaim Policy:回收策略

目前的回收策略有:
Retain(手动回收):默认策略,保留数据,需要手动回收;

Recycle(回收):保留PV,但是会清除PV中的数据,相当于rm -rf /thevolume/*;

Delete(删除):删除PV和数据,与PV相关联的后端存储同时被删除。诸如AWS EBS、
GCE PD、Azure Disk或OpenStack Cinder卷这类关联存储资源也被删除。

PVC:持久卷申领

持久卷申领(PersistentVolumeClaim, PVC)是用户对集群中PV资源的请求。
Pod申请PVC作为卷来使用,k8s通过PVC查找绑定的PV,并在Pod中挂载。创建PVC之后,
k8s控制平面将通过匹配访问模式accessModes和容量storage查找满足要求的PV。
如果控制平面找到具有合适的PV,则会将PVC绑定到该PV上。

PVC和PV的一般匹配原则如下:

首先,PV.spec.accessModes要与PVC.spec.accessModes相同;
其次,PV.spec.capacity.storage不能小于PVC.spec.resources.requests.storage;

如果有多个PV满足以上要求,则优先选择容量最接近的PV;
如果没有找到合适的PV,则容器和PVC会一直处于Pending状态。

PVC中的需求容量字段storage只能用于匹配PV,并不能起到资源限制作用。容器实际能
够使用的容量大小取决于后端存储。也就是说,假设PVC中定义的资源需求为10G,PV后
端使用的存储的剩余空间为100G,那么绑定该PV后,容器实际可以使用的存储大小也是100G。

K8s存储管理—volume、pv、pvc


PV全称叫做Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的,这个通常都是由运维工程师来定义。

PVC的全称是 Persistent Volume claim,是持久化存储的请求。它是用来描述希望使用什么样的或者说是满足什么条件的PV存储。

PVC 的使用逻辑:在 Pod 中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,PVC必须与对应的PV建立关系,PVC会根据配置的定义去PV申请,而PV是由存储空间创建出来的。PV和PVC 是 Kubernetes抽象出来的一种存储资源。

上面介绍的PV和FVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV.对于运维人员来说维护成本很高,Kubernetes提供一种自动创建Pv的机制,叫StorageClass,它的作用就是创建PV的模板。

创建StorageClass 需要定义PV的属性,比如存储类型、大小等;另外创建这种PV需要用到的存储插件,比如Ceph等。 有了这两部分信息,Kubernetes就能够根据用户提交的 PVC,找到对应的 storageClass,然后Kubernetes就会调用StorageClass声明的存储插件,,自动创建需要的PV并进行绑定。

PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索引检查。
PV全称叫做Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的,这个通常都是由运维工程师来定义。
PVC的全称是 Persistent Volume claim,是持久化存储的请求。它是用来描述希望使用什么样的或者说是满足什么条件的PV存储。
PVC 的使用逻辑:在 Pod 中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,PVC必须与对应的PV建立关系,PVC会根据配置的定义去PV申请,而PV是由存储空间创建出来的。PV和PVC 是 Kubernetes抽象出来的一种存储资源。
上面介绍的PV和FVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV.对于运维人员来说维护成本很高,Kubernetes提供一种自动创建Pv的机制,叫StorageClass,它的作用就是创建PV的模板。
创建StorageClass 需要定义PV的属性,比如存储类型、大小等;另外创建这种PV需要用到的存储插件,比如Ceph等。 有了这两部分信息,Kubernetes就能够根据用户提交的 PVC,找到对应的 storageClass,然后Kubernetes就会调用StorageClass声明的存储插件,,自动创建需要的PV并进行绑定。
PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索引检查。

PV和PVC之间的相互作用遵循这个生命周期:

Provisioning(配置)---> Binding(绑定) --->Using (使用) --->Releasing(释放)--->Recycling(回收)

①Provisioning,即 PV的创建,可以直接创建PV(静态方式),也可以使用storageClass 动态创建

②Binding、将PV 分配给pvc

③Using,Pod通过PVC使用该Volume,并可以通过准入控制StorageProtection(1.9及以前版本为PVCProtection )阻止:删除正在使用的 PVC

④Releasing,Pod释放volume并删除PVC

⑤Reclaiming,回收 PV,可以保留PV以便下次使用,也可以直接从云存储中删除
Provisioning(配置)---> Binding(绑定) --->Using (使用) --->Releasing(释放)--->Recycling(回收)

①Provisioning,即 PV的创建,可以直接创建PV(静态方式),也可以使用storageClass 动态创建

②Binding、将PV 分配给pvc

③Using,Pod通过PVC使用该Volume,并可以通过准入控制StorageProtection(1.9及以前版本为PVCProtection )阻止:删除正在使用的 PVC

④Releasing,Pod释放volume并删除PVC

⑤Reclaiming,回收 PV,可以保留PV以便下次使用,也可以直接从云存储中删除
spec
nfs: (定义存储类型)
path: (定义挂载卷路径)
server: (定义服务器名称)
accessModes: (定义访问模型,有以下三种访问模型,以列表的方式存在,也就是说可以定义多个访问模式)
- Readwriteonce #(RWO)存储可读可写,但只支持被单个 Pod挂载
- ReadonlyMany #(ROX)存储可以以只读的方式被多个Pod 挂载
- ReadwriteMany #(RWX)存储可以以读写的方式被多个Pod 共享
#nfs支持全部三种; isCSI 不支持ReadwiriteMany (iscsI就是在IP 网络上运行scsI协议的一种网络存储技术) HostPath不支持 ReadonlyMany和ReadwriteMany。

capacity: (定义存储能力,一般用于设置存储空间)
storage: 2Gi(指定大小)
StorageClassName: (自定义存储类名称,此配置用于绑定具有相同类别的PvC和pv)
PersistentvolumeReclaimPolicy: Retain #回收策略((Retain/Delete/Recycle)

#Retain(保留):当删除与之绑定的PVC时候,这个PV被标记为released (PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV 上,但是该PV不可用,需要手动来处理这些数据并删除该PV。

#Delete(删除):删除与PV相连的后端存储资源(只有 AWS EBS,GCE PD,Azure Disk 和 Cinder 支持)

#Recycle(回收):删除数据,效果相当于执行了rm -rf /thevolume/*(只有NES 和HostPath支持)



示例:创建一个NFS的pv和pvc

K8s存储管理—volume、pv、pvc

重新设置NFS共享策略

[root@localhost /data/volumes]# vim /etc/exports
/data/volume 192.168.37.0/24(rw,sync,no_root_squash)
/data/volumes/v1 192.168.37.0/24(rw,sync,no_root_squash)
/data/volumes/v2 192.168.37.0/24(rw,sync,no_root_squash)
/data/volumes/v3 192.168.37.0/24(rw,sync,no_root_squash)
/data/volumes/v4 192.168.37.0/24(rw,sync,no_root_squash)
/data/volumes/v5 192.168.37.0/24(rw,sync,no_root_squash)
#不重启生效

exportfs -ar

再看别的主机

[root@master01 /opt/Volumes]# showmount -e 192.168.37.108
Export list for 192.168.37.108:
/data/volumes/v5 192.168.37.0/24
/data/volumes/v4 192.168.37.0/24
/data/volumes/v3 192.168.37.0/24
/data/volumes/v2 192.168.37.0/24
/data/volumes/v1 192.168.37.0/24
/data/volume 192.168.37.0/24

创建pv

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
labels:
type: pv001
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
nfs:
path: /data/volumes/v1
server: 192.168.37.108
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002
labels:
type: pv002
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
nfs:
path: /data/volumes/v2
server: 192.168.37.108
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
labels:
type: pv003
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
nfs:
path: /data/volumes/v3
server: 192.168.37.108
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv004
labels:
type: pv004
spec:
capacity:
storage: 4Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
nfs:
path: /data/volumes/v4
server: 192.168.37.108
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv005
labels:
type: pv005
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
- ReadWriteMany
nfs:
path: /data/volumes/v5
server: 192.168.37.108

执行:[root@master01 /opt/Volumes]# kubectl apply -f pv.yaml

查看pv

K8s存储管理—volume、pv、pvc

创建pvc

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi

执行,查看pv、pvc

[root@master01 /opt/Volumes]# kubectl apply -f pvc.yaml

K8s存储管理—volume、pv、pvc

访问 demo4-pv.yaml

apiVersion: v1
kind: Pod
metadata:
name: pod-pv
spec:
containers:
- image: nginx
name: myapp1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc

执行

kubectl apply -f demo4-pv.yaml

在pv003输入一条数据,访问

kubectl apply -f demo4-pv.yaml

pvc会根据pv的属性来匹配,直到匹配不上为止。

StorageClass:存储类

PV的供给有两种方式:静态供给和动态供给。


1.静态PV:像前面那样,集群管理员创建多个PV,它们携带着真实存储的详细信息,
这些存储对于集群用户是可用的。它们存在于Kubernetes API中,并可用于存储使用。

2.动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给
volume给PVC。这种供给基于StorageClass。

PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期的处于unbound未绑定状态。

集群管理员也可以使用StorageClass实现PV资源的动态供给。每个StorageClass都包
含provisioner、parameters和reclaimPolicy字段, 这些字段会在StorageClass
需要动态分配PV时会使用到。

K8s存储管理—volume、pv、pvc


StorageClass的属性:

1)Provisioner(存储分配器):用来决定使用哪个卷插件分配 PV,该字段必须指定。
可以指定内部分配器,也可以指定外部分配器。外部分配器的代码地址为: kubernetes-incubator/external-storage,其中包括NFS和Ceph等。

(2)Reclaim Policy(回收策略):通过reclaimPolicy字段指定创建的
Persistent Volume的回收策略,回收策略包括:Delete 或者 Retain,没有指定默认
为Delete。

更多属性查看:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/
补充:
a. NFS Client Provisioner是一个automatic provisioner,使用NFS作为存储**,
自动创建PV和对应的PVC**,本身不提供NFS存储,需要外部先有一套NFS存储服务;

b.PV以 $ {namespace}-$ {pvcName}-$ {pvName}的命名格式提供(在NFS服务器上);
c.PV回收的时候以 archieved-$ {namespace}-$ {pvcName}-$ {pvName} 的命名格式
(在NFS服务器上);

d.nfs-client-provisioner源码地址:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client

每个存储类都有一个provisioner,用来决定使用哪个卷插件制备PV。

目前,NFS没有内部制备器,需要使用外部制备器插件来实现PV的动态供给。

官方内部支持的制备器参见:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#provisioner
NFS外部制备器插件参考:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
官方内部支持的制备器参见:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#provisioner
NFS外部制备器插件参考:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

NFS provisioner插件部署:

[root@k8s-master nfs-storageClass]# ls
class.yaml deployment.yaml rbac.yaml
#修改NFS服务器地址和共享文件路径
[root@k8s-master nfs-storageClass]# grep NFS deployment.yaml
- name: NFS_SERVER
- name: NFS_PATH

#授权访问api-server
[root@k8s-master nfs-storageClass]# kubectl apply -f rbac.yaml
#部署nfs provisioner插件
[root@k8s-master nfs-storageClass]# kubectl apply -f deployment.yaml
#创建存储类
[root@k8s-master nfs-storageClass]# kubectl apply -f class.yaml

#查看存储类
[root@k8s-master nfs-storageClass]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 2m3s
注:部署配置文件下载地址为 https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy

NFS StorageClass动态供给PV

使用nfs存储类来实现动态供给PV卷(pod-scnfs.yaml)。

---
apiVersion: v1
kind: Pod
metadata:
name: pod-sc-nfs
spec:
containers:
- name: pod-sc-nfs
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: sc-nfs-pvc
mountPath: /usr/share/nginx/html
volumes:
- name: sc-nfs-pvc #与volumeMounts.name保持一致
persistentVolumeClaim:
claimName: test-nfs-scclaim

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-nfs-scclaim #与claimName保持一致
spec:
storageClassName: "managed-nfs-storage" #与部署nfs provisioner插件时class.yaml中metadata.name保持一致
accessModes: #访问模式
- ReadWriteMany
resources:
requests:
storage: 1Gi #容量

无需手动创建PV,检查部署后状态:

[root@k8s-master ~]# kubectl apply -f pod-scnfs.yaml
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-pod 1/1 Running 0 5h40m
nfs-client-provisioner-5d5775b9bb-5657c 1/1 Running 0 21m
pod-sc-nfs 1/1 Running 0 24s
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pvc,pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/my-pvc Bound my-pv 1Gi RWX 5h41m
persistentvolumeclaim/test-nfs-scclaim Bound pvc-14c4221b-f3af-465c-b431-eddc1191a864 1Gi RWX managed-nfs-storage 33s

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/my-pv 1Gi RWX Retain Bound default/my-pvc 5h41m
persistentvolume/pvc-14c4221b-f3af-465c-b431-eddc1191a864 1Gi RWX Delete Bound default/test-nfs-scclaim managed-nfs-storage 33s
[root@k8s-master ~]#

可以看到,动态分配的PV的STORAGECLASS一栏为managed-nfs-storage,回收策略为Delete。

查看NFS Server上的共享文件路径:

[root@k8s-master ~]# kubectl exec -it pod-sc-nfs -- bash
root@pod-sc-nfs:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
...
192.168.136.120:/ifs/kubernetes/default-test-nfs-scclaim-pvc-14c4221b-f3af-465c-b431-eddc1191a864 nfs4 37G 3.7G 34G 10% /usr/share/nginx/html
...
root@pod-sc-nfs:/# cd /usr/share/nginx/html
root@pod-sc-nfs:/usr/share/nginx/html# ls
root@pod-sc-nfs:/usr/share/nginx/html# touch test-nfs-file-001
root@pod-sc-nfs:/usr/share/nginx/html# exit

[root@k8s-node1 ~]# ll /ifs/kubernetes/
total 4
-rw-r--r--. 1 root root 0 Aug 12 23:26 a.txt
-rw-r--r--. 1 root root 0 Aug 12 23:29 b.txt
drwxrwxrwx. 2 root root 6 Aug 13 05:23 default-test-nfs-scclaim-pvc-14c4221b-f3af-465c-b431-eddc1191a864
-rw-r--r--. 1 root root 20 Aug 12 23:49 index.html
[root@k8s-node1 ~]# ls /ifs/kubernetes/default-test-nfs-scclaim-pvc-14c4221b-f3af-465c-b431-eddc1191a864/
test-nfs-file-001

测试Delete回收策略是否会生效:

[root@k8s-master ~]# kubectl delete -f pod-scnfs.yaml
pod "pod-sc-nfs" deleted
persistentvolumeclaim "test-nfs-scclaim" deleted

#查看绑定的PV是否一并被删除
[root@k8s-master ~]# kubectl get pvc,pv,sc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/my-pvc Bound my-pv 1Gi RWX 5h55m

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/my-pv 1Gi RWX Retain Bound default/my-pvc 5h55m

NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
storageclass.storage.k8s.io/managed-nfs-storage k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 36m

#查看NFS Server上的共享文件路径是否同时被删除
[root@k8s-node1 ~]# ls /ifs/kubernetes/
a.txt b.txt index.html

如果想在删除PVC时,对PV中的数据进行归档,可以将nfs provisioner插件部署配置文件class.yaml中的参数

archiveOnDelete: "false"

修改为true后重新部署。这样NFS Server上共享文件路径中的数据就会留存一份归档。

References

【1】https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/

【2】https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/

【3】https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

【4】https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/