王豪迈
作者王豪迈·2017-01-10 10:32
CTO·星辰天合(XSKY)

解析Ceph: Librbd 的克隆问题

字数 1517阅读 2546评论 0赞 0

在较早的文章就指出 Librbd 在卷的克隆时会形成子卷对父卷的依赖,在产生较长的克隆依赖链后会有严重的性能损耗。OpenStack Cinder 会有一个选项专门用来 flatten 这种较长依赖链,在超过阀值后选择强制 flatten。本篇文章主要会阐述这种依赖的问题和根源,并且给出目前的解决方案。

卷的克隆

在 Librbd 中,每个卷实际上是由一个 metadata object 进行维护并管理卷元数据和一对 data objects 构成。普通的读写 IO 不会修改 metadata object 而只会修改 data objects,且 metadata object 不含有 data objects 的任意信息,只有针对卷的操作如创建快照、克隆、修改大小等重量级操作才会修改 metadata object(每个客户端会维持一个 refresh id,当发生这种重量操作时,通过 Ceph Notification 机制可以通知所有该卷的客户端需要重新获取最新的 metadata object)。这样的好处是,在正常读写 IO 逻辑下,客户端是不需要修改 metadata object,这样就可以将 metadata object 看成是只读信息。多个客户端就可以同时对一个卷进行 IO 读写,因为 data object 在 OSD 端是线性化的。

而克隆操作本质上复制了一个 metadata object,并且链接向父卷,而 data objects 是不存在的。因此在每次读操作时会先向本卷可能的 data object 访问。在返回对象不存在错误后会向父卷访问对应的对象最终决定这块数据是否存在。因此当存在多个层级的克隆链后,读操作需要更多的损耗去读上级卷的 data objects。只有当本卷的 data object 存在后(也就是写操作后),才不需要访问上级卷。

卷索引表

因此,自然而然我们就需要解决这个问题,就是为每个卷创建一个索引来避免额外的父卷读取操作。在 Ceph Giant 版本的 CDS 上就提出了这个 BP (https://wiki.ceph.com/Planning/Blueprints/Hammer/librbd%3A_shared_flag%2C_object_map)。主要有两种实现思路:

索引数据是不可靠的: 只允许一个客户端操作一个卷,索引数据在内存中更新并且阶段性持久化。这样的好处是最大程度减少正常读写 IO 额外损耗,但是会在客户端 Crash 后造成索引数据丢失。因此客户端需要假设持久化的索引是不可靠的,并且在实际的 IO 操作后”训练”索引
索引数据是可靠的: 在每次需要更新索引数据的 IO 操作中附加更新索引数据的 IO,因此会增加正常读写 IO 的负载,但是可以让索引数据真实可信
在上两个月,作为 BP 的作者先实现了第一个方案,在 maillist 的讨论中”惨遭”婉拒。后来因为要参与到 Messenger 的重构中,因此本来打算改成第二个方案的实现也拖后了。现在 Redhat 的 Jason 在跟我邮件沟通后毅然承担起了重新实现第二种方案的大梁,目前已在 PR list 中。

卷索引副产品

在实现卷索引后,会导致之前卷的多客户端特性遭到挑战。因此索引数据更新相对来说仍然是个比较常规的数据操作,如果依赖于 Ceph Notification 会造成严重的性能损耗,因此会使得一个卷只能被一个客户端进行读写 IO 操作。

通过卷索引后,Librbd 可以支持更强大的统计功能了,可以根据卷索引信息统计父卷和子卷的差距来有效统计子卷目前实际占用大小。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

X社区推广