关于 Kubernetes 系列其他文章的传送门
-
《 k8s 中的存储系统 – Volumes 》 您当前所在位置

一、k8s 中的存储卷¶
存储卷是被挂载到 Pod 内的基础容器 pause 上,当这个 Pod 内的容器需要使用存储卷时,指明要挂载 pause 上的哪个存储卷即可。多个容器可同时挂载同一个 pause 上的存储卷,将共享使用。
不同的存储设备,访问的方式也不一样,一个节点中所有运行的容器是共享物理节点内核的,而连接存储的驱动是运行在内核中,所以首先要确保节点自己能够连接到存储设备,这个节点上的容器才能够连接到存储设备
当 pause 连接存储设备时,要明确指明让 pause 使用哪种类型的存储设备,并与对应类型的存储设备的访问接口相匹配才行,因为真正去驱动,并调用存储的客户端不是物理节点的内核,内核只是负责把二者之间桥接起来而已,而真正调存储服务的那一方,必须要能够成为存储设备的客户端才可以。
市面上的存储种类众多,k8s 自己已经内置了很多常见存储设备的客户端插件,如果内置的插件都不合适的话,可以使用自定义接口 CSI:Ccontainer Storage Interface
hostPath:定义本地存储目录的相关信息:
path:在宿主机的哪个路径下;type:在 k8s 中,创建存储卷时,如果本地的目录不存在,要使用 type 来定义该如何操作,可选的方式有:Directory:这个目录必须存在且是目录,不然就报错;DirectoryOrCreate:如果目录不存在就自动新建;File:这个文件必须存在且是目录,不然就保存;文件可以单独挂载;FileOrCreate:如果文件不存在就自动创建;
Pod 的存储卷都是挂载到 pause 容器中的,其他容器如果需要使用,从 pause 容器中挂载到自己的容器内:
volumeMounts:定义挂载 pause 容器的卷存储时的参数;mountPath:指定容器内的挂载点;name:要挂载哪一个名称的存储卷;readOnly:定义是否只读,默认false为读写;
使用节点级存储卷的示例¶
1 | apiVersion: v1 |
Pod 被调度到哪个主机,存储卷就被创建在哪个主机上;在目录中创建一个测试页面:
1 | echo "test.page" > /volumes/myapp/index.html |
这种方式只能提供节点性的持久性,以后这个 Pod 必须运行在这个节点上,才能使用这个文件,如果调度到其他节点,就无法访问。可以在定义 Pod 时使用 nodeName 明确告诉调度器只能调度到指定的节点上,不过不建议使用。
Local 类型¶
官方介绍:https://kubernetes.io/zh/docs/concepts/storage/volumes/#local
local 卷,可以挂载目录,还可以挂载某个本地存储设备,例如磁盘、分区。
定义方式:
1 | local: |
emptyDir 类型¶
这种类型的存储属于临时性的,Pod 一删除就没有了。主要用于:
- Pod 中需要缓存时,可以通过此种类型把缓存目录映射到节点上的 SSD 盘;甚至可以在节点的内存中,划分一部分映射到 Pod 中作为缓存使用;
- 当 Pod 没有持久存储的需求,但是一个 Pod 中的两个容器需要共享部分数据,就借用此类型的存储,因为一个 Pod 内不同的容器并不共享 Mount 名称空间。
参数¶
medium:none:空值,不定义则使用本地磁盘;Memory:使用内存作为存储:sizeLimit:当使用 Memory 存储时,定义最大可使用容量,不定义则无限;
配置示例¶
1 | -> # vim vol-emptydir.yaml |
二、使用外部存储配置示例¶
常见使用 ceph,这里使用 nfs 作为演示。
部署 nfs¶
注意:在 master 节点配置 nfs
先在 master 节点部署 nfs
1 | yum install nfs-utils -y |
创建子目录来模拟存储设备:
1 | mkdir /vols/v{1,2,3,4,5} -p |
定义可以把目录导出给哪个网络的设备使用:
1 | vim /etc/exports |
确认 2049 端口已启用并可以正常访问。
注意:在客户端配置
在每个 node 节点都安装 nfs,并挂载 master 的 nfs 目录,测试是否可以正常使用,之后要取消挂载,只是测试一下:
1 | yum install nfs-utils -y |
Pod 使用 nfs 存储的配置示例¶
参数:
path:nfs 服务器上导出的目录是什么;server:nfs 服务器的地址和端口;readOnly:挂载的 nfs 是否只读。
1 | -> # vim vol-nfs.yaml |
这样创建的 Pod,无论被调度到哪个节点,只要物理节点能正常连接至 nfs 服务器,那么数据就可以跨节点持久保存。如果使用 Ceph 时,只是配置方式不一致。
三、存储卷中的一些概念¶
PV¶
为了让任何人包括没有存储知识的人方便使用,实现消费者和提供者模型,k8s 在 spec.volumes 和 后端存储 之间,加一个中间层,把存储系统提供的存储能力给抽象成 k8s 中标准类型的资源,称之为 PV。
一个 PV 对应存储系统内可提供的最小的逻辑资源,如果是 nfs,应该为一个导出的目录。
pvc 的绑定(Bind)¶
PV 在 k8s 中是属于集群级别的资源,不属于任何一个名称空间,但是 Pod 是名称空间级别的资源,所以要在某个名称空间内的 Pod 使用 PV,就要把 PV 注册到对应的名称空间中去,而且是 1 对 1 的,比如在 A 名称空间内使用了,就不能在 B 名称空间内使用。
注册方法:在 Pod 所在的名称空间内创建一个 pvc(persistenVolumeClaim) 来请求占住 pv,当 pv 被这个 pvc 关联后,就不能被其他 pvc 关联了,这种方式被称为 Bind。之后 Pod 就可以使用 pvc,再通过 pvc 去使用 pv。
至于同一个名称空间内的不同 Pod 是否可以 Bind 到同一个 pvc,取决于后端存储是否支持多个客户端多路读写访问。
- ReadWriteOnce,简称 RWO:单路读写;
- ReadOnlyMany,简称 ROX:多路只读;
- ReadWriteMany,简称 RWX:多路读写;
当配置 Pod 时,存储卷类型为 persistenVolumeClaim:
persistenVolumeClaim:使用 PVC 类型的存储卷:claimName:指明要使用的 PVC 的名字;readOnly:是否为只读的。
PV 的回收策略(Reclaim)¶
当删除 pvc 后,应该怎么处理 pv,使用 pv 的回收策略(reclaim)来定义:
- 删除(Delete):删除 pvc 时,对应的 pv 也一并删除;
- 空间回收(Recycle):删除 pvc 时,对应的 pv 上的数据全部删除;已废弃;
- 保持不动(Return):删除 pvc 后,对应的 pv 保持原样。
PV 的供给(Provisioning)¶
PV 如何供给,有两种:
- 静态:由管理员手动创建;
- 动态:有两种方式:
- 当用户需要在某一个名称空间当中创建 pvc 的时候,要定义用户的需求,比如要定义使用多大空间。这时根据用户的请求,按需找出存储设备中最合适的存储逻辑单元,并动态的创建成 pv,之后和 pvc 绑定起来。这种方式要求存储设备上要存在多个存储单元,在必要时要能够满足 pvc 的请求。
- 存储系统上没有事先创建存储逻辑单元,只是有存储空间而已。当创建 pvc 时,根据用户的请求,动态的调用存储系统的管理接口(API),临时创建一个刚好满足 pvc 需求的存储单元,再把这个存储单元创建为这个 k8s 集群上的 pv,然后再与 pvc 绑定。
而 pvc 之所以能够在创建时自动触发动态创建供给,是依赖另外一个资源,叫 存储类(Storage Class)
使用前要先在 k8s 之上,把外部的存储系统,先定义为 存储类 类型的资源,之后当创建一个 pvc 时,pvc 不是向存储系统请求资源,而是向 SC 请求,而 SC 上已经配置了如何去连接外部存储设备的 API,之后通过这个 API 创建逻辑单元,并在 SC 的内部创建 pv,所以这样创建的 pv 是属于 SC 的。而如果 pvc 请求一个 pv 是通过 SC 来实现的,那么这个 pvc 也会属于 SC。
所以 k8s 中所有的 pv 和 pvc 可以分为两类,属于某个 SC 的和不属于某个 SC 的。
- pvc 只能向其所在的 SC 内的 pv 请求绑定,不能跨 SC;
- 如果一个 pvc 不属于任何 SC,那么它可以绑定的 pv 也一定不能属于任何 SC。
生命周期(LifeCycle)¶
- Provisioning:供给;
- Binding:绑定;
- Using:使用;
- Reclaiming:回收。
四、使 pv 连接 nfs 配置示例¶
使用外部存储
在 master 节点配置 nfs¶
先在 master 节点部署 nfs
1 | yum install nfs-utils -y |
创建子目录来模拟存储设备:
1 | mkdir /vols/v{1,2,3,4,5,6} -p |
定义可以把目录导出给哪个网络的设备使用:
1 | vim /etc/exports |
确认 2049 端口已启用并可以正常访问。
配置静态供给 PV¶
定义访问模型 accessModes 的参数:
- ReadWriteOnce,简称 RWO:单路读写;
- ReadOnlyMany,简称 ROX:多路只读;
- ReadWriteMany,简称 RWX:多路读写;
注意:不是所有的存储设备都支持创建 PV,比如 Local 类型就不支持。
新建一个 pv 的配置文件,把 nfs 中的 v2 目录创建为 pv;注意:pv 不属于任何名称空间,配置文件中不要包含 namespace 参数:
1 | ~]# vim pv.yaml |
创建并查看:
1 | # kubectl apply -f pv.yaml |
可以看到 pv 的状态为可用,接下来创建 pvc。
定义 PVC¶
1 | ~]# vim pvc.yaml |
创建 ns:
1 | kubectl create ns vol |
创建 pvc:
1 | kubectl apply -f pvc.yaml |
查看创建的 pvc,可以看到匹配到之前创建的 pv 上了,:
1 | kubectl get pvc -n vol |
再回头查看 pv 的状态变为绑定了:
1 | kubectl get pv |
创建 Pod 使用此 pvc¶
1 | -> # vim vol-redis-pvc.yaml |
创建 Pod:
1 | kubectl apply -f vol-redis-pvc.yaml |
测试是否已经使用 pvc:
1 | kubectl exec -it -n vol redis-pvc -- /bin/sh # 连接到 redis-pvc 的交互接口 |
这样就可以使用 pvc 类型的存储卷,实现跨节点的数据持久了。
五、pvc 保护机制¶
在 k8s 1.11 版本之后,增加了 pvc 保护机制:如果删除正在被 Pod 使用的 pvc,删除操作会被延迟并阻塞终端,直到使用此 pvc 的 Pod 被删除时才真的删除。删除 pv 同理,会延迟删除直到关联至此 pv 的 pvc 被删除时才真的删除。