@lijiang

Sculpting in time

Do one thing and do it well.
Every story has a beginning and an end.

OpenEBS Container Attached Storage

openEBS 分布式文件系统架构

3 分钟

本文图片来源Openebs

前言

关于 OpenEBS

OpenEBS是一个运行在Kubernets上的开源存储项目,符合CAS(Container Attached Storage)架构, 它的所有组件全部运行在用户层,属于在k8s上云原生的分布式文件系统。

OpenEBS文件系统提供如下类别的特性,每个特性都可以用于某个特定环境下的解决方案。

cStor: 支持容错,高可用性,副本拷贝,支持快照,克隆等技术,其后端是由ZFS支持。

Jiva: 后端引擎为年轻的Longhorn,支持容错,副本拷贝,但没有cStor的快照和克隆技术。

Dynamic Local PV HostPath: 直接使用本地文件存储,优点是低延时,缺点是不支持高可用,一旦本地节点宕机或者本地磁盘损坏,则导致数据丢失,

Dynamic Local PV Devices: 和HostPath类似,直接使用本地的硬盘进行存储,缺点也是无法支持高可用,但是低延迟。

Dynamic Local PV ZFS: 不支持高可用,但可以使用ZFS的功能(制作快照和克隆技术),从而提升数据的保护能力,便于从灾难中恢复数据。

特性(Features)

CAS 架构:

PV和PVC都是容器化的,便于kubernets进行管理。

同步副本:

当采用cStor Jiva Mayastor存储引擎时,实现多副本同步拷贝,从而提供数据的高可用冗错机制。

快照和克隆:

采用cStor存储引擎时,支持写时复制快照技术,生成快照有助于我们可以回滚数据,克隆可以快速的生成一套生产环境数据以便开发在测试环境使用真实的生产数据。

备份和恢复:

使用备份工具及插件,快速的备份文件和恢复文件。

监控:

openebs 组件会提供Metric接口,从而方便prometheus对其监控,进而调优整套存储方案的性能。

Alpha特性

支持ARM64,这也是我目前在使用的原因,自己在组建一套7*24小时运行的Polkadot委托节点,因为直接和收益挂钩,所以我调研了很多容器化的存储架构方案,虽然Openebs性能不如其他的老牌分布式存储系统,像Ceph,glusterfs这样的,但是它提供的CAS架构和高容错机制,以及支持ARM64,就让我决定选择它用于金融行业的云存储架构方案。

架构

基于单一服务的PV架构

整个PV的底层读写操作全部在内核层面完成,Kubernets只是增加了一层抽象,将PV通过底层的分布式文件读写接口导出到用户层,Pods通过PVC与PV绑定进行数据的读写处理。

基于微服务的PV架构

典型的CAS架构,数据的读写操作全部在用户层处理,CAS架构中的副本拷贝以及存储控制器全部由kubernets自身来进行调度,PV直接从CAS控制器的底层资源中获取存储设备,Pods通过PVC与PV绑定,从而完成数据的读写处理。

None CAS 与 CAS 对比

CAS的优势:

  1. 更加敏捷,可以直接在Pods层面去优化存储控制器。

  2. 更加精细的监控,能够直接监控到每一个Volume的IOPS,读写延迟。

  3. 解耦合,不依赖于第三方的云存储系统,比如你可以不改变yaml文件的情况下,将应用从一个集群,迁移到另一个集群,如果采用None CAS,则会非常依赖对于Kubenets不可见的存储管理系统,迁移过程中需要修改yaml文件中关于PV的描述。

  4. 云原生,好处就在于能够直接与其他系统对接,比如文件系统监控,管理,采用Kube CRD的方式直接调用底层的存储系统。

微服务架构

采用微服务的方式极大的提高了分布式文件系统的高可用性,因为整个存储的服务全部运行在用户层,每一个服务都可以从一个节点迁移到另外一个节点,只要我们满足副本的可用性,即使某一个节点宕机也不会影像我们线上的数据访问,保证系统的可用。

