Kubernetes / 云计算 / 存储系统

Kubernetes 存储卷详细介绍

温馨提示:本文最后更新于2021-08-05 22:59:10,某些文章具有时效性,若有错误或已失效,请在下方留言或提交工单提交工单
浅时光 · 9月27日 · 2021年 · 本文7851个字,预计阅读20分钟 2890次已读

一、场景说明


在我们实际的业务项目中,我们会根据应用程序本身是否需要持久存储数据,以及某一次请求跟此前请求是否有关联性,我们可以分为四类应用:

  • (1)、有状态有持久存储
  • (2)、有状态无持久存储
  • (3)、无状态有持久存储
  • (4)、无状态无持久存储

注:大多数跟数据存储服务相关的应用和有状态应用,我们都需要进行数据持久化存储数据;

容器本身有生命周期,为了使得容器将来终结以后,可以把它删除甚至是编排到其他节点上去运行,那就意味着我们数据不能放在容器本地,不能放在容器自己的名称空间中;

在K8S中,Pod运行在某一节点上,只要节点不出故障则会一直运行在此节点上(如Pod中容器有问题,无非就是Pod一直重启而已,但是不会调度到其他节点),只有当该节点出现了故障才会重新调度到其它节点上去;

如上所说,如果一个节点出现了故障,那么这个Pod则会调度到其他节点,这个Pod就会编排到其他节点上去运行,因此我们把数据放在容器自身的存储空间中,数据会随着Pod的终结而删除,为了突破Pod生命周期受限的这种现状,我们需要把数据放在Pod自有文件系统之外的地方进行存储数据。

在Docker中我们可以把数据放在对应的节点上,我们使用存储卷,相当于把容器中的某一目录与节点上的某一目录建立映射关系(或者关联关系),随后容器中的数据则会存储到该节点的目录下,当我们删除容器后,只要节点上的卷不受影响,那么我们重新起个容器并与之前的卷进行映射那么这个容器中的数据就没有问题,从而实现数据持久化;但是在K8S中则不能通过上面的方式去操作,原因在于K8S是一个集群,是一个容器编排系统,是由调度器调度的,比如某个节点上的Pod被删除了,那么这个Pod在下一次重新运行时则会进行重新调度,重新调度后不一定还在该节点上,所以一旦别调度到其他节点上,则原先的Pod数据则访问不到,所以像在Docker中通过节点与容器进行数据映射的方式来持久存储数据的逻辑在K8S之上不是真正意义上的持久化存储(只能代表短暂的持久化,Pod一直保留在当前节点);所以为了实现更强大的持久存储,我们应该使用脱离节点而使用的存储设备共享存储设备。

二、存储卷类型


对Pod来讲,它有一特点,同一Pod中的多个容器可共享访问同一组存储卷,因为对K8S来讲,存储卷不属于容器而是属于Pod,我可以在容器中挂载,如果2个容器都挂载了,则代表2个容器共享数据了;

Pod底层有一个基础容器(不启动,称为基础架构容器),而且是靠一个独特的镜像来创建的,这个镜像叫做 pause 一个很小的镜像,我们创建Pod,而它是Pod的根,所有Pod的网络空间等都是分配给pause的,Pod中运行的主容器是共享的pause的网络空间的,而容器挂载存储卷,其实也是复制了pause的存储卷而已。那么同一个Pod中的所有容器共享pause这个容器的网络名称空间,所以这也就是为什么同一Pod中的所有容器它们可以使用同一个地址,共享同一个TCP/IP协议栈,使用同一组主机名的原因。

1、empDir

介绍

       empDir(空目录),只在节点本地使用,一旦Pod删除,那么该存储卷也会一并删除;而它则是用来做临时目录,临时数据存储,或当做缓冲使用,因为empDir背后关联的这个宿主机的目录可以是宿主机的内存,把内存批出来一块当做目录给empDir使用。

  • empDir生命周期同Pod生命周期
  • 注:该存储卷没有任何持久化存储的功能

范例

  • empDir作为范例,以下是单一容器,如果有多个容器,哪个容器使用存储卷,那么容器就需要挂载
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:	#容器级别,指明容器中挂载到哪个目录
    - mountPath: /cache	#挂载的目录
      name: cache-volume	#名称
  volumes:	#注意:与containers同级
  - name: cache-volume	#定义存储卷名称
emptyDir: {}  #定义存储卷类型,{}表示映射,空

2、hostPath

