春争日,夏争时,扫码抽取夏日礼包!!! 了解详情
写点什么

分布式存储 Ceph 介绍及原理架构分享(下)

  • 2019 年 9 月 18 日
  • 本文字数:5615 字

    阅读完需:约 18 分钟

分布式存储 Ceph 介绍及原理架构分享(下)

本文由于篇幅过长,分文上下两篇,此篇为下篇。

本文主要从架构简介使用场景,以及内部 IO 流程、心跳机制、通信框架、CRUSH 算法、QOS 等多个方面逐渐介绍分布式存储系统 Ceph 的特性。希望对你有所帮助。


3. Ceph 心跳机制

3.1 心跳介绍

心跳是用于节点间检测对方是否故障的,以便及时发现故障节点进入相应的故障处理流程。


问题:


a. 故障检测时间和心跳报文带来的负载之间做权衡。


b. 心跳频率太高则过多的心跳报文会影响系统性能。


c. 心跳频率过低则会延长发现故障节点的时间,从而影响系统的可用性。


故障检测策略应该能够做到:


及时:节点发生异常如宕机或网络中断时,集群可以在可接受的时间范围内感知。


适当的压力:包括对节点的压力,和对网络的压力。


容忍网络抖动:网络偶尔延迟。


扩散机制:节点存活状态改变导致的元信息变化需要通过某种机制扩散到整个集群。


3.2 Ceph 心跳检测


OSD 节点会监听 public、cluster、front 和 back 四个端口


  • public 端口:监听来自 Monitor 和 Client 的连接。

  • cluster 端口:监听来自 OSD Peer 的连接。

  • front 端口:供客户端连接集群使用的网卡, 这里临时给集群内部之间进行心跳。

  • back 端口:供客集群内部使用的网卡。集群内部之间进行心跳。

  • hbclient:发送 ping 心跳的 messenger。


▍3.3 Ceph OSD 之间相互心跳检测



步骤:


a. 同一个 PG 内 OSD 互相心跳,他们互相发送 PING/PONG 信息。


b. 每隔 6s 检测一次(实际会在这个基础上加一个随机时间来避免峰值)。


c. 20s 没有检测到心跳回复,加入 failure 队列。


3.4 Ceph OSD 与 Mon 心跳检测


OSD 报告给 Monitor:


  • a. OSD 有事件发生时(比如故障、PG 变更)。

  • b. 自身启动 5 秒内。

  • c. OSD 周期性的上报给 Monito

  • d. OSD 检查 failure_queue 中的伙伴 OSD 失败信息。

  • e. 向 Monitor 发送失效报告,并将失败信息加入 failure_pending 队列,然后将其从 failure_queue 移除。

  • f. 收到来自 failure_queue 或者 failure_pending 中的 OSD 的心跳时,将其从两个队列中移除,并告知 Monitor 取消之前的失效报告。

  • g. 当发生与 Monitor 网络重连时,会将 failure_pending 中的错误报告加回到 failure_queue 中,并再次发送给 Monitor。

  • h. Monitor 统计下线 OSD

  • i. Monitor 收集来自 OSD 的伙伴失效报告。

  • j. 当错误报告指向的 OSD 失效超过一定阈值,且有足够多的 OSD 报告其失效时,将该 OSD 下线。


3.5 Ceph 心跳检测总结

Ceph 通过伙伴 OSD 汇报失效节点和 Monitor 统计来自 OSD 的心跳两种方式判定 OSD 节点失效。


及时:


伙伴 OSD 可以在秒级发现节点失效并汇报 Monitor,并在几分钟内由 Monitor 将失效 OSD 下线。


适当的压力:


由于有伙伴 OSD 汇报机制,Monitor 与 OSD 之间的心跳统计更像是一种保险措施,因此 OSD 向 Monitor 发送心跳的间隔可以长达 600 秒,Monitor 的检测阈值也可以长达 900 秒。Ceph 实际上是将故障检测过程中中心节点的压力分散到所有的 OSD 上,以此提高中心节点 Monitor 的可靠性,进而提高整个集群的可扩展性。


容忍网络抖动:


Monitor 收到 OSD 对其伙伴 OSD 的汇报后,并没有马上将目标 OSD 下线,而是周期性的等待几个条件:


  1. 目标 OSD 的失效时间大于通过固定量 osd_heartbeat_grace 和历史网络条件动态确定的阈值。

  2. 来自不同主机的汇报达到 mon_osd_min_down_reporters。

  3. 满足前两个条件前失效汇报没有被源 OSD 取消。


扩散:


作为中心节点的 Monitor 并没有在更新 OSDMap 后尝试广播通知所有的 OSD 和 Client,而是惰性的等待 OSD 和 Client 来获取。以此来减少 Monitor 压力并简化交互逻辑。


4. Ceph 通信框架