从上图中,我们可以看出Storage Controller直接管理底层的副本处理的相关Pods,应用层的Pods通过PV与Storage Controller沟通。

OpenEBS 组件

  1. 控制组件

    • maya-apiServer

    • provisioner

    • NDM Operator

    • NDM Daemonset

  2. 数据组件

    • cStor

    • Jiva

    • LocalPV

控制器

控制器中主要由两部分: maya-apiServer provisioner

provisioner: 初始化存储

maya-apiServer: 提供存储API接口以及调用kube-api来创建用于存储数据管理的Pods

从图中的分析可以得出如下调用流程:

kubectl调用kube-api请求PVC,provisioner通过iSCSI协议管理底层存储,provisioner根据用户的需求创建PV和PVC,provisioner与maya-apiServer进行交互,创建Volume Controller Pod和Volume Replica Pod。

maya-apiServer

maya-apiServer提供了OpenEBS的REST API,当provisioner完成PV的创建之后,maya-apiServer会接收到provisioner的请求,maya-apiServer会根据用户定义的存储引擎调用kube-api接口启动Controller Pod和Volume Replica Pod。

其中的Controller PodVolume Replica Pod我们称之为数据端的组件,它们以SideCars的形式运行,就像上面命令行窗口中显示的pvc-223d971c-cd6a-4584-b5c3-bc501594d85b-target-5db799684cws2bz这样的Pod,其中运行的三个容器分别为cstor-istgtcstor-volume-mgmtmaya-volume-exporter,这些Pod我们统一称之为Storage Controller Pods

cstor-volume-mgmt: 为cStor Controller和cStor Replica提供配置信息。

maya-volume-exporter: 监控数据统计,包括文件系统读写延迟,读写IOPS,文件系统容量。

类似cstor-disk-pool-7prl-5c896fc5f8-7n68l,我们称之为CAS Pods,其中运行的容器分别为cstor-poolcstor-pool-mgmtmaya-exporter

Node Device Manager

在命令行图中我们可以看到这样的Pods openebs-ndm-fhph9,这个就是Node Device Manager,其作用是发现,监控,管理底层的磁盘,为provisioner提供服务。

NDM DaemonSet会运行在每个节点上,根据用户自行配置的NDM Configmap,自动发现可用的磁盘块,将其加入到blockdevice列表中。

CAS Engine

CAS 引擎总共有三种: Jiva cStor LocalPV

我们主要区分下Jiva和cStor,它们都具有副本复制的功能,但是Jiva更加轻量,Jiva没有Disk Pool这样的抽象层,所以的副本拷贝和数据操作都是通过Replica Pod完成的,所有在性能上会优于cStor,再来看下cStor,它采用Disk Pool的这种机制,中间增加了像cStor Storage PoolStorage Pool Claim这样的抽象层,下面我们会具体讨论cStor。

存储引擎的特性比较

cStor Engine

cStor是OpenEBS使用最广泛的存储引擎,因为经受住了在生产环境使用的考验,Jiva因为采用的是Longhorn后端引擎,目前Longhorn还是一个孵化项目,可以在测试和开发环境使用Jiva,生产环境尽量使用cStor

当在Kubernets中使用cStor时,我们可以看到Application Pod上的数据会被多份拷贝到不同的数据节点上,所有拷贝到节点上的数据都是完整的副本。

这里我们需要介绍两个主要模块,就是图中的cStor TargetcStor Pool,对应的类似Pods分别为pvc-223d971c-cd6a-4584-b5c3-bc501594d85b-target-5db799684cws2bzcstor-disk-pool-7prl-5c896fc5f8-7n68l

  • cStor Pool: 维护存储上的数据,持久运行在节点上,维护每一个卷的副本拷贝,管理快照和克隆副本。类似LVM的概念,创建cStor Pool需要先创建Storage Pool。

  • cStor Target: 当PVC卷被创建完成之后,cStor Target Pods就会被创建,它们会提供iSCSI数据存储服务,Application将数据通过iSCSI服务请求cStor Target Pods,cStor Target会将数据发往cStor Pool。

