k8s 持久化存储-常见的存储卷介绍

Posted 笨小孩@GF 知行合一

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了k8s 持久化存储-常见的存储卷介绍相关的知识,希望对你有一定的参考价值。

  • 在 k8s 中为什么要做持久化存储?

  • 在 k8s 中部署的应用都是以 pod 容器的形式运行的,假如部署 mysql、Redis 等数据库,需要对这些数据库产生的数据做备份。因为 Pod 有生命周期,如果 pod 不挂载数据卷,那 pod 被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到 pod 数据持久化存储。
  • k8s 持久化存储方案-emptyDir
  • #查看 k8s 支持哪些存储
    [~]# kubectl explain pods.spec.volumes
    KIND: Pod
    VERSION: v1
    RESOURCE: volumes <[]Object>
    DESCRIPTION:
     List of volumes that can be mounted by containers belonging to the pod.
     More info: https://kubernetes.io/docs/concepts/storage/volumes
     Volume represents a named volume in a pod that may be accessed by any
     container in the pod.
    FIELDS:
     awsElasticBlockStore<Object>
     azureDisk <Object>
     azureFile <Object>
     cephfs <Object>
     cinder <Object>
     configMap <Object>
     csi <Object>
     downwardAPI <Object>
     emptyDir <Object>
     ephemeral <Object>
     fc <Object>
     flexVolume <Object>
     flocker<Object>
     gcePersistentDisk <Object>
     gitRepo <Object>
     glusterfs <Object>
     hostPath <Object>
     iscsi <Object>
     name <string> -required-
     nfs <Object>
     persistentVolumeClaim <Object>
     photonPersistentDisk <Object>
     portworxVolume <Object>
     projected <Object>
     quobyte <Object>
     rbd <Object>
     scaleIO <Object>
     secret <Object>
     storageos <Object>
     vsphereVolume <Object>
  • 常用的如下:
    emptyDir 
    hostPath 
    nfs
    persistentVolumeClaim
    glusterfs
    cephfs
    configMap
    secret
  • 想要使用存储卷,需要经历如下步骤
    1、定义 pod 的 volume,这个 volume 指明它要关联到哪个存储上的
    2、在容器中要使用 volume mounts 挂载对应的存储
    经过以上两步才能正确的使用存储卷
  • emptyDir 类型的 Volume 是在 Pod 分配到 Node 上时被创建,Kubernetes 会在 Node 上自动分配一个目录,因此无需指定宿主机 Node 上对应的目录文件。 这个目录的初始内容为空,当 Pod 从Node 上移除时,emptyDir 中的数据会被永久删除。

    emptyDir Volume 主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。
  • 创建一个 pod,挂载临时目录 emptyDir
    参考:https://kubernetes.io/docs/concepts/storage/volumes#emptydir
    vim emptydir.yaml 

    查看本机临时目录存在的位置,可用如下方法:
    #查看 pod 调度到哪个节点以及 pod 的 uid

    在 k8s-01 上查看

    由上可知,临时目录在本地的/var/lib/kubelet/pods/191812ff-2111-491f-9611-a6cdc0a03023/volumes/kubernetes.io~empty-dir/cache-volume/下
     

  • k8s 持久化存储方案-hostPath

  • hostPath Volume 是指 Pod 挂载宿主机上的目录或文件。

  • hostPath Volume 使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在 pod 被删除,这个存储卷还是存在的,不会被删除,所以只要同一个 pod 被调度到同一个节点上来,在 pod 被删除重新被调度到这个节点之后,对应的数据依然是存在

  • #查看 hostPath 存储卷的用法
    [~]# kubectl explain pods.spec.volumes.hostPath
    KIND: Pod
    VERSION: v1
    RESOURCE: hostPath <Object>
    DESCRIPTION:
     HostPath represents a pre-existing file or directory on the host machine
     that is directly exposed to the container. This is generally used for
     system agents or other privileged things that are allowed to see the host
     machine. Most containers will NOT need this. More info:
     https://kubernetes.io/docs/concepts/storage/volumes#hostpath
     Represents a host path mapped into a pod. Host path volumes do not support
     ownership management or SELinux relabeling.
    FIELDS:
     path <string> -required-
     type <string>

    参考:https://kubernetes.io/docs/concepts/storage/volumes#hostpath 

  • #创建一个 pod,挂载 hostPath 存储卷
    vim  hostpath.yaml 
     

    # DirectoryOrCreate 表示本地有/data1 目录,就用本地的,本地没有就会在 pod 调度到的节点自动创建一个
    查看 pod 被调度到了哪个节点,然后查看相关目录是否存在
     

     #在 k8s-01 上的/data1 下创建一个目录

    #测试存储卷是否可以正常使用,登录到 nginx 容器 和 tomcat 容器

     #通过上面测试可以看到,同一个 pod 里的 test-nginx 和 test-tomcat 这两个容器可以共享存储卷

  • hostpath 存储卷缺点:
    单节点 pod 删除之后重新创建必须调度到同一个 node 节点,数据才不会丢失
    可以用分布式存储:nfs,cephfs,glusterfs

  • k8s 持久化存储方案-NFS

  • 上节说的 hostPath 存储,存在单点故障,pod 挂载 hostPath 时,只有调度到同一个节点,数据才不会丢失。那可以使用 nfs 作为持久化存储。
  • 搭建 nfs 服务,以 k8s 的控制节点作为 NFS 服务端并创建 NFS 需要的共享目录

  • 在其他 k8s 节点上也安装 nfs并测试手动挂载后卸载:

  • #创建 Pod,挂载 NFS 共享出来的目录
    []# vim nfs.yaml 

    注: path: /data/volumes    # nfs 的共享目录
            server:192.168.2.30 是 k8s-master1 机器的 ip, nfs 服务器地址

    在 nfs 共享目录创建一个 index.html 测试能否正常访问

     #上面说明挂载 nfs 存储卷成功了,nfs 支持多个客户端挂载,可以创建多个 pod,挂载同一个 nfs 服务器共享出来的目录;

    但是 nfs 如果宕机了,数据也就丢失了,所以需要使用分布式存储,常见的分布
    式存储有 glusterfs 和 cephfs

  • k8s 持久化存储方案-PVC(PersistentVolumeClaim)

  • 参考官网:
    https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

  • K8s PV (PersistentVolume)是什么?

  • PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像 pod 是 k8s 集群资源一样。 PV 是容量插件,如 Volumes,其生命周期独立于使用 PV的任何单个 pod。

  • K8s PVC 是什么?

  • PersistentVolumeClaim(PVC)是一个持久化存储卷,在创建 pod 时可以定义这个类型的存储卷。 它类似于一个 pod。

  • pod 消耗节点资源,PVC 消耗 PV 资源

  • pod 可以请求特定级别的资源(CPU 和内存)。 pvc 在申请 pv 的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。

  • K8s PVC 和 PV 工作原理

  • PV 是群集中的资源。 PVC 是对这些资源的请求。

  • PV 和 PVC 之间的相互作用遵循以下生命周期:
    (1)pv 的供应方式
    可以通过两种方式配置 PV:静态或动态。
    静态的:
    集群管理员创建了许多 PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API 中,可供使用。
    动态的:
    当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,群集可能会尝试为 PVC 专门动态配置卷。此配置基于 StorageClasses,PVC 必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。
    (2)绑定
    用户创建 pvc 并指定需要的资源和访问模式。在找到可用 pv 之前,pvc 会保持未绑定状态
    (3)使用
    a)需要找一个存储服务器,把它划分成多个存储空间;
    b)k8s 管理员可以把这些存储空间定义成多个 pv;
    c)在 pod 中使用 pvc 类型的存储卷之前需要先创建 pvc,通过定义需要使用的 pv 的大小和对应的访问模式,找到合适的 pv;
    d)pvc 被创建之后,就可以当成存储卷来使用了,我们在定义 pod 时就可以使用这个 pvc 的存储卷
    e)pvc 和 pv 它们是一一对应的关系,pv 如果被 pvc 绑定了,就不能被其他 pvc 使用了;
    f)我们在创建 pvc 的时候,应该确保和底下的 pv 能绑定,如果没有合适的 pv,那么 pvc 就会处于 pending 状态。
    (4)回收策略
    当创建 pod 时如果使用 pvc 做为存储卷,那么它会和 pv 绑定,当删除 pod,pvc 和 pv 绑定就会解除,解除之后和 pvc 绑定的 pv 卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:
    Retain
    Recycle (不推荐使用,1.15 可能被废弃了)
    Delete
    1、Retain
    当删除 pvc 的时候,pv 仍然存在,处于 released 状态,但是它不能被其他 pvc 绑定使用,里面的数据还是存在的,当下次再使用的时候,数据还是存在的,这个是默认的回收策略
    Delete
    删除 pvc 时即会从 Kubernetes 中移除 PV,也会从相关的外部设施中删除存储资产

  • 创建 pod,使用 pvc 作为持久化存储卷

  • 创建 nfs 共享目录
    #在宿主机创建 NFS 需要的共享目录
     

  • #查看定义 pv 需要的字段
    [~]# kubectl explain pv 
    KIND: PersistentVolume
    VERSION: v1
    DESCRIPTION:
     PersistentVolume (PV) is a storage resource provisioned by an
     administrator. It is analogous to a node. More info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes
    FIELDS:
     apiVersion <string>s
     kind <string>
     metadata <Object>
     spec <Object>

    #查看定义 nfs 类型的 pv 需要的字段
    [~]# kubectl explain pv.spec.nfs
    KIND: PersistentVolume
    VERSION: v1
    RESOURCE: nfs <Object>
    FIELDS:
     path <string> -required-
     readOnly <boolean>
     server <string> -required

  • 创建 pv
    vim pv.yaml

     #STATUS 是 Available,表示 pv 可用

  • 创建 pvc,和符合条件的 pv 绑定
    vim  pvc.yaml

    pvc 的名字-绑定到 pv-绑定的是 v2 这个 pv-pvc 可使用的容量是 2G
    #STATUS 是 Bound,表示这个 pv 已经被 my-pvc 绑定了

  • 创建 pod,挂载 pvc
    [~]# vim pod_pvc.yaml

    注:使用 pvc 和 pv 的注意事项
    1、每次创建 pvc 的时候,需要事先有划分好的 pv,这样可能不方便,那么可以在创建 pvc 的时候直接动态创建一个 pv 这个存储类,pv 事先是不存在的
    2、pvc 和 pv 绑定,如果使用默认的回收策略 retain,那么删除 pvc 之后,pv 会处于 released 状态,想要继续使用这个 pv,需要手动删除 pv,kubectl delete pv pv_name,删除 pv,不会删除pv 里的数据,当重新创建 pvc 时还会和这个最匹配的 pv 绑定,数据还是原来数据,不会丢失。

  • k8s 存储类:storageclass

  • 上面介绍的 PV 和 PVC 模式都是需要先创建好 PV,然后定义好 PVC 和 pv 进行一对一的 Bond,但是如果 PVC 请求成千上万,那么就需要创建成千上万的 PV,对于运维人员来说维护成本很高,Kubernetes 提供一种自动创建 PV 的机制,叫 StorageClass,它的作用就是创建 PV 的模板
  • k8s 集群管理员通过创建 storageclass 可以动态生成一个存储卷 pv 供 k8s pvc 使用。
  • 每个 StorageClass 都包含字段 provisioner,parameters 和 reclaimPolicy。

    具体来说,StorageClass 会定义以下两部分:
    1、PV 的属性 ,比如存储的大小、类型等;
    2、创建这种 PV 需要使用到的存储插件,比如 Ceph、NFS 等


    有了这两部分信息,Kubernetes 就能够根据用户提交的 PVC,找到对应的 StorageClass,然后Kubernetes 就会调用 StorageClass 声明的存储插件,创建出需要的 PV。

    #查看定义的 storageclass 需要的字段
    ~]# kubectl explain storageclass
    KIND: StorageClass
    VERSION: storage.k8s.io/v1
    DESCRIPTION:
     StorageClass describes the parameters for a class of storage for which
     PersistentVolumes can be dynamically provisioned.
     StorageClasses are non-namespaced; the name of the storage class according
     to etcd is in ObjectMeta.Name.
    FIELDS:
     allowVolumeExpansion <boolean>
     allowedTopologies<[]Object>
     apiVersion <string>
     kind <string>
     metadata<Object>
     mountOptions <[]string>
     parameters <map[string]string>
     provisioner <string> -required-
     reclaimPolicy <string>
     volumeBindingMode <string>

    provisioner:供应商,storageclass 需要有一个供应者,用来确定使用什么样的存储来创建 pv,常见的 provisioner 如下
    (https://kubernetes.io/zh/docs/concepts/storage/storage-classes/):

    provisioner 既可以由内部供应商提供,也可以由外部供应商提供,如果是外部供应商可以参考https://github.com/kubernetes-incubator/external-storage/下提供的方法创建。

    以 NFS 为例,要想使用 NFS,需要一个 nfs-client 的自动装载程序,称之为 provisioner,这个程序会使用已经配置好的 NFS 服务器自动创建持久卷,也就是自动帮我们创建 PV。

    reclaimPolicy:回收策略
    allowVolumeExpansion:允许卷扩展,PersistentVolume 可以配置成可扩展。将此功能设置为true 时,允许用户通过编辑相应的 PVC 对象来调整卷大小。当基础存储类的allowVolumeExpansion 字段设置为 true 时,以下类型的卷支持卷扩展。
  • 安装 nfs provisioner,用于配合存储类动态生成 pv

  • 创建运行 nfs-provisioner 需要的 sa 账号
    vim serviceaccount.yaml

    扩展:什么是 sa?
    sa 的全称是 serviceaccount。
    serviceaccount 是为了方便 Pod 里面的进程调用 Kubernetes API 或其他外部服务而设计的。
    指定了 serviceaccount 之后,把 pod 创建出来,在使用这个 pod 时,这个 pod 就有了指定的账户的权限

    对 sa 授权
    kubectl create clusterrolebinding nfs-provisioner --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner

    安装 nfs-provisioner 程序
    # mkdir /data/nfs_pro -p
    #把/data/nfs_pro 变成 nfs 共享的目录
    vim /etc/exports

    创建 pod
    vim nfs-deployment.yaml 

    创建 storageclass,动态供给 pv
    vim nfs-storageclass.yaml

     #显示内容如上,说明 storageclass 创建成功了
    注意:provisioner 处写的 example.com/nfs 应该跟安装 nfs provisioner 时候的 env 下的PROVISIONER_NAME 的 value 值保持一致,如下:
    env:
     - name: PROVISIONER_NAME
     value: example.com/nfs

    创建 pvc,通过 storageclass 动态生成 pv
    vim claim.yaml

     

     #通过上面可以看到 test-claim1 的 pvc 已经成功创建了,绑定的 pv 是 pvc-99d5a561-584b-43dc-8d11-4862f4362589,这个 pv 是由 storageclass 调用 nfs provisioner 自动生成的。

  • 步骤总结:
    1、供应商:创建一个 nfs provisioner
    2、创建 storageclass,storageclass 指定刚才创建的供应商
    3、创建 pvc,这个 pvc 指定 storageclass

 

  • 创建 pod,挂载 storageclass 动态生成的 pvc:test-claim1
    vim read-pod.yaml 

以上是关于k8s 持久化存储-常见的存储卷介绍的主要内容,如果未能解决你的问题,请参考以下文章

从零开始入门 K8s | 应用存储和持久化数据卷:核心知识

从入门到上手:什么是K8S持久卷?

从入门到上手:什么是K8S持久卷?

k8s数据持久化(存储卷)

应用存储和持久化数据卷:核心知识

部署k8s集群存储-数据卷