runsisi's

technical notes

OpenStack + Ceph + QoS

2019-01-08 runsisi#ceph

QoS

Methods for differentiating services

QoS for Ceph

现状

  1. Ceph 现有代码理论上支持基于消息优先级的 QoS
  2. 当前基于消息的优先级仅用于后端数据迁移过程,是否能够在前端业务层进行支持存疑
  3. 该 QoS 能力太弱,且不支持存储 IO 限速 (可由 OpenStack 前端 QoS 实现)

未来

1. mClock 算法[1]

Hypervisor IO 资源调度算法,支持虚机粒度的 IO 优先级 (Weight) 及最大 (Limit)、最小(Reservation) IOPS 约束

不支持多 hypervisor 节点

mclock

2. dmClock 算法

mClock 算法的扩展,集群分布式存储系统 IO 资源调度算法,支持 mClock 算法所支持的虚机粒度的 RLW 约束

由于是在存储端实现的 IO 资源调度算法,因此支持多 hypervisor 节点

dmclock

3. dmClock for OpenStack + Ceph + QoS

mClock 及 dmClock 只支持基于 IOPS 的 QoS,能否扩展至基于 BW (bandwidth) 不得而知

OpenStack 是一个多 hypervisor 节点的虚拟化平台,因此 mClock 算法并不适用,而 Ceph 作为一个分布式存储系统使用 dmClock 算法实现存储端的 QoS 支持是可行的

cdmclock

4. Ceph + dmClock 现状

社区早在2015年就有对 QoS 的预研[2],且在 Jewel 版本有相应的规划[3]

而在 cohortfs 被 Red Hat 收购 (2015年7月) 之前,cohortfs 的 Crimson 项目也已经有考虑实现 dmClock 算法原型[4]

当前 dmClock 的 C++ 11 实现由前 cohortfs 员工 J. Eric Ivancich 进行积极开发中[5]

总的来说社区有明确的需求规划但无明确的时间表

5. Ceph + dmClock 实现

通过阅读论文[1],dmClock 算法原理本身并不算太复杂,但对于[5]的实现及集成进 Ceph 并无了解,还需要时间进一步分析

Ceph 涉及到的修改主要包括

  1. 前端,包括应用,也即当前主要考虑的 librbd 块设备库,以及 librados 基础库

librbd 层的 QoS spec 设置、读取、应用

librados 层的 dmClock 算法客户端实现,包括状态参数实时统计及下发

  1. 后端 RADOS 层

修改 OSD ShardedOpWQ 队列以支持 dmClock 算法

在 Messenger 上下文处理 Op 并分发Op时入新的支持 dmClock 算法的工作队列,并在 Op 出队时实现 dmClock 服务端算法

  1. Recovery Op 的处理

由于前端业务 IO 及 后端数据迁移 (recovery + backfill) 的 IO 由不同的 Messenger 入相同的 WQ 进行处理,此处需要考虑对已有的 Op 优先级的支持

  1. 虚拟化平台中的 Ceph 驱动

限定到 OpenStack 平台,需要修改 Cinder volume 驱动以支持卷 (volume) 创建时 QoS spec 下发至后端 RBD 驱动并进行解释

QoS for OpenStack

  1. 当前 OpenStack 已有比较完善的 QoS 支持

包括 hypervisor 前端[6]及存储后端 QoS[7]

  1. 关于前端 QoS

通过 libvirt 进行解释,支持 IOPS 和 BW 的限制 (L),不支持预留 (R) 及优先级 (W)

  1. 关于后端 QoS

每个创建的 volume 基于 volume type 进行创建

volume type 关联 QoS spec,而 QoS spec 同样由用户创建,为简单的 <string, string> 键值对

QoS spec 完全由 volume 后端驱动解释,也可以忽略不解释

如果存储后端为 RBD 则虚机中的一个块设备对应 RBD 中的一个 image,因此 QoS 主要在 image 粒度进行处理

参考文献

[1] mClock: Handling Throughput Variability for Hypervisor IO Scheduling

https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Gulati.pdf

[2] https://www.linkedin.com/in/smbillah