每创建一个PVC我们都可以看到类似的kube Service被创建:

apiVersion: v1
kind: Service
metadata:
  annotations:
    openebs.io/storage-class-ref: |
      name: openebs-cstor-disk-pool
  labels:
    openebs.io/cas-template-name: cstor-volume-create-default-2.1.0
    openebs.io/cas-type: cstor
    openebs.io/persistent-volume: pvc-a3ef1f31-04c6-47e8-811c-40fba7a02a42
    openebs.io/persistent-volume-claim: polkadot-node-database-polkadot-node-0
    openebs.io/storage-engine-type: cstor
    openebs.io/target-service: cstor-target-svc
    openebs.io/version: 2.2.0
spec:
  clusterIP: 10.111.0.196
  ports:
  - name: cstor-iscsi
    port: 3260
    protocol: TCP
    targetPort: 3260
  - name: cstor-grpc
    port: 7777
    protocol: TCP
    targetPort: 7777
  - name: mgmt
    port: 6060
    protocol: TCP
    targetPort: 6060
  - name: exporter
    port: 9500
    protocol: TCP
    targetPort: 9500
  selector:
    app: cstor-volume-manager
    openebs.io/persistent-volume: pvc-a3ef1f31-04c6-47e8-811c-40fba7a02a42
    openebs.io/target: cstor-target
  sessionAffinity: None
  type: ClusterIP

我们可以看到cstor-iscsi对应的端口会被映射到openebs.io/target: cstor-target

这里需要说明一个重点,所有副本拷贝全部发生在卷层面,即cstor-target,包括数据重建也是发生在target层,我们可以看上图,其中定义了两个Target,分别为Target1和Target2,Target1会拷贝三个副本到Pool1中,而Target2只拷贝一个副本到Pool1中(原始数据只有一份)。

Install

openebs-operator-custom.yaml

按照需求修改yaml文件中的docker image,因为我使用的是arm64架构,所以镜像采用的都是arm64。

部署应用:

kubectl apply -f openebs-operator-custom.yaml

获取磁盘块数据:

kubectl get blockdevice -n openebs

apiVersion: openebs.io/v1alpha1
kind: StoragePoolClaim
metadata:
  namespace: openebs
  name: cstor-disk-pool
  annotations:
    cas.openebs.io/config: |
      - name: PoolResourceRequests
        value: |-
            memory: 1Gi
      - name: PoolResourceLimits
        value: |-
            memory: 5Gi
spec:
  name: cstor-disk-pool
  type: disk
  poolSpec:
    poolType: striped
  blockDevices:
    blockDeviceList:
    - blockdevice-4e46911e784fa99e62b48fa6e03375df
    - blockdevice-78e8ee08a23ed4363f34ca699ad5666e
    - blockdevice-da35a8151b969f3822effcf3fdc8cc13

根据需求创建cstor-disk-pool

最后创建Storage Class

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: openebs-cstor-disk-pool
  annotations:
    openebs.io/cas-type: cstor
    cas.openebs.io/config: |
      - name: StoragePoolClaim
        value: "cstor-disk-pool"
      - name: ReplicaCount
        value: "3"
provisioner: openebs.io/provisioner-iscsi

最后确保所有的OpenEBS Pods都是正常运行的,这样就可以开始在Pods创建中使用openebs-cstor-disk-pool PVC了。

Upgrade

采用官方的升级手册Upgrade OpenEBS进行升级,目前自己测试是可行的,比如从2.1.0升级到2.2.0。

Trouble Shooting

  1. 如果cStorPool遇到 ERROR Failed to connect to 10.x.x.x 这样的错误,说明PVC没有被清除干净。

shell 登入 cstor-disk-pool, 执行zfs get io.openebs:targetip,查看是否有上面的10.x.x.x,如果有,我们可以列出PVC zfs list,然后删除对应的pvc zfs destroy $pvc

  1. 如果cStorPool遇到 zpool list: no such pool 并且查看cstor-pool中的错误为:
