Ceph的可靠性恢复原理: Scrub机制
Scrub是Ceph集群进行的副本间的数据扫描操作,以检测副本间的数据一致性,包括Scrub和Deep-Scrub,其中Scrub只是对元数据信息进行扫描,相对比较快,而Deep-Scrub不仅对元数据进行扫描,还会对存储的数据进行扫描,相对比较慢。Ceph集群会定期进行Scrub操作。
Scrub默认策略是每天到每周(如果集群负荷大周期就是一周,如果集群负荷小周期就是一天)进行一次,时间区域默认为全天(0时-24时),Deep-Scrub默认策略是每周一次。
Ceph进行数据读写时并不会增加校验数据来保证数据的正确性(类似ECC、CheckSum等),只能通过内部的Scrub机制来校验数据。
Scrub流程是以PG为单位定期触发的,由该 PG 的Master 角色所在 OSD 启动;每次Scrub会扫描一个PG内的部分对象,校验期间对象的数据是不允许修改。
校验信息包括每个对象的元信息如大小、扩展属性的所有键和历史版本信息等,在Ceph 中被称为 ScrubMap。发起者会比较多个ScrubMap并发现不一致的对象,不一致对象会被收集最后发送给 Monitor,由用户手工发起修复。
Ceph允许通过Deep Scrub模式来全量比较对象信息来期望发现Ceph 本身或者文件系统问题,这通常会带来较大的 IO 负担,较少在生产环境使用。
当然,Ceph Scrub机制存在的问题。在发现不一致对象后,缺少策略来自动矫正错误,比如如果多数副本达成一致,那么少数副本对象会被同化。Scrub 机制并不能及时解决存储系统端到端正确的问题,很有可能上层应用早已经读到错误数据,下面一起来看看Scrub的工作流程:
① OSD 会以 PG 为粒度触发 Scrub流程,触发的频率可以通过选项指定,而一个PG的Scrub启动都是由该 PG 的 Master 角色所在OSD启动。
② 一个PG在普通的环境下会包含几千个到数十万个不等的对象,因为Scrub流程需要提取对象的校验信息然后跟其他副本的校验信息对比,这期间被校验对象的数据是不能被修改的。因此一个PG的Scrub流程每次会启动小部分的对象校验,Ceph 会以每个对象名的哈希值的部分作为提取因子,每次启动对象校验会找到符合本次哈希值的对象,然后进行比较。这也是 Ceph称其为Chunky Scrub的原因。
③ 在找到待校验对象集后,发起者需要发出请求来锁定其他副本的这部分对象集。因为每个对象的Master和Replicate节点在实际写入到底层存储引擎的时间会出现一定的差异。这时候,待校验对象集的发起者会附带一个版本发送给其他副本,直到这些副本节点与主节点同步到相同版本。
④ 在确定待校验对象集在不同节点都处于相同版本后,发起者会要求所有节点都开始计算这个对象集的校验信息并反馈给发起者。
⑤ 该校验信息包括每个对象的元信息如大小、扩展属性的所有键和历史版本信息等等,在Ceph 中被称为 ScrubMap。
⑥ 发起者会比较多个ScrubMap并发现不一致的对象,不一致对象会被收集最后发送给 Monitor,最后用户可以通过Monitor了解Scrub的结果信息。
另外,当用户在发现出现不一致的对象时,可以通过 “ceph pgrepair [pg_id]” 的方式来启动修复进程,目前的修复仅仅会将主节点的对象全量复制到副本节点,因此目前要求用户手工确认主节点的对象是“正确副本”。此外,Ceph允许Deep Scrub模式来全量比较对象信息来期望发现 Ceph 本身或者文件系统问题,这通常会带来较大的IO负担,因此在实际生产环境中很难达到预期效果。
通过上述Scrub流程,大家也会发现目前的 Scrub机制还存在以下2个问题:
① 在发现不一致对象后,缺少策略来自动矫正错误,比如如果多数副本达成一致,那么少数副本对象会被同化。
② Scrub 机制并不能及时解决存储系统端到端正确的问题,很有可能上层应用早已经读到错误数据。
对于第一个问题,目前Ceph已经有Blueprint来加强Scrub的修复能力,用户启动Repair时会启动多数副本一致的策略来替代目前的主副本同步策略。
对于第二个问题,传统端到端解决方案会更多采用固定数据块附加校验数据的“端到端校验”方案,但是 Ceph 因为并不是存储设备空间实际的管理和分配者,它依赖于文件系统来实现存储空间的管理,如果采用对象校验的方式会严重损耗性能。
因此在从文件系统到设备的校验需要依赖于文件系统,而Ceph包括客户端和服务器端的对象正确性校验只能更多的依赖于ReadVerify机制,在涉及数据迁移时需要同步的比较不同副本对象的信息来保证正确性。目前的异步方式会允许期间发生错误数据返回的可能性。