4.1 Ceph 通信框架种类介绍

网络通信框架三种不同的实现方式:


Simple 线程模式


特点:每一个网络链接,都会创建两个线程,一个用于接收,一个用于发送。


缺点:大量的链接会产生大量的线程,会消耗 CPU 资源,影响性能。


Async 事件的 I/O 多路复用模式


特点:这种是目前网络通信中广泛采用的方式。k 版默认已经使用 Asnyc 了。


XIO 方式使用了开源的网络通信库 accelio 来实现


特点:这种方式需要依赖第三方的库 accelio 稳定性,目前处于试验阶段。


4.2 Ceph 通信框架设计模式

设计模式(Subscribe/Publish):


订阅发布模式又名观察者模式,它意图是“定义对象间的一种一对多的依赖关系,


当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。


4.3 Ceph 通信框架流程图


步骤:


  • a. Accepter 监听 peer 的请求, 调用 SimpleMessenger::add_accept_pipe() 创建新的 Pipe 到 SimpleMessenger::pipes 来处理该请求。

  • b. Pipe 用于消息的读取和发送。该类主要有两个组件,Pipe::Reader,Pipe::Writer 用来处理消息读取和发送。

  • c. Messenger 作为消息的发布者, 各个 Dispatcher 子类作为消息的订阅者, Messenger 收到消息之后,通过 Pipe 读取消息,然后转给 Dispatcher 处理。

  • d. Dispatcher 是订阅者的基类,具体的订阅后端继承该类,初始化的时候通过 Messenger::add_dispatcher_tail/head 注册到 Messenger::dispatchers. 收到消息。

  • e. DispatchQueue 该类用来缓存收到的消息, 然后唤醒 DispatchQueue::dispatch_thread 线程找到后端的 Dispatch 处理消息。



4.4 Ceph 通信框架类图


4.5 Ceph 通信数据格式

通信协议格式需要双方约定数据格式。


消息的内容主要分为三部分:


· header //消息头,类型消息的信封


· user data //需要发送的实际数据


o payload     //操作保存元数据
o middle //预留字段
o data //读写数据
o footer //消息的结束标记
复制代码


class Message : public RefCountedObject {protected:  ceph_msg_header  header;      // 消息头  ceph_msg_footer  footer;      // 消息尾  bufferlist       payload;  // "front" unaligned blob  bufferlist       middle;   // "middle" unaligned blob  bufferlist       data;     // data payload (page-alignment will be preserved where possible)   /* recv_stamp is set when the Messenger starts reading the   * Message off the wire */  utime_t recv_stamp;       //开始接收数据的时间戳  /* dispatch_stamp is set when the Messenger starts calling dispatch() on   * its endpoints */  utime_t dispatch_stamp;   //dispatch 的时间戳  /* throttle_stamp is the point at which we got throttle */  utime_t throttle_stamp;   //获取throttle 的slot的时间戳  /* time at which message was fully read */  utime_t recv_complete_stamp;  //接收完成的时间戳   ConnectionRef connection;     //网络连接   uint32_t magic = 0;           //消息的魔术字   bi::list_member_hook<> dispatch_q;  //boost::intrusive 成员字段}; struct ceph_msg_header {    __le64 seq;       // 当前session内 消息的唯一 序号    __le64 tid;       // 消息的全局唯一的 id    __le16 type;      // 消息类型    __le16 priority;  // 优先级    __le16 version;   // 版本号     __le32 front_len; // payload 的长度    __le32 middle_len;// middle 的长度    __le32 data_len;  // data 的 长度    __le16 data_off;  // 对象的数据偏移量      struct ceph_entity_name src; //消息源     /* oldest code we think can decode this.  unknown if zero. */    __le16 compat_version;    __le16 reserved;    __le32 crc;       /* header crc32c */} __attribute__ ((packed)); struct ceph_msg_footer {    __le32 front_crc, middle_crc, data_crc; //crc校验码    __le64  sig; //消息的64位signature    __u8 flags; //结束标志} __attribute__ ((packed));
复制代码


5. Ceph CRUSH 算法

5.1 数据分布算法挑战

数据分布和负载均衡:


  1. 数据分布均衡,使数据能均匀的分布到各个节点上。

  2. 负载均衡,使数据访问读写操作的负载在各个节点和磁盘的负载均衡。


灵活应对集群伸缩:


  1. 系统可以方便的增加或者删除节点设备,并且对节点失效进行处理。

  2. 增加或者删除节点设备后,能自动实现数据的均衡,并且尽可能少的迁移数据。


支持大规模集群:


  1. 要求数据分布算法维护的元数据相对较小,并且计算量不能太大。随着集群规模的增 加,数据分布算法开销相对比较小。


5.2 Ceph CRUSH 算法说明

CRUSH 算法的全称为:Controlled Scalable Decentralized Placement of Replicated Data,可控的、可扩展的、分布式的副本数据放置算法。


PG 到 OSD 的映射的过程算法叫做 CRUSH 算法。(一个 Object 需要保存三个副本,也就是需要保存在三个 osd 上)。


CRUSH 算法是一个伪随机的过程,他可以从所有的 OSD 中,随机性选择一个 OSD 集合,但是同一个 PG 每次随机选择的结果是不变的,也就是映射的 OSD 集合是固定的。


5.3 Ceph CRUSH 算法原理

CRUSH 算法因子:


层次化的 Cluster Map


反映了存储系统层级的物理拓扑结构。定义了 OSD 集群具有层级关系的静态拓扑结构。OSD 层级使得 CRUSH 算法在选择 OSD 时实现了机架感知能力,也就是通过规则定义, 使得副本可以分布在不同的机 架、不同的机房中、提供数据的安全性 。


Placement Rules


决定了一个 PG 的对象副本如何选择的规则,通过这些可以自己设定规则,用户可以自定义设置副本在集群中的分布。


5.3.1 层级化的 Cluster Map


CRUSH Map 是一个树形结构,OSDMap 更多记录的是 OSDMap 的属性(epoch/fsid/pool 信息以及 osd 的 ip 等等)。


叶子节点是 device(也就是 osd),其他的节点称为 bucket 节点,这些 bucket 都是虚构的节点,可以根据物理结构进行抽象,当然树形结构只有一个最终的根节点称之为 root 节点,中间虚拟的 bucket 节点可以是数据中心抽象、机房抽象、机架抽象、主机抽象等。


5.3.2 数据分布策略 Placement Rules

数据分布策略 Placement Rules 主要有特点:


  1. 从 CRUSH Map 中的哪个节点开始查找

  2. 使用那个节点作为故障隔离域

  3. 定位副本的搜索模式(广度优先 or 深度优先)


rule replicated_ruleset  #规则集的命名,创建pool时可以指定rule集
{
ruleset 0 #rules集的编号,顺序编即可
type replicated #定义pool类型为replicated(还有erasure模式)
min_size 1 #pool中最小指定的副本数量不能小1
max_size 10 #pool中最大指定的副本数量不能大于10
step take default #查找bucket入口点,一般是root类型的bucket
step chooseleaf firstn 0 type host #选择一个host,并递归选择叶子节点osd
step emit #结束
}
复制代码


5.3.3 Bucket 随机算法类型


一般的 buckets:适合所有子节点权重相同,而且很少添加删除 item。


list buckets:适用于集群扩展类型。增加 item,产生最优的数据移动,查找 item,时间复杂度 O(n)。


tree buckets:查找负责度是 O (log n), 添加删除叶子节点时,其他节点 node_id 不变。


straw buckets:允许所有项通过类似抽签的方式来与其他项公平“竞争”。定位副本时,bucket 中的每一项都对应一个随机长度的 straw,且拥有最长长度的 straw 会获得胜利(被选中),添加或者重新计算,子树之间的数据移动提供最优的解决方案。


5.4 Ceph CRUSH 算法案例

说明:


集群中有部分 sas 和 ssd 磁盘,现在有个业务线性能及可用性优先级高于其他业务线,能否让这个高优业务线的数据都存放在 ssd 磁盘上。


普通用户:



高优用户:



配置规则:



6. 定制化 Ceph RBD QOS

6.1 QOS 介绍

QoS (Quality of Service,服务质量)起源于网络技术,它用来解决网络延迟和阻塞等问题,能够为指定的网络通信提供更好的服务能力。


问题:


我们总的 Ceph 集群的 IO 能力是有限的,比如带宽,IOPS。如何避免用户争抢资源,如何保证集群所有用户资源的高可用性,以及如何保证高优用户资源的可用性。所以我们需要把有限的 IO 能力合理分配。


6.2 Ceph IO 操作类型

ClientOp:来自客户端的读写 I/O 请求。


SubOp:osd 之间的 I/O 请求。主要包括由客户端 I/O 产生的副本间数据读写请求,以及由数据同步、数据扫描、负载均衡等引起的 I/O 请求。


SnapTrim:快照数据删除。从客户端发送快照删除命令后,删除相关元数据便直接返回,之后由后台线程删除真实的快照数据。通过控制 snaptrim 的速率间接控制删除速率。


Scrub:用于发现对象的静默数据错误,扫描元数据的 Scrub 和对象整体扫描的 deep Scrub。


Recovery:数据恢复和迁移。集群扩/缩容、osd 失效/从新加入等过程。


6.3 Ceph 官方 QOS 原理


mClock 是一种基于时间标签的 I/O 调度算法,最先被 Vmware 提出来的用于集中式管理的存储系统。(目前官方 QOS 模块属于半成品)。


基本思想:


  • reservation 预留,表示客户端获得的最低 I/O 资源。

  • weight 权重,表示客户端所占共享 I/O 资源的比重。

  • limit 上限,表示客户端可获得的最高 I/O 资源。


6.4 定制化 QOS 原理

6.4.1 令牌桶算法介绍


基于令牌桶算法(TokenBucket)实现了一套简单有效的 qos 功能,满足了云平台用户的核心需求。


基本思想:


  • 按特定的速率向令牌桶投放令牌。

  • 根据预设的匹配规则先对报文进行分类,不符合匹配规则的报文不需要经过令牌桶的处理,直接发送。

  • 符合匹配规则的报文,则需要令牌桶进行处理。当桶中有足够的令牌则报文可以被继续发送下去,同时令牌桶中的令牌量按报文的长度做相应的减少。

  • 当令牌桶中的令牌不足时,报文将不能被发送,只有等到桶中生成了新的令牌,报文才可以发送。这就可以限制报文的流量只能是小于等于令牌生成的速度,达到限制流量的目的。


6.4.2 RBD 令牌桶算法流程


步骤:


  • 用户发起请求异步 IO 到达 Image 中。

  • 请求到达 ImageRequestWQ 队列中。

  • 在 ImageRequestWQ 出队列的时候加入令牌桶算法 TokenBucket。

  • 通过令牌桶算法进行限速,然后发送给 ImageRequest 进行处理。


6.4.3 RBD 令牌桶算法框架图

现有框架图:



令牌图算法框架图:



本文转载自公众号滴滴技术(ID:didi_tech)。


原文链接:


https://mp.weixin.qq.com/s/6CyQsY06Ny7Fi-jy19fjEA


2019 年 9 月 18 日 12:261802

评论

发布
暂无评论
发现更多内容

三个维度,透视5G价值的持续点亮之旅

脑极体

详解Redis主从复制原理

蘑菇睡不着

Java redis

ZooKeeper实战

CodeWithBuff

Java zookeeper

Rust从0到1-函数式编程-迭代器

rust 函数式编程 Iterator 迭代器

研发管理工具 ONES 完成3亿人民币 B1 B2 轮融资,继续领跑研发管理赛道

万事ONES

项目管理 融资 研发管理工具 ONES

毕业论文被不小心删除了,有什么方法可以恢复?

淋雨

EasyRecovery 文件恢复 硬盘数据恢复

Dapr:我不是Service Mesh!我只是长得很像

中原银行

云原生 Service Mesh istio Multi-Architecture dapr

【源码篇】Flutter Bloc背后的思想,一篇纠结的文章

小呆呆666

flutter ios android 大前端

推荐一个MySQL宝藏网站

Simon

MySQL 网站

「2021中国峰会同行记」第一回 | 与埃森哲一同追溯技术合力的本源

亚马逊云科技 (Amazon Web Services)

lockSupport怎么玩

卢卡多多

锁机制 6月日更

成为你想要看到的改变,首先就是让正确的事情持续的发生。

叶小鍵

趣谈Java类加载器

程序猿阿星

Java ClassLoader 类加载器

迪士尼将亚马逊云科技作为首选的公有云基础设施供应商,支持 Disney+ 全球扩展

亚马逊云科技 (Amazon Web Services)

我是如何用 ThreadLocal 虐面试官的?

陈皮的JavaLib

Java 面试 多线程 ThreadLocal

密码学系列之:twofish对称密钥分组算法

程序那些事

加密解密 密码学 程序那些事

ONLYOFFICE-基本组成及工作原理

一个需求

onlyoffice

前端面试中有趣的题目(一)

空城机

JavaScript 大前端 6月日更

react native实践总结与思考

碗盆

android 跨平台 React Native

python 连接钉钉传输工作数据监控

百里丶落云

Linux之less命令

入门小站

Linux

数据结构——顺序栈

若尘

数据结构 6月日更

【源码篇】Flutter Provider的另一面(万字图文+插件)

小呆呆666

flutter ios android 大前端

过一过Java“锁”事

CodeWithBuff

Java 并发 同步

如果非要在多线程中使用 ArrayList 会发生什么?(第二篇)

看山

Java 并发编程

[译] R8 优化: Lambda Groups

Antway

6月日更

「2021中国峰会同行记」第二回 | 探索店匠从0到1出海的技术密码

亚马逊云科技 (Amazon Web Services)

人工智能应用架构的思考

金科优源汇

建信金科大咖访谈:地方特色产业互联网建设思考与实践

金科优源汇

领域驱动设计101 - 模块

luojiahu

领域驱动设计 DDD

谈一谈Java的网络编程

CodeWithBuff

Java 网络io

分布式存储 Ceph 介绍及原理架构分享(下)_文化 & 方法_李航_InfoQ精选文章