pool_state=0x0 pool_context=0x2 pool_failmode="wait" vdev_guid=0xb1670e3a8363ea11 vdev_type="disk

我们可以登入到cstor-disk-pool中,使用zpool import -F cstor-dee8315a-a61f-492c-8f21-96e608fe5c02来解决问题,具体的**-F cstor-dee8315a-a61f-492c-8f21-96e608fe5c02**根据你的实际环境来填写。

openebs cstor 出现如下错误,并且cstor-pool日志中输出ERROR fail on already available snapshot pvc-6da87e1d-1528-4c9b-b78f-eae6e97f1d80@rebuild_snap,以下方法针对openebs cspc version。

ereport.fs.zfs.checksum ena=0x604aaa86feffc01 detector=[ version=0x0 scheme=
 "zfs" pool=0x1080d831c2f79f6d vdev=0x8521415e00a48561 ] pool=
 "cstor-4bd062aa-abe6-4383-8dfc-c06ca3aaf9e3" pool_guid=0x1080d831c2f79f6d
 pool_state=0x0 pool_context=0x0 pool_failmode="wait" vdev_guid=
 0x8521415e00a48561 vdev_type="disk" vdev_path="/dev/sda1" vdev_ashift=0x9
 vdev_complete_ts=0x604aa9debc vdev_delta_ts=0xee4c063 vdev_read_errors=0x0
 vdev_write_errors=0x0 vdev_cksum_errors=0x0 parent_guid=0x1080d831c2f79f6d
 parent_type="root" vdev_spare_paths=[...] vdev_spare_guids=[ ] zio_err=0x34
 zio_flags=0x180880 zio_stage=0x400000 zio_pipeline=0xf80000 zio_delay=
 0xee4af9b zio_timestamp=0x603bc51e59 zio_delta=0xee4c007 zio_offset=
 0xb66bc1b800 zio_size=0x200 zio_objset=0x1d2 zio_object=0x3 zio_level=0x0
 zio_blkid=0x29c cksum_expected=[ 0xdee3d00b9 0x47359dd3651 0xcc306ebdcfad
 0x1a924b3b71b5fd ] cksum_actual=[ 0x3d2d239389 0xfd293e09abf 0x2c631c30b7653
 0x5db85ed39028f3 ] cksum_algorithm="fletcher4" time=[ 0x61a020d4 0x0 ] eid=0x4
``**

解决思路:

    1. 查出故障的cstor pool disk节点上托管着哪些volumes。

    2. 编辑cstor volumes配置: kubectl edit cstorvolumeconfigs.cstor.openebs.io -n openebs pvc-5ec38555-b11f-462e-ad8a-67d7dac52edb
       去除其中replicaPoolInfo包含损坏的pool
       replicaPoolInfo:
       - poolName: cstor-disk-pool-cfc9
       - poolName: cstor-disk-pool-dqv7
       - poolName: cstor-disk-pool-p4jr (delete**

    3. 遍历所有的cstor volumes,重复执行操作步骤2。

    4. 最后在cspc中删除损坏的pool node,kubectl edit cspc -n openebs cstor-disk-pool,pool node将会被删除。

    5. 重建cstor pool node,kubectl edit cspc -n openebs cstor-disk-pool 重新加入pool node 配置。

    6. kubectl get cstorpoolinstances.cstor.openebs.io -A 查找重建完成的pool node instance 名称,比如cstor-disk-pool-p4jr。

    7. 遍历上述修改过的cstor volumes配置,replicaPoolInfo选项中添加- poolName: cstor-disk-pool-p4jr。

    8. 等待pool node instance节点完成数据同步。

4.

    openebs cstor error 11 listsnap

    解决方案,查处哪些PVC出现error 11 listsnap,删除PVC pods,比如,kubectl delete pods -n openebs pvc-033c9690-da5c-4363-a31a-97a563c4b48f-target-788b64454ccll7z

最新文章

分类

关于

Keep thinking, Stay curious
Always be sensitive to new things