介绍

       主机路径存储,与Docker上的数据存储意义一样,但是也不具有真正意义上的持久化存储。

hostPath简单来讲,就是把Pod所在的宿主机之上的,脱离Pod中容器的名称空间之外的宿主机的文件系统中某一目录与Pod建立关联关系;在Pod被删除时,这个存储卷是不会被删除的,所以在Pod被删除以后,重构的Pod只要能够调度在同一节点上那么对应的数据依然存在。

如果要使用hostPath就需要在宿主机上指明路径,如果指明的路径在宿主机上并不存在,那么则是根据hostPath定义的类型决定是新建还是不新建

  • 注:该存储卷没有任何持久化存储的功能
  • hostPath 卷能将主机节点文件系统上的文件或目录挂载到您的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。
例如,hostPath 的一些用法有:
  • 运行一个需要访问 Docker 引擎内部机制的容器;请使用 hostPath 挂载 /var/lib/docker 路径。
  • 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys
  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

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

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

范例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd	#容器挂载的路径
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 宿主机上的目录位置
      path: /data	#宿主机上的路径
      # 此字段是可选的
      type: Directory	#定义类型为宿主机上该目录必须存在

3、网络文件存储

第一类:传统存储设备

1、介绍:

本地的SAN(网络附加存储)、NAS(存储区域网络);在NAS中常见的用法协议:NFS,cifs;SAN中用的比较多的协议有:iSCSI

以上两种都是脱离主机节点本地的网络存储设备

注意:虽然NFS可以实现数据的持久化存储,但是由于NFS并不是一个分布式存储,是没有冗余能力的,当NFS服务挂掉之后数据也就挂了。

2、范例:
  • 比如通过NFS做文件共享存储系统;
  • 前提需要各节点安装了nfs-utils程序
  • 通过以下命令查看NFS配置要求
kubectl explain pods.spec.volumes.nfs
参数选项:
   path	<string> 	#必需服务,NFS服务器导出的路径
   readOnly	<boolean>	#在此处指定强制以只读权限挂载NFS导出,默认为false
   server	<string>	#必需服务,NFS服务器的主机名或IP地址


apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: xxxx.com/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/		#映射到容器中的路径
  volumes:
  - name: html
    nfs:
      path: /data/volumes	#NFS服务器上导出的路径目录
      server: 192.168.66.150	#NFS服务器地址

第二类:分布式存储

1、介绍:

它们有可能是块级别,或文件系统级别;

  • GlusterFS、ceph(RBD:块存储)、cephFS(文件系统存储)
  • 注意:线上环境建议使用分布式存储,推荐使用cephFS,ceph本身输出的就是RestFull风格的接口,可以被K8S直接使用;但GlusterFS需要额外部署一个专有接口Heketi,以便提供RestFull接口,供K8S所使用

cephfs 允许您将现存的 CephFS 卷挂载到 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,cephfs 卷的内容在删除 Pod 时会被保留,卷只是被卸载掉了。 这意味着 CephFS 卷可以被预先填充数据,并且这些数据可以在 Pod 之间“传递”。CephFS 卷可同时被多个写者挂载。

  • 注意: 在您使用 Ceph 卷之前,您的 Ceph 服务器必须正常运行并且要使用的 share 被导出(exported)
2、范例:
  • cephFS作为分布式存储,作为范例
apiVersion: v1
kind: Pod
metadata:
  name: cephfs
spec:
  containers:
  - name: cephfs-rw
    image: kubernetes/pause
    volumeMounts:
    - mountPath: "/mnt/cephfs"	#Pod容器中映射的路径
      name: cephfs
  volumes:
  - name: cephfs
    cephfs:
      monitors:
      - 192.168.66.61:6789	#ceph服务集群地址
      - 192.168.66.62:6789
      - 192.168.66.63:6789
      # 默认情况下,路径是/,也可以通过path参数指定
      # path: /some/path/in/side/cephfs
      user: cephfs	#ceph客户端用户
      secretFile: "/etc/ceph/ceph-secret"	#用户密码文件存储路径
      readOnly: true

第三类:云存储

第三方云厂商的各类存储设备;但是这种只适用于一种场景,就是你的K8S集群是托管在它们的公有云上的

三、PV和PVC介绍使用


1、介绍:

PVC关键是要与PV建立关联关系,PV关键是要与存储卷建立关联关系

存储类:按照存储系统各种或者某一性能指标的关注而对存储系统做的分类