Intern
Red Hat
May 2015 – August 2015 (4 months)
Worked on the QoS part of Ceph RADOS. Implemented a customized, scalable prototype of mClock (http://dl.acm.org/citation.cfm?id=1924974) algorithm that runs on OSDs to make sure the Ceph clients could meet their Service Level Objectives (SLO) expressed in terms of three parameters: Reservation (R), Proportional (P), and Limit(L).

[3] RADOS QOS

http://tracker.ceph.com/projects/ceph/wiki/Rados_qos

[4] https://github.com/cohortfsllc/crimson

[5] https://github.com/ceph/dmclock

[6] http://ceph.com/planet/openstack-ceph-rbd-and-qos/

[7] https://github.com/openstack/cinder/tree/master/cinder/volume/drivers

hpe/hpe_3par_common.py
class HPE3PARCommon(object):
  hpe_qos_keys = ['minIOPS', 'maxIOPS', 'minBWS', 'maxBWS', 'latency',
                  'priority']

  def create_volume(self, volume):
    self.get_volume_settings_from_type
      self.get_volume_settings_from_type_id(type_id, pool)
        self.get_type_info(type_id)
          qos = self._get_qos_by_volume_type(volume_type)
    self._add_volume_to_volume_set
      self._set_qos_rule(qos, vvs_name)

hpe/hpe_3par_iscsi.py
class HPE3PARISCSIDriver(driver.TransferVD,
                         driver.ManageableVD,
                         driver.ExtendVD,
                         driver.SnapshotVD,
                         driver.ManageableSnapshotsVD,
                         driver.MigrateVD,
                         driver.ConsistencyGroupVD,
                         driver.BaseVD):
  def create_volume(self, volume):
    common = self._login()
      common = self._init_common()
        return hpecommon.HPE3PARCommon(self.configuration,
                                       self._active_backend_id)

hpe/hpe_3par_fc.py
class HPE3PARFCDriver(driver.TransferVD,
                      driver.ManageableVD,
                      driver.ExtendVD,
                      driver.SnapshotVD,
                      driver.ManageableSnapshotsVD,
                      driver.MigrateVD,
                      driver.ConsistencyGroupVD,
                      driver.BaseVD):
  def create_volume(self, volume):
    common = self._login()
      common = self._init_common()
        return hpecommon.HPE3PARCommon(self.configuration,
                                       self._active_backend_id)

netapp/utils.py
QOS_KEYS = frozenset(['maxIOPS', 'maxIOPSperGiB', 'maxBPS', 'maxBPSperGiB'])

netapp/dataontap/block_base.py
class NetAppBlockStorageLibrary(object):
  def create_volume(self, volume):
    self._setup_qos_for_volume

netapp/dataontap/block_cmode.py
class NetAppBlockStorageCmodeLibrary(block_base.NetAppBlockStorageLibrary):
  def _setup_qos_for_volume(self, volume, extra_specs)

netapp/dataontap/nfs_base.py
class NetAppNfsDriver(driver.ManageableVD,
                      driver.CloneableImageVD,
                      driver.SnapshotVD,
                      nfs.NfsDriver):
  def create_volume(self, volume):
    self._do_qos_for_volume

netapp/dataontap/nfs_cmode.py
class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver):
  def _do_qos_for_volume(self, volume, extra_specs, cleanup=True)

// 比较奇怪,其它厂商都是在 create_volume 处设置 QoS
emc/scaleio.py
class ScaleIODriver(driver.VolumeDriver):
  def initialize_connection(self, volume, connector):
    qos_specs = self._get_volumetype_qos(volume)

solidfire.py
class SolidFireDriver(san.SanISCSIDriver):
  sf_qos_dict = {'slow': {'minIOPS': 100,
                          'maxIOPS': 200,
                          'burstIOPS': 200},
                 medium': {'minIOPS': 200,
                           'maxIOPS': 400,
                           'burstIOPS': 400},
                 'fast': {'minIOPS': 500,
                          'maxIOPS': 1000,
                          'burstIOPS': 1000},
                 'performant': {'minIOPS': 2000,
                                'maxIOPS': 4000,
                                'burstIOPS': 4000},
                 'off': None}
  sf_qos_keys = ['minIOPS', 'maxIOPS', 'burstIOPS']

  def create_volume(self, volume):
    qos = self._retrieve_qos_setting(volume)
    self._do_volume_create(sf_account, params)

huawei/huawei_driver.py
class HuaweiBaseDriver(driver.VolumeDriver):
  def create_volume(self, volume):
    self._create_base_type_volume(opts, volume, volume_type))
      qos = smartx.SmartQos.get_qos_by_volume_type(volume_type)

huawei/constants.py
HUAWEI_VALID_KEYS = ['maxIOPS', 'minIOPS', 'minBandWidth',
                     'maxBandWidth', 'latency', 'IOType']
QOS_KEYS = [i.upper() for i in HUAWEI_VALID_KEYS]