zoukankan      html  css  js  c++  java
  • 8-k8s笔记-共享存储原理

    第8章 共享存储原理
    8.1 共享存储机制概述
    8.2 PV详解
    8.2.1 PV的关键配置参数
    8.2.2 PV生命周期的各个阶段
    8.3 PVC详解
    8.4 PV和PVC的生命周期
    8.4.1 资源供应
    8.4.2 资源绑定
    8.4.3 资源使用
    8.4.4 资源释放
    8.4.5 资源回收
    8.5 StorageClass详解
    8.5.1 StorageClass的关键配置参数
    8.5.2 设置默认的StorageClass
    8.6 动态存储管理实战:GlusterFS
    8.6.1 准备工作
    8.6.2 创建GlusterFS管理服务容器集群
    8.6.3 创建Heketi服务
    8.6.4 为Heketi设置GlusterFS集群
    8.6.5 定义StorageClass
    8.6.6 定义PVC
    8.6.7 Pod使用PVC的存储资源
    8.7 CSI存储机制详解
    8.7.1 CSI的设计背景
    8.7.2 CSI存储插件的关键组件和部署架构
    8.7.3 CSI存储插件的使用示例
    8.7.4 CSI的发展

    8.1 共享存储机制概述
    Kubernetes对于有状态的容器应用或者对数据需要持久化的应用,不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,
    而且需要更加可靠的存储来保存应用产生的重要数据,以便容器应用在重建之后仍然可以使用之前的数据。
    不过,存储资源和计算资源(CPU/内存)的管理方式完全不同。
    为了能够屏蔽底层存储实现的细节,让用户方便使用,同时让管理员方便管理,
    Kubernetes从1.0版本就引入PersistentVolume(PV)和PersistentVolumeClaim(PVC)两个资源对象来实现对存储的管理子系统。
    PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源”,比如Node也是一种容器应用可以“消费”的资源。
    PV由管理员创建和配置,它与共享存储的具体实现直接相关,
    例如GlusterFS、iSCSI、RBD或GCE或AWS公有云提供的共享存储,通过插件式的机制完成与共享存储的对接,以供应用访问和使用。
    PVC则是用户对存储资源的一个“申请”。就像Pod“消费”Node的资源一样,PVC能够“消费”PV资源。PVC可以申请特定的存储空间和访问模式。
    使用PVC“申请”到一定的存储空间仍然不能满足应用对存储设备的各种需求。
    通常应用程序都会对存储设备的特性和性能有不同的要求,包括读写速度、并发性能、数据冗余等更高的要求,
    Kubernetes从1.4版本开始引入了一个新的资源对象StorageClass,用于标记存储资源的特性和性能。
    到1.6版本时,StorageClass和动态资源供应的机制得到了完善,实现了存储卷的按需创建,在共享存储的自动化管理进程中实现了重要的一步。
    通过StorageClass的定义,管理员可以将存储资源定义为某种类别(Class),正如存储设备对于自身的配置描述(Profile),例如“快速存储”“慢速存储”“有数据冗余”“无数据冗余”等。
    用户根据StorageClass的描述就能够直观地得知各种存储资源的特性,就可以根据应用对存储资源的需求去申请存储资源了。
    Kubernetes从1.9版本开始引入容器存储接口Container Storage Interface(CSI)机制,
    目标是在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务,类似于CRI(容器运行时接口)和CNI(容器网络接口)。
    下面对Kubernetes的PV、PVC、StorageClass、动态资源供应和CSI等共享存储管理机制进行详细说明。

    8.2 PV详解
    PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。
    下面的例子声明的PV具有如下属性:
    5GiB存储空间,访问模式为ReadWriteOnce,存储类型为slow(要求在系统中已存在名为slow的StorageClass),
    回收策略为Recycle,并且后端存储类型为nfs(设置了NFS Server的IP地址和路径):
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: pv1
    spec:
    capacity:
    storage: 5Gi
    accessModes:
    - ReadWriteOnce
    storageClassName: slow
    PersistentVolumeReclaimPolicy: Recycle
    nfs:
    server: 172.17.0.2
    image: /tmp
    Kubernetes支持的PV类型如下:
    AWSElasticBlockStore:AWS公有云提供的ElasticBlockStore。
    AzureFile:Azure公有云提供的File。
    AzureDisk:Azure公有云提供的Disk。
    CephFS:一种开源共享存储系统。
    FC(Fibre Channel):光纤存储设备。
    FlexVolume:一种插件式的存储机制。
    Flocker:一种开源共享存储系统。
    GCEPersistentDisk:GCE公有云提供的PersistentDisk。
    Glusterfs:一种开源共享存储系统。
    HostPath:宿主机目录,仅用于单机测试。
    iSCSI:iSCSI存储设备。
    Local:本地存储设备,从Kubernetes 1.7版本引入,到1.14版本时更新为稳定版,目前可以通过指定块(Block)设备提供Local PV,
    或通过社区开发的sig-storage-local-static-provisioner插件(https://github.com/kubernetes-sigs/sigstorage-local-static-provisioner)来管理Local PV的生命周期。
    NFS:网络文件系统。
    Portworx Volumes:Portworx提供的存储服务。
    Quobyte Volumes:Quobyte提供的存储服务。
    RBD(Ceph Block Device):Ceph块存储。
    ScaleIO Volumes:DellEMC的存储设备。
    StorageOS:StorageOS提供的存储服务。
    VsphereVolume:VMWare提供的存储系统。
    每种存储类型都有各自的特点,在使用时需要根据它们各自的参数进行设置。

    8.2.1 PV的关键配置参数
    1.存储能力(Capacity)
    描述存储设备具备的能力,目前仅支持对存储空间的设置(storage=xx),未来可能加入IOPS、吞吐率等指标的设置。
    2.存储卷模式(Volume Mode)
    Kubernetes从1.13版本开始引入存储卷类型的设置(volumeMode=xxx),可选项包括Filesystem(文件系统)和Block(块设备),默认值为Filesystem。
    目前有以下PV类型支持块设备类型:
    AWSElasticBlockStore
    AzureDisk
    FC
    GCEPersistentDisk
    iSCSI
    Local volume
    RBD(Ceph Block Device)
    VsphereVolume(alpha)
    下面的例子为使用块设备的PV定义:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: block_pv
    spec:
    capacity:
    storage: 5Gi
    accessModes:
    - ReadWriteOnce
    storageClassName: slow
    PersistentVolumeReclaimPolicy: Retain
    volumeMode: block
    fc:
    targetWWNs: ["xxxxxx"]
    lun: 0
    readOnly: false
    3.访问模式(Access Modes)
    对PV进行访问模式的设置,用于描述用户的应用对存储资源的访问权限。
    访问模式如下:
    ReadWriteOnce(RWO):读写权限,并且只能被单个Node挂载。
    ReadOnlyMany(ROX):只读权限,允许被多个Node挂载。
    ReadWriteMany(RWX):读写权限,允许被多个Node挂载。
    某些PV可能支持多种访问模式,但PV在挂载时只能使用一种访问模式,多种访问模式不能同时生效。
    表8.1描述了不同的存储提供者支持的访问模式。
    HostPath(宿主机目录)仅支持ReadWriteOnce访问模式
    NFS(网络文件系统)支持 ReadWriteOnce、ReadOnlyMany、ReadWriteMany 三种访问模式。
    Glusterfs(开源共享存储系统)支持 ReadWriteOnce、ReadOnlyMany、ReadWriteMany 三种访问模式。

    4.存储类别(Class)
    PV可以设定其存储的类别,通过storageClassName参数指定一个StorageClass资源对象的名称。
    具有特定类别的PV只能与请求了该类别的PVC进行绑定。
    未设定类别的PV则只能与不请求任何类别的PVC进行绑定。

    5.回收策略(Reclaim Policy)
    通过PV定义中的persistentVolumeReclaimPolicy字段进行设置,可选项如下。
    Retain保留:保留数据,需要手工处理。
    Recycle回收空间:简单清除文件的操作(例如执行rm -rf /thevolume/*命令)。
    Delete删除:与PV相连的后端存储完成Volume的删除操作(如AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等设备的内部Volume清理)。
    目前,只有NFS和HostPath两种类型的存储支持Recycle策略;AWS EBS、GCE PD、Azure Disk和Cinder volumes支持Delete策略。

    6.挂载参数(Mount Options)
    在将PV挂载到一个Node上时,根据后端存储的特点,可能需要设置额外的挂载参数,可以根据PV定义中的mountOptions字段进行设置。
    下面的例子为对一个类型为gcePersistentDisk的PV设置挂载参数:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: gce-disk-1
    spec:
    capacity:
    storage: 5Gi
    accessModes:
    - ReadWriteOnce
    mountOptions:
    - hard
    - nolock
    - nfsvers=3
    gcePersistentDisk:
    fsType: "ext4"
    pdName: "gce-disk-1"
    目前,以下PV类型支持设置挂载参数:
    AWSElasticBlockStore
    AzureDisk
    AzureFile
    CephFS
    Cinder (OpenStack block storage)
    GCEPersistentDisk
    Glusterfs
    NFS
    Quobyte Volumes
    RBD (Ceph Block Device)
    StorageOS
    VsphereVolume
    iSCSI

    7.节点亲和性(Node Affinity)
    PV可以设置节点亲和性来限制只能通过某些Node访问Volume,可以在PV定义中的nodeAffinity字段进行设置。
    使用这些Volume的Pod将被调度到满足条件的Node上。
    这个参数仅用于Local存储卷,例如:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: example-local-pv
    spec:
    capacity:
    storage: 5Gi
    accessModes:
    - ReadWriteOnce
    PersistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
    path: /mnt/disks/ssd1
    nodeAffinity:
    required:
    nodeSelectorTerms:
    - matchExpressions:
    - key: kubernetes.io/hostname
    operator: In
    values:
    - my-node
    公有云提供的存储卷(如AWS EBS、GCE PD、Azure Disk等)都由公有云自动完成节点亲和性设置,无须用户手工设置。

    8.2.2 PV生命周期的各个阶段
    某个PV在生命周期中可能处于以下4个阶段(Phaes)之一。
    Available:可用状态,还未与某个PVC绑定。
    Bound:已与某个PVC绑定。
    Released:绑定的PVC已经删除,资源已释放,但没有被集群回收。
    Failed:自动资源回收失败。
    定义了PV以后如何使用呢?这时就需要用到PVC了。下一节将对PVC进行详细说明。

    8.3 PVC详解
    PVC作为用户对存储资源的需求申请,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。
    下例声明的PVC具有如下属性:
    申请8GiB存储空间,访问模式为ReadWriteOnce,
    PV 选择条件为包含标签“release=stable”并且包含条件为“environment In [dev]”的标签,
    存储类别为“slow”(要求在系统中已存在名为slow的StorageClass):
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: myclaim
    spec:
    resources:
    request:
    storage: 8Gi
    accessModes:
    - ReadWriteOnce
    storageClassName: slow
    selector:
    matchLabels:
    release: "stable"
    matchExpressions:
    - {key: environment, operator: In, values: [dev] }
    PVC的关键配置参数说明如下:
    资源请求(Resources):描述对存储资源的请求,目前仅支持request.storage的设置,即存储空间大小。
    访问模式(Access Modes):PVC也可以设置访问模式,用于描述用户应用对存储资源的访问权限。其三种访问模式的设置与PV的设置相同。
    存储卷模式(Volume Modes):PVC也可以设置存储卷模式,用于描述希望使用的PV存储卷模式,包括文件系统和块设备。
    PV选择条件(Selector):通过对Label Selector的设置,可使PVC对于系统中已存在的各种PV进行筛选。系统将根据标签选出合适的PV与该PVC进行绑定。
    选择条件可以使用matchLabels和matchExpressions进行设置,如果两个字段都设置了,则Selector的逻辑将是两组条件同时满足才能完成匹配。
    存储类别(Class):PVC 在定义时可以设定需要的后端存储的类别(通过storageClassName字段指定),以减少对后端存储特性的详细信息的依赖。
    只有设置了该Class的PV才能被系统选出,并与该PVC进行绑定。
    PVC也可以不设置Class需求。如果storageClassName字段的值被设置为空(storageClassName=""),则表示该PVC不要求特定的Class,系统将只选择未设定Class的PV与之匹配和绑定。
    PVC也可以完全不设置storageClassName字段,此时将根据系统是否启用了名为DefaultStorageClass的admission controller进行相应的操作。
    未启用DefaultStorageClass:等效于PVC设置storageClassName的值为空(storageClassName=""),即只能选择未设定Class的PV与之匹配和绑定。
    启用DefaultStorageClass:要求集群管理员已定义默认的StorageClass。如果在系统中不存在默认的StorageClass,则等效于不启用DefaultStorageClass的情况。
    如果存在默认的StorageClass,则系统将自动为PVC创建一个PV(使用默认StorageClass的后端存储),并将它们进行绑定。
    集群管理员设置默认StorageClass的方法为,在StorageClass的定义中加上一个annotation“storageclass.kubernetes.io/is-default-class= true”。
    如果管理员将多个StorageClass都定义为default,则由于不唯一,系统将无法为PVC创建相应的PV。
    注意,PVC和PV都受限于Namespace,PVC在选择PV时受到Namespace的限制,只有相同Namespace中的PV才可能与PVC绑定。
    Pod在引用PVC时同样受Namespace的限制,只有相同Namespace中的PVC才能挂载到Pod内。
    当Selector和Class都进行了设置时,系统将选择两个条件同时满足的PV与之匹配。
    另外,如果资源供应使用的是动态模式,即管理员没有预先定义PV,仅通过StorageClass交给系统自动完成PV的动态创建,那么PVC再设定Selector时,系统将无法为其供应任何存储资源。
    在启用动态供应模式的情况下,一旦用户删除了PVC,与之绑定的PV也将根据其默认的回收策略“Delete”被删除。
    如果需要保留PV(用户数据),则在动态绑定成功后,用户需要将系统自动生成PV的回收策略从“Delete”改成“Retain”。

    8.4 PV和PVC的生命周期
    我们可以将PV看作可用的存储资源,PVC则是对存储资源的需求,PV和PVC的相互关系遵循如图8.1所示的生命周期。
    8.4.1 资源供应
    Kubernetes支持两种资源的供应模式:静态模式(Static)和动态模式(Dynamic)。
    资源供应的结果就是创建好的PV。
    静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置。
    动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种类型。
    此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及与PVC的绑定。
    PVC可以声明Class为"",说明该PVC禁止使用动态模式。
    8.4.2 资源绑定
    在用户定义好PVC之后,系统将根据PVC对存储资源的请求(存储空间和访问模式)在已存在的PV中选择一个满足PVC要求的PV,
    一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了。
    如果在系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV。
    PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了。
    在这种情况下,当PVC申请的存储空间比PV的少时,整个PV的空间就都能够为PVC所用,可能会造成资源的浪费。
    如果资源供应使用的是动态模式,则系统在为PVC找到合适的StorageClass后,将自动创建一个PV并完成与PVC的绑定。
    8.4.3 资源使用
    Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。
    Volume的类型为persistentVolumeClaim,在后面的示例中再进行详细说明。
    在容器应用挂载了一个PVC后,就能被持续独占使用。
    不过,多个Pod可以挂载同一个PVC,应用程序需要考虑多个实例共同访问一块存储空间的问题。
    8.4.4 资源释放
    当用户对存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能立刻与其他PVC进行绑定。
    通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。
    8.4.5 资源回收
    对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。
    只有PV的存储空间完成回收,才能供新的PVC绑定和使用。回收策略详见下节的说明。
    下面通过两张图分别对在静态资源供应模式和动态资源供应模式下,PV、PVC、StorageClass及Pod使用PVC的原理进行说明。
    图8.2描述了在静态资源供应模式下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制。
    图8.3描述了在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。
    接下来看看StorageClass的概念和用法。

    8.5 StorageClass详解
    StorageClass作为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,
    一方面减少了用户对于存储资源细节的关注,另一方面减轻了管理员手工管理PV的工作,由系统自动完成PV的创建和绑定,实现了动态的资源供应。
    基于StorageClass的动态资源供应模式将逐步成为云平台的标准存储配置模式。
    StorageClass的定义主要包括名称、后端存储的提供者(provisioner)和后端存储的相关参数配置。
    StorageClass一旦被创建出来,则将无法修改。如需更改,则只能删除原StorageClass的定义重建。
    下例定义了一个名为standard的StorageClass,提供者为aws-ebs,其参数设置了一个type,值为gp2:
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: standard
    provisioner: kubernetes.io/aws-ebs
    parameters:
    type: gp2

    8.5.1 StorageClass的关键配置参数
    1.提供者(Provisioner)
    描述存储资源的提供者,也可以看作后端存储驱动。
    目前Kubernetes支持的Provisioner都以“kubernetes.io/”为开头,用户也可以使用自定义的后端存储提供者。
    为了符合StorageClass的用法,自定义Provisioner需要符合存储卷的开发规范,
    详见https://github.com/kubernetes/community/blob/master/contributors/design-proposals/volume-provisioning.md的说明。

    2.参数(Parameters)
    后端存储资源提供者的参数设置,不同的Provisioner包括不同的参数设置。
    某些参数可以不显示设定,Provisioner将使用其默认值。
    接下来通过几种常见的Provisioner对StorageClass的定义进行详细说明。
    1)AWS EBS存储卷
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: standard
    provisioner: kubernetes.io/aws-ebs
    parameters:
    type: io1
    zone: us-east-1d
    iopsPerGB: "10"
    参数说明如下(详细说明请参考AWS EBS文档)。
    type:可选项为io1,gp2,sc1,st1,默认值为gp2。
    zone:AWS zone的名称。
    iopsPerGB:仅用于io1类型的Volume,意为每秒每GiB的I/O操作数量。
    encrypted:是否加密。
    kmsKeyId:加密时的Amazon Resource Name。
    2)GCE PD存储卷
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: standard
    provisioner: kubernetes.io/gce-pd
    parameters:
    type: pd-standard
    zone: us-centrall-a
    参数说明如下(详细说明请参考GCE文档)。
    type:可选项为pd-standard、pd-ssd,默认值为pd-standard。
    zone:GCE zone名称。
    3)GlusterFS存储卷
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: standard
    provisioner: kubernetes.io/glusterfs
    parameters:
    resturl: "http://127.0.0.1:8081" # Gluster REST服务(Heketi)的URL地址,用于自动完成GlusterFSvolume的设置。
    clusterid: "xxxx" # GlusterFS的Cluster ID
    restauthenabled: "true" # 是否对Gluster REST服务启用安全机制。
    restuser: "admin" # 访问Gluster REST 服务的用户名。
    secretNamespace: "default" # 保存访问Gluster REST服务密码的Secret命名空间。
    secretName: "heketi-secret" # 保存访问Gluster REST服务密码的Secret资源对象名。
    gidMin: "40000"
    gidMax: "50000" # StorageClass的GID范围,用于动态资源供应时为PV设置的GID。
    volumetype: "replicate:3" # 设置GlusterFS的内部Volume类型,例如replicate:3(Replicate类型,3份副本)
    参数说明如下(详细说明请参考GlusterFS和Heketi的文档)。
    resturl:Gluster REST服务(Heketi)的URL地址,用于自动完成GlusterFSvolume的设置。
    restauthenabled:是否对Gluster REST服务启用安全机制。
    restuser:访问Gluster REST 服务的用户名。
    secretNamespace和secretName:保存访问Gluster REST服务密码的Secret资源对象名。
    clusterid:GlusterFS的Cluster ID。
    gidMin和gidMax:StorageClass的GID范围,用于动态资源供应时为PV设置的GID。
    volumetype:设置GlusterFS的内部Volume类型,例如:
    replicate:3(Replicate类型,3份副本);
    disperse:4:2(Disperse类型,数据4份,冗余两份);
    “none”(Distribute类型)。
    4)OpenStack Cinder存储卷
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: gold
    provisioner: kubernetes.io/cinder
    parameters:
    type: fast
    availability: nova
    参数说明如下。
    type:Cinder的VolumeType,默认值为空。
    availability:Availability Zone,默认值为空。
    其他Provisioner的StorageClass相关参数设置请参考它们各自的配置手册。

    8.5.2 设置默认的StorageClass
    要在系统中设置一个默认的StorageClass,则首先需要启用名为DefaultStorageClass的admission controller,
    即在kube-apiserver的命令行参数--admission-control中增加:
    --admission-control=...,DefaultStorageClass
    然后,在StorageClass的定义中设置一个annotation:
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: gold
    annotations:
    storageclass.beta.kubernetes.io/is-default-class="true"
    provisioner: kubernetes.io/gce-pd
    parameters:
    type: pd-ssd
    通过kubectl create命令创建成功后,查看StorageClass列表,可以看到名为gold的StorageClass被标记为default:
    # kubectl get sc

    8.6 动态存储管理实战:GlusterFS
    本节以GlusterFS为例,从定义StorageClass、创建GlusterFS和Heketi服务、用户申请PVC到创建Pod使用存储资源,
    对StorageClass和动态资源分配进行详细说明,进一步剖析Kubernetes的存储机制。

    8.6.1 准备工作
    为了能够使用GlusterFS,首先在计划用于GlusterFS的各Node上安装GlusterFS客户端:
    # yum install glusterfs glusterfs-fuse
    GlusterFS管理服务容器需要以特权模式运行,在kube-apiserver的启动参数中增加:
    --allow-privileged=true
    给要部署GlusterFS管理服务的节点打上“storagenode=glusterfs”的标签,是为了将GlusterFS容器定向部署到安装了GlusterFS的Node上:
    # kubectl label node k8s-node-1 storagenode=glusterfs
    # kubectl label node k8s-node-2 storagenode=glusterfs
    # kubectl label node k8s-node-3 storagenode=glusterfs

    8.6.2 创建GlusterFS管理服务容器集群
    GlusterFS管理服务容器以DaemonSet的方式进行部署,确保在每个Node上都运行一个GlusterFS管理服务。
    glusterfs-daemonset.yaml的内容如下:
    apiVersion: extensions/v1beta1
    kind: DaemonSet
    metadata:
    name: glusterfs
    labels:
    glusterfs: daemonset
    annotations:
    description: GlusterFS DaemonSet
    tags: glusterfs
    spec:
    template:
    metadata:
    name: glusterfs
    labels:
    glusterfs-node: pod
    spec:
    nodeSelector:
    storagenode: glusterfs
    hostNetwork: true
    containers:
    - image: gluster/gluster-centos:latest
    name: glusterfs
    volumeMounts:
    - name: glusterfs-heketi
    mountPath: "/var/lib/heketi"
    - name: glusterfs-run
    mountPath: "/run"
    - name: glusterfs-lvm
    mountPath: "/run/lvm"
    - name: glusterfs-etc
    mountPath: "/etc/glusterfs"
    - name: glusterfs-logs
    mountPath: "/var/log/glusterfs"
    - name: glusterfs-config
    mountPath: "/var/lib/glusterd"
    - name: glusterfs-dev
    mountPath: "/dev"
    - name: glusterfs-misc
    mountPath: "/var/lib/misc/glusterfsd"
    - name: glusterfs-cgroup
    mountPath: "/sys/fs/cgroup"
    readOnly: true
    - name: glusterfs-ssl
    mountPath: "/etc/ssl"
    readOnly: true
    securityContext:
    capabilities: {}
    privileged: true
    readinessProbe:
    timeoutSeconds: 3
    initialDelaySeconds: 60
    exec:
    command:
    - "/bin/bash"
    - "-c"
    - systemctl status glusterd.service
    livenessProbe:
    timeoutSeconds: 3
    initialDelaySeconds: 60
    exec:
    command:
    - "/bin/bash"
    - "-c"
    - systemctl status glusterd.service
    volumes:
    - name: glusterfs-heketi
    hostPath:
    path: "/var/lib/heketi"
    - name: glusterfs-run
    - name: glusterfs-lvm
    hostPath:
    path: "/run/lvm"
    - name: glusterfs-etc
    hostPath:
    path: "/etc/glusterfs"
    - name: glusterfs-logs
    hostPath:
    path: "/var/log/glusterfs"
    - name: glusterfs-config
    hostPath:
    path: "/var/lib/glusterd"
    - name: glusterfs-dev
    hostPath:
    path: "/dev"
    - name: glusterfs-misc
    hostPath:
    path: "/var/lib/misc/glusterfsd"
    - name: glusterfs-cgroup
    hostPath:
    path: "/sys/fs/cgroup"
    - name: glusterfs-ssl
    hostPath:
    path: "/etc/ssl"
    # kubectl create -f glusterfs-daemonset.yaml
    # kubectl get pod

    8.6.3 创建Heketi服务
    Heketi 是一个提供RESTful API管理GlusterFS卷的框架,
    并能够在OpenStack、Kubernetes、OpenShift等云平台上实现动态存储资源供应,支持GlusterFS多集群管理,便于管理员对GlusterFS进行操作。
    图8.4简单描述了Heketi的作用。
    在部署Heketi服务之前,需要为它创建一个ServiceAccount对象(heketi-service-account.yaml):
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: heketi-service-account
    # kubectl create -f heketi-service-account.yaml
    部署Heketi服务(heketi-deployment-svc.yaml):
    apiVersion: v1
    kind: Deployment
    metadata:
    name: deploy-heketi
    labels:
    glusterfs: heketi-deployment
    deploy-heketi: heketi-deployment
    annotations:
    description: Defines how to deploy Heketi
    spec:
    ...
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: deploy-heketi
    labels:
    glusterfs: heketi-service
    deploy-heketi: support
    annotations:
    description: Exposes Heketi Service
    spec:
    selector:
    name: deploy-heketi
    ports:
    - name: deploy-heketi
    port: 8080
    targetPort: 8080
    需要注意的是,Heketi的DB数据需要持久化保存,建议使用hostPath或其他共享存储进行保存:
    # kubectl create -f heketi-deployment-svc.yaml

    8.6.4 为Heketi设置GlusterFS集群
    在Heketi能够管理GlusterFS集群之前,首先要为其设置GlusterFS集群的信息。
    可以用一个topology.json配置文件来完成各个GlusterFS节点和设备的定义。
    Heketi要求在一个GlusterFS集群中至少有3个节点。
    在topology.json配置文件hostnames字段的manage上填写主机名,在storage上填写IP地址,
    devices要求为未创建文件系统的裸设备(可以有多块盘),以供Heketi自动完成PV(Physical Volume)、VG(Volume Group)和LV(Logical Volume)的创建。
    topology.json文件的内容如下:
    {
    "clusters": [
    {
    "nodes": [
    {
    "node": {
    "hostnames": {
    "manage": ["k8s-node-1"],
    "storage": ["192.168.18.3"]
    },
    "zone": 1
    },
    "devices": ["/dev/sdb"]
    },
    ......其余两个node节点配置
    ]
    }
    ]
    }
    进入Heketi容器,使用命令行工具heketi-cli完成GlusterFS集群的创建:
    # export HEKETI_CLI_SERVER=http://localhost:8080
    # heketi_cli topology load --json=topology.json
    经过这个操作,Heketi完成了GlusterFS集群的创建,同时在GlusterFS集群的各个节点的/dev/sdb盘上成功创建了PV和VG。
    查看Heketi的topology信息,可以看到Node和Device的详细信息,包括磁盘空间的大小和剩余空间。此时,Volume和Brick还未创建:
    # heketi_cli topology info

    8.6.5 定义StorageClass
    准备工作已经就绪,集群管理员现在可以在Kubernetes集群中定义一个StorageClass了。
    storageclass-gluster-heketi.yaml配置文件的内容如下:
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: gluster-heketi
    provisioner: kubernetes.io/glusterfs
    parameters:
    resturl: "http://172.17.2.2:8080"
    restauthenabled: "false"
    Provisioner参数必须被设置为“kubernetes.io/glusterfs”。
    resturl的地址需要被设置为API Server所在主机可以访问到的Heketi服务的某个地址,可以使用服务ClusterIP+端口号、容器IP地址+端口号,或将服务映射到物理机,使用物理机IP+NodePort。
    创建这个StorageClass资源对象:
    # kubectl create -f storageclass-gluster-heketi.yaml

    8.6.6 定义PVC
    现在,用户可以申请一个PVC了(pvc-gluster-heketi.yaml)。
    例如,一个用户申请一个1GiB空间的共享存储资源,StorageClass使用“gluster-heketi”,未定义任何Selector,说明使用动态资源供应模式:
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pvc-gluster-heketi
    spec:
    resources:
    request:
    storage: 1Gi
    accessModes:
    - ReadWriteOnce
    storageClassName: gluster-heketi
    # kubectl create -f pvc-gluster-heketi.yaml
    PVC的定义一旦生成,系统便将触发Heketi进行相应的操作,主要为在GlusterFS集群上创建brick,再创建并启动一个Volume。整个过程可以在Heketi的日志中查到。
    查看PVC的状态,可见其已经为Bound(已绑定):
    # kubectl get pvc
    查看PV,可见系统自动创建的PV:
    # kubectl get pv
    查看该PV的详细信息,可以看到其容量、引用的StorageClass等信息都已正确设置,状态也为Bound,回收策略则为默认的Delete。同时Gluster的Endpoint和Path也由Heketi自动完成了设置:
    # kubectl describe pv pvc-xxxx
    至此,一个可供Pod使用的PVC就创建成功了。接下来Pod就能通过Volume的设置将这个PVC挂载到容器内部进行使用了。

    8.6.7 Pod使用PVC的存储资源(pod-use-pvc.yaml)
    在Pod中使用PVC定义的存储资源非常容易,只需设置一个Volume,其类型为persistentVolumeClaim,即可轻松引用一个PVC。
    下例中使用一个busybox容器验证对PVC的使用,注意Pod需要与PVC属于同一个Namespace:
    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-use-pvc
    spec:
    containers:
    - name: pod-use-pvc
    image: busybox
    command:
    - sleep
    - "3600"
    volumeMounts:
    - name: gluster-volume
    mountPath: "/pv-data"
    readOnly: false
    volumes:
    - name: gluster-volume
    persistentVolumeClaim:
    claimName: pvc-gluster-heketi
    # kubectl create -f pod-use-pvc.yaml
    进入容器pod-use-pvc,在/pv-data目录下创建一些文件:
    # kubectl exec -it pod-use-pvc -- /bin/sh
    cd /pv-data
    touch a
    echo "hello" > b
    可以验证文件a和b在GlusterFS集群中是否正确生成。
    至此,使用Kubernetes最新的动态存储供应模式,配合StorageClass和Heketi共同搭建基于GlusterFS的共享存储就完成了。
    有兴趣的读者可以继续尝试StorageClass的其他设置,例如调整GlusterFS的Volume类型、修改PV的回收策略等。
    在使用动态存储供应模式的情况下,相对于静态模式的优势至少包括如下两点:
    (1)管理员无须预先创建大量的PV作为存储资源。
    (2)用户在申请PVC时无法保证容量与预置PV的容量完全匹配。
    从Kubernetes 1.6版本开始,建议用户优先考虑使用StorageClass的动态存储供应模式进行存储管理。

    8.7 CSI存储机制详解
    Kubernetes从1.9版本开始引入容器存储接口Container Storage Interface(CSI)机制,
    用于在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务。
    CSI到Kubernetes 1.10版本升级为Beta版,到Kubernetes 1.13版本升级为GA版,已逐渐成熟。

    8.7.1 CSI的设计背景
    Kubernetes通过PV、PVC、Storageclass已经提供了一种强大的基于插件的存储管理机制,
    但是各种存储插件提供的存储服务都是基于一种被称为“in-tree”(树内)的方式提供的,
    这要求存储插件的代码必须被放进Kubernetes的主干代码库中才能被Kubernetes调用,属于紧耦合的开发模式。
    这种“in-tree”方式会带来一些问题:
    存储插件的代码需要与Kubernetes的代码放在同一代码库中,并与Kubernetes的二进制文件共同发布;
    存储插件代码的开发者必须遵循Kubernetes的代码开发规范;
    存储插件代码的开发者必须遵循Kubernetes的发布流程,包括添加对Kubernetes存储系统的支持和错误修复;
    Kubernetes社区需要对存储插件的代码进行维护,包括审核、测试等工作;
    存储插件代码中的问题可能会影响Kubernetes组件的运行,并且很难排查问题;
    存储插件代码与Kubernetes的核心组件(kubelet和kube-controller-manager)享有相同的系统特权权限,可能存在可靠性和安全性问题。
    Kubernetes已有的Flex Volume插件机制试图通过为外部存储暴露一个基于可执行程序(exec)的API来解决这些问题。
    尽管它允许第三方存储提供商在Kubernetes核心代码之外开发存储驱动,但仍然有两个问题没有得到很好的解决:
    部署第三方驱动的可执行文件仍然需要宿主机的root权限,存在安全隐患;
    存储插件在执行mount、attach这些操作时,通常需要在宿主机上安装一些第三方工具包和依赖库,使得部署过程更加复杂,
    例如部署Ceph时需要安装rbd库,部署GlusterFS时需要安装mount.glusterfs库,等等。
    基于以上这些问题和考虑,Kubernetes逐步推出与容器对接的存储接口标准,
    存储提供方只需要基于标准接口进行存储插件的实现,就能使用Kubernetes的原生存储机制为容器提供存储服务。
    这套标准被称为CSI(容器存储接口)。
    在CSI成为Kubernetes的存储供应标准之后,存储提供方的代码就能和Kubernetes代码彻底解耦,部署也与Kubernetes核心组件分离,
    显然,存储插件的开发由提供方自行维护,就能为Kubernetes用户提供更多的存储功能,也更加安全可靠。
    基于CSI的存储插件机制也被称为“out-of-tree”(树外)的服务提供方式,是未来Kubernetes第三方存储插件的标准方案。

    8.7.2 CSI存储插件的关键组件和部署架构
    图8.5描述了Kubernetes CSI存储插件的关键组件和推荐的容器化部署架构。
    其中主要包括两种组件:CSI Controller和CSI Node。
    1.CSI Controller
    CSI Controller的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。
    在Kubernetes中建议将其部署为单实例Pod,可以使用StatefulSet或Deployment控制器进行部署,设置副本数量为1,保证为一种存储插件只运行一个控制器实例。
    在这个Pod内部署两个容器,如下所述。
    (1)与Master(kube-controller-manager)通信的辅助sidecar容器。
    在sidecar容器内又可以包含external-attacher和external-provisioner两个容器,它们的功能分别如下。
    external-attacher:监控VolumeAttachment资源对象的变更,触发针对CSI端点的ControllerPublish和ControllerUnpublish操作。
    external-provisioner:监控PersistentVolumeClaim资源对象的变更,触发针对CSI端点的CreateVolume和DeleteVolume操作。
    (2)CSI Driver存储驱动容器,由第三方存储提供商提供,需要实现上述接口。
    这两个容器通过本地Socket(Unix Domain Socket,UDS),并使用gPRC协议进行通信。
    sidecar容器通过Socket调用CSI Driver容器的CSI接口,CSI Driver容器负责具体的存储卷操作。

    2.CSI Node
    CSI Node的主要功能是对主机(Node)上的Volume进行管理和操作。
    在Kubernetes中建议将其部署为DaemonSet,在每个Node上都运行一个Pod。
    在这个Pod中部署以下两个容器:
    (1)与kubelet通信的辅助sidecar容器node-driver-registrar,主要功能是将存储驱动注册到kubelet中;
    (2)CSI Driver存储驱动容器,由第三方存储提供商提供,主要功能是接收kubelet的调用,需要实现一系列与Node相关的CSI接口,
    例如NodePublishVolume接口(用于将Volume挂载到容器内的目标路径)、NodeUnpublishVolume接口(用于从容器中卸载Volume),等等。
    node-driver-registrar容器与kubelet通过Node主机的一个hostPath目录下的unix socket进行通信。
    CSI Driver容器与kubelet通过Node主机的另一个hostPath目录下的unix socket进行通信,
    同时需要将kubelet的工作目录(默认为/var/lib/kubelet)挂载给CSI Driver容器,用于为Pod进行Volume的管理操作(包括mount、umount等)。

    8.7.3 CSI存储插件的使用示例
    下面以csi-hostpath插件为例,对如何部署CSI插件、用户如何使用CSI插件提供的存储资源进行详细说明。
    (1)设置Kubernetes服务启动参数。为kube-apiserver、kube-controller-manager和kubelet服务的启动参数添加:
    --feature-gates=VolumeSnapshotDataSource=true,CSINodeInfo=true,CSIDriverRegistry=true
    这3个特性开关是Kubernetes从1.12版本引入的Alpha版功能,CSINodeInfo和CSIDriverRegistry需要手工创建其相应的CRD资源对象。
    Kubernetes 1.10版本所需的CSIPersistentVolume和MountPropagation特性开关已经默认启用,
    KubeletPluginsWatcher特性开关也在Kubernetes 1.12版本中默认启用,无须在命令行参数中指定。
    (2)创建CSINodeInfo和CSIDriverRegistry CRD资源对象。
    csidriver.yaml的内容如下:
    csinodeinfo.yaml的内容如下:
    使用kubectl create命令完成创建:
    # kubectl create -f csidriver.yaml
    # kubectl create -f csinodeinfo.yaml
    (3)创建csi-hostpath存储插件相关组件,
    包括csi-hostpath-attacher、csi-hostpathprovisioner和csi-hostpathplugin(其中包含csi-node-driver-registrar和hostpathplugin)。
    其中为每个组件都配置了相应的RBAC权限控制规则,对于安全访问Kubernetes资源对象非常重要。
    csi-hostpath-attacher.yaml的内容如下:
    csi-hostpath-provisioner.yaml的内容如下:
    csi-hostpathplugin.yaml的内容如下:
    使用kubectl create命令完成创建:
    # kubectl create -f csi-hostpath-attacher.yaml
    # kubectl create -f csi-hostpath-provisioner.yaml
    # kubectl create -f csi-hostpathplugin.yaml
    确保3个Pod都正常运行:
    # kubectl get pods
    至此就完成了CSI存储插件的部署。
    (4)应用容器使用CSI存储。
    应用程序如果希望使用CSI存储插件提供的存储服务,则仍然使用Kubernetes动态存储管理机制。
    首先通过创建StorageClass和PVC为应用容器准备存储资源,然后容器就可以挂载PVC到容器内的目录进行使用了。
    创建一个StorageClass,provisioner为CSI存储插件的类型,在本例中为csi-hostpath:
    csi-storageclass.yaml的内容如下:
    # kubectl create -f csi-storageclass.yaml
    创建一个PVC,引用刚刚创建的StorageClass,申请存储空间为1GiB:
    csi-pvc.yaml的内容如下:
    # kubectl create -f csi-pvc.yaml
    查看PVC和系统自动创建的PV,状态为Bound,说明创建成功:
    # kubectl get pvc
    # kubectl get pv
    最后,在应用容器的配置中使用该PVC:
    csi-app.yaml的内容如下:
    # kubectl create -f csi-app.yaml
    # kubectl get pods
    在Pod创建成功之后,应用容器中的/data目录使用的就是CSI存储插件提供的存储。
    我们通过kubelet的日志可以查看到Volume挂载的详细过程。

    8.7.4 CSI的发展
    目前可用于生产环境的CSI插件如表8.2所示。
    实验性的CSI插件如表8.3所示。
    每种CSI存储插件都提供了容器镜像,
    与external-attacher、external-provisioner、node-driver-registrar等sidecar辅助容器共同完成存储插件系统的部署,
    每个插件的部署配置详见官网https://kubernetes-csi.github.io/docs/drivers.html中的链接。
    Kubernetes从1.12版本开始引入存储卷快照(Volume Snapshots)功能,
    通过新的CRD自定义资源对象VolumeSnapshotContent、VolumeSnapshot和VolumeSnapshotClass进行管理。
    VolumeSnapshotContent定义从当前PV创建的快照,类似于一个新的PV;
    VolumeSnapshot定义需要绑定某个快照的请求,类似于PVC的定义;
    VolumeSnapshotClass用于屏蔽VolumeSnapshotContent的细节,类似于StorageClass的功能。
    下面是一个VolumeSnapshotContent的例子:
    下面是一个VolumeSnapshot的例子:
    后续要进一步完善的工作如下。
    (1)将以下Alpha版功能更新到Beta版:
    Raw Block类型的Volumes;
    拓扑感知,Kubernetes理解和影响CSI卷的配置位置(如zone、region 等)的能力;
    完善基于CRD的扩展功能(例如Skip attach、Pod info on mount等)。
    (2)完善对本地短暂卷(Local Ephemeral Volume)的支持。
    (3)将Kubernetes“in-tree”存储卷插件迁移到CSI。

  • 相关阅读:
    springboot2.0整合logback日志(详细)
    关于Logstash中grok插件的正则表达式例子
    feign多文件上传
    HBase API(新版本)
    HBase基础知识
    Hive数据操作和数据查询
    Hive数据定义
    Hive基础知识
    Hive安装
    Spark词频统计,求TOP值,文件排序,二次排序
  • 原文地址:https://www.cnblogs.com/BradMiller/p/12228265.html
Copyright © 2011-2022 走看看