Kubernetes 存储卷详细介绍-浅时光博客
上图说明:

在Pod中我们只需要定义存储卷就可以了,定义的时候指明要使用多大的存储卷;这个存储卷叫PVC类型的存储卷,而PVC类型的存储卷必须与当前名称空间中的PVC建立直接绑定关系,而PVC必须与PV建立绑定关系,而PV则是某个设备上的真正存储空间。

PVC和PV也是K8S上的一种标准资源;真正存储设备上需要先将存储空间划分好,然后将存储空间绑定到各个PV,并创建PVC与之PV进行绑定,最后在Pod中定义好PVC和容量大小;

注意:在PVC没有被调用时,PV是不会与PVC进行绑定的,只有在Pod中定义了PVC时,PVC和PV才会进行绑定;PVC和PV是一一对应的,如果当前PV被绑定了,那么这个PV则不能再被其他PVC进行绑定;但是PVC一旦被创建和使用,则可以被多个Pod所访问使用(创建Pod时可以定义PVC的访问模式)

Kubernetes 存储卷详细介绍-浅时光博客

2、范例:

(1)NFS服务器创建多个存储目录
~]# mkdir -p /data/volumes/v{1,2,3,4,5}
~]# vim /etc/exports
/data/volumes/v1        *(rw,no_root_squash)
/data/volumes/v2        *(rw,no_root_squash)
/data/volumes/v3        *(rw,no_root_squash)
/data/volumes/v4        *(rw,no_root_squash)
/data/volumes/v5        *(rw,no_root_squash)

~]# systemctl restart nfs
~]# exportfs -arv
exporting *:/data/volumes/v5
exporting *:/data/volumes/v4
exporting *:/data/volumes/v3
exporting *:/data/volumes/v2
exporting *:/data/volumes/v1

~]# showmount -e
Export list for k8s-master3:
/data/volumes/v5        *
/data/volumes/v4        *
/data/volumes/v3        *
/data/volumes/v2        *
/data/volumes/v1        *
(2)K8S上创建PV
#查看定义参数
kubectl explain pv
kubectl explain pv.spec
kubectl explain pv.spec.nfs

#注意:定义PV时不要划分名称空间,因为PV属于集群级别的不属于名称空间
#定义一个NFS格式的PV 
~]# vim pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
server: 192.168.66.60
  #访问模式
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  #指定存储空间大小
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
server: 192.168.66.60
accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: 192.168.66.60
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: 192.168.66.60
  accessModes: ["ReadWriteMany"]
  capacity:
    storage: 5Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: 192.168.66.60
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
storage: 3Gi

#执行apply创建pv
~]# kubectl apply -f pv-demo.yaml 
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created


~]# kubectl get pv
(3)K8S上创建PVC
  • 注意:PVC需要定义namespace
~]# vim pod-vol-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-app
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-pvc
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx:1.16
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: pvc-app	 #使用pvc,这里写创建的pvc的name值

~]# kubectl apply -f pod-vol-pvc.yaml
  • 检查绑定状态
~]# kubectl get pv
Kubernetes 存储卷详细介绍-浅时光博客
Kubernetes 存储卷详细介绍-浅时光博客

相关知识点:

访问模式

PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为 对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器 上以只读的方式导出。每个 PV 卷都会获得自身的访问模式集合,描述的是 特定 PV 卷的能力。

访问模式有:

  • ReadWriteOnce:卷可以被一个节点以读写方式挂载;
  • ReadOnlyMany:卷可以被多个节点以只读方式挂载;
  • ReadWriteMany:卷可以被多个节点以读写方式挂载。

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

  • RWO:ReadWriteOnce
  • ROX:ReadOnlyMany
  • RWX:ReadWriteMany
回收策略

目前的回收策略有:

  • Retain:手动回收,保留
  • Recycle:基本擦除 (rm -rf /thevolume/*)
  • Delete:诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除
  • 目前,仅 NFS 和 HostPath 支持回收(Recycle)。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete)

相关参考官方文档

持久卷官方文档:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/
容量:https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/resources.md




本文作者:浅时光
原文链接:https://www.dqzboy.com/8591.html
版权声明:知识共享署名-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)协议进行许可
转载时请以超链接形式标明文章原始出处和作者信息

0 条回应

必须 注册 为本站用户, 登录 后才可以发表评论!

    本站已稳定运行: | 耗时 0.618 秒 | 查询 10 次 | 内存 38.18 MB