内容简介:写在前面话题扯远了,回到这篇文章,文章的标题包含“复盘”,顾名思义,是对以前的发生的现象或问题进行回顾,学而不思则罔,目的第一是从问题中总结经验,最大化发掘它的价值;第二是不断锻炼自己分析问题的能力;最后是希望强化在某项知识上的运用能力,毕竟以今天分析昨天,有思维就ok了,但是以今天设想未来,需要长期深厚的积累才行。
写在前面
刚刚过去的9月,人工智能、云计算和物联网界热闹非凡,接连迎来了世界物联网博览会、世界人工智能大会和阿里云栖大会。2018世界物联网博览会就在家门口举行,抓了空去现场看展览,外行人看看热闹,有感于科技的日新月异给生活带来的便利。
话题扯远了,回到这篇文章,文章的标题包含“复盘”,顾名思义,是对以前的发生的现象或问题进行回顾,学而不思则罔,目的第一是从问题中总结经验,最大化发掘它的价值;第二是不断锻炼自己分析问题的能力;最后是希望强化在某项知识上的运用能力,毕竟以今天分析昨天,有思维就ok了,但是以今天设想未来,需要长期深厚的积累才行。
1 背景
云硬盘的快照、克隆,属于块存储RBD基本功能,在很多版本的openstack云平台中我们都使用过,之前我并没有太多关注这两块,保证功能能用、好用就过去了。
不过在最近的产品中,遇到几个与克隆或快照相关联的bug,涉及到rbd clone、rbd flatten等,正好是之前未遇到过,因此找了时间对产生的bug复盘,也是希望借bug观察ceph对克隆和快照的处理流程。
整篇文章分以下几小章节:
1 、背景
2 、bug回顾
3、 利用rbd_max_clone_depth触发flatten
4 、cinder和ceph层面对clone、flatten的实现
2 bug回顾
2.1 bug1 通过快照创建云硬盘,删除父快照失败
这个问题复现步骤很简单,如下流程图所示:
遇到的问题就是在删除快照时发生失败,禁止该删除操作。
由快照创建出的云硬盘,一般情况与快照是父子关系,在ceph下通过rbd info或rbd children可以查询到链的关系。 如下图红色框中所示,在parent一项中,可以看到volume-8e81a7b0-4fdc-49b0-a9ed-4124c8e61f7d是volume-117078c1-c724-44b5-a271-e0f708e9d6b3下的快照克隆得来:
图1
既然克隆盘与快照存在父子关系,要删除父快照的首要条件是斩断这种依赖关系,这个就需要通过 rbd_flatten_volume_from_snapshot配置项来实现,见/etc/cinder/cinder.conf配置文件,如下图中:
在我们的云环境下, rbd_flatten_volume_from_snapshot=false,在false条件下volume和snapshot之间的关系是什么?
在解决bug之前,先来了解下 rbd_flatten_volume_from_snapshot在true、false下的不同作用,见下表:
一目了然,在false条件下, volume和snapshot存在依赖,要想解决该bug,只要开启为true即可。
不过先别急着改/etc/cinder/cinder.conf,在此之前我们通过flatten手动解除依赖关系,再重复bug的步骤看是否能够成功,这个小实验分5个步骤复现,如下图3中:
(1)创建volume和快照 PASS
(2)执行克隆 PASS
(3)删除父快照 FAILED
错误提示需要对快照先去除保护,unprotect后再执行删除快照,错误提示:cannot unprotect: at least 2 child(ren) [1a5e266b8b4567,1aa62d6b8b4567] in pool
(4)执行flatten操作,解除依赖 PASS
(5)再次删除父快照 PASS
小实验OK,证明了解除克隆盘与snapshot的依赖后再删除快照成功。如果在控制节点的cinder配置文件中,开启了rbd_flatten_volume_from_snapshot = true,则 由快照创建出的云硬盘,会自动合并,清除依赖关系,这样一来这个云硬盘就变为扁平的没有层级的volume。最后记得重启cinder服务,使其生效!
2.2 bug2 大容量的空盘创建快照,再通过该快照创建云硬盘,耗时过长
这个bug提的比较优秀,功能本身属于正常流程,但之前遗漏了大容量云硬盘这个场景,比如1T、2T。
问题复现步骤同bug1,只是少了删除快照的步骤,如下流程图所示:
通常,我们在云平台上对云硬盘创建快照后,会同时创建快照卷,由于精简配置的属性,只需分配相对少量的存储空间即可,当再通过该快照clone出云硬盘,快照处于只读保护, 在cow的机制下,克隆操作会很迅速。下面引用一张ceph官方的图来解释:
上图4中,parent是指源云硬盘的快照,而child是从快照克隆出来的云硬盘。
这个bug 2与“2.1 bug 1”对比,相同点都是由 rbd_flatten_volume_from_snapshot 造成的bug,不同的地方在于true和false。
在bug 2 的云环境下, rbd_flatten_volume_from_snapshot =true,在上文的bug1中曾说过解除volume和snapshot的依赖关系,取消这种依赖关系叫做flatten,这个flatten花费的时间和源云硬盘(volume)的大小成正比。
回到bug本身,内部在做排查时,依照以下的顺序:
(1)检查volume qos
眼光先放在了volume qos上,在有数据条件下,qos速率大小(比如write=40MB/S)肯定是会影响到快照创建云硬盘的速度的。转念一想,云硬盘是空盘,并不存在任何object,因此克隆速度应该是很快的。
(2)检查父云硬盘、快照、子云硬盘的实际容量
我们环境中云硬盘是空盘,不存在任何数据,同样的由快照创建出的新盘也不会有任何数据。实际是否如此,通过下面的验证步骤来证实一下:
-
创建云硬盘和快照,并获取真实存储空间
-
图5
图5中,新建1G的volume,并创建该盘的快照后,rados查询实际存在的object,找不到任何数据,这是正确的。
-
由快照创建云硬盘,并获取真实存储空间
图6
图6中,快照创建出新的云硬盘,叫volume-d7199f3d-ed96-446a-83c8-25083a752e23,可以看到在云硬盘创建过程中,新的云硬盘和快照时父子关系,创建成功后,新的云硬盘和快照时父子关系被解除。
图7所示是获取新的云硬盘的实际数据对象,发现已经存在256个object(父云硬盘总容量为1GB,根据order 22 (4096 kB objects)来切分)
图8中,随机抽查几个object,发现其实这些object的容量都是0,并不存在真实的数据。一般而言,从快照创建云硬盘,代码实现很简单,先克隆再flatten,Fill clone with parent data (make it independent),此时flatten会将所有块从父节点复制到child,但父云硬盘中没有数据,flatten操作是不应该产生object的。
这个bug问题就在于flatten会对新建云盘的每一个对象进行一个写操作,从而创建无数个大小为0的对象,又在qos的限制下,所以耗时较长。最终解决方法就是:根据快照的object_map,跳过不存在的对象,只发送有数据的对象即可。
3 利用rbd_max_clone_depth触发flatten
麦子迈在《解析Ceph: Librbd 的克隆问题》一文中提到 “Librbd 在卷的克隆时会形成子卷对父卷的依赖,在产生较长的克隆依赖链后会有严重的性能损耗”。这个理论其实和cow下多快照产生的性能衰减是一样的,对ceph的云硬盘做快照,每次做完快照后再对云硬盘进行写入时就会触发COW操作, 即1次读操作、2次写操作,volume→volume的克隆本质上就是将 volume 的某一个 Snapshot 的状态复制变成另一个volume。
为解决在产生较长的克隆依赖链后会有严重的性能损耗问题,在OpenStack Cinder 的/etc/cinder/cinder.conf中提供一个参数,可以解除父子依赖关系,在超过自定义设置的阀值后选择强制 flatten。
在图9中,通过 rbd_max_clone_depth来控制最大可克隆的层级。
rbd_max_clone_depth = 5 这个参数控制卷克隆的最大层数,超过的话则使用 fallten。设为 0 的话,则禁止克隆。
为了验证这个过程,下面我们做个实验,创建1个volume,命名为01,依次复制下,即由01复制成02,02复制为03,03复制为04,04复制为05,05复制为06,06复制为07,如下图流程图:
实验预期结果,就是当从06复制到07时,满足rbd_max_clone_depth > 5,此时触发flatten操作。
图10
图11
图10、图11是 复制云硬盘后的查询到克隆盘信息
图12
图12中, 上面的log记录了复制07时,触发了flatten操作,对上级云硬盘06执行flatten操作,开始执行合并。
图13
图13所示是Flatten成功后,可以看到云硬盘06 的parent一项消失,此时在页面上可以删除云硬盘06
4 cinder和ceph层面对clone、flatten的实现
现在市面上很多讲ceph的书(大多数翻译自ceph中国社区之手),在RBD块存储章节都会对快照、克隆等操作花很多篇幅去描述,基本都是在rbd层通过命令一步步分解rbd clone过程来讲原理。
对于类似我这样的刚接触ceph不久的人来说,知识点分散在各处,看了前面忘了后面,很难在脑子里建立完整的概念,当然主要原因还是自己太菜了,迷雾重重看不透!
言归正传,我只是想大概的了解下对云硬盘执行操作在底层是如何实现的,因此还是由上文中提到的小处(bug)来入手,自顶向下先设计一个思考流程,带着目标按照这个从上到下的顺序去理解,如下图所示:
4.1 从快照克隆卷的流程
(1)openstack cinder
自顶向下,先从cinder层入手,通过代码可以看到从快照克隆出volume的思路,从本质上讲,快照克隆出新的卷,也是volume create的性质,所以先来了解下volume create过程
cinder:/cinder/api/v2/volumes.py
volumes.py中def create方法我省略了很多,主要就是通过req、body的参数来获取创建volume所需要的参数,根据不同参数来发送具体的创建volume请求,因为我是从快照来创建,snapshot id自然必不可少,在 volumes.py最后实际调用new_volume = self.volume_api.create()去实现。
cinder:/cinder/volume/api.py
经过volume_api.create(),在/cinder/volume/api.py来处理前端发来的卷相关的所有请求,通过create_what{}表示volume的实现参数,然后分别就调用cinder.scheduler的scheduler_rpcapi,cinder.volume的 volume_rpcapi建立创建volume的工作流:create_volume.get_flow
注:关于create volume flow的流程及具体实现,见/cinder/volume/rpcapi.py:def create_volume(),/cinder/volume/flows/api/create_volume.py,本篇省略过程
cinder:/cinder/volume/manager.py
对于api来讲,只是做到处理前端发来的卷相关的所有请求,具体实现交由manager下的去完成,rpcapi调用inder/volume/manager.py:def create_volume()去操作
执行中发现crate voluem 有snapshot id,然后调用/cinder/volume/flows/manager/create_volume.py下的私有方法_create_volume_from_snapshot()
最后根据配置文件指定的RBD后端请求/cinder/volume/drivers/rbd.py的create_volume_from_snapshot()
cinder:/cinder/volume/drivers/rbd.py
众所周知,一般cinder使用RBD驱动来对接底层的后端存储(比如ceph、xsky),在openstack cinder层面最终交由create_volume_from_snapshot()实现,因为是通过快照来创建volume,还需要调用私有方法_clone(),满足条件的话,还要调用_flatten()和_resize()。
(2)librbd
经历多方接力才结束在cinder层面的流程,这还不算完,真正要实现create volume from snapshot的创建,核心在调用ceph执行。
ceph:/src/pybind/rbd/rbd.pyx
/ceph/blob/v10.2.3/src/librbd/librbd.cc
在librbd中对外提供api在class RBD中,从librbd.cc函数中看到有多个clone()、clone2()、clone3()函数,区别在于根据传入的不同参数来调用对应的函数,但这些函数都不像是具体的功能实现,只是一些相关参数传值。
再看看/ceph/blob/v10.2.3/src/librbd/internal.cc函数,同librbd.cc一样,对应的clone()也是3种,因为篇幅如下展示的是clone3()函数(实际命名并不如此,通过参数来区分得知是clone2):
将librbd.cc、internal.cc两个函数联系起来看,librbd.cc只是定义了对外的各种函数接口,接口的具体实现,调用的还是internal.cc中定义的函数内容。
总结一下,根据自己的理解将整个流程绘成图,如下图所示中,需要一提的是,我没有涉及到librados的实现过程,因为clone等volume的操作,librbd可以说就是rbd的完整实现,rados只是作为后端的存储
4.2 flatten的流程
在前文“ 利用rbd_max_clone_depth触发flatten”小节中,我们描述了一个volume clone的过程,通过cinder.conf的一个参数,当满足rbd_max_clone_depth最大层数后,触发flatten操作,下面我们通过代码去看一看具体实现的流程。
(1)openstack cinder
对于上层云平台而言,从云硬盘1克隆出云硬盘2,或者从快照创建云硬盘,一般是能够触发flatten操作的主要场景,其实两者实现原理基本一致。
因此,和之前的由snapshot来实现创建新的云硬盘一样,首要都是从create()开始,只是参数不同,克隆盘在create过程先要获取parent volume id
之后也是一样经历api→manager→driver的过程,这里省掉重复的过程,直接看cinder调用rbd驱动对克隆云硬盘的实现代码,如下图中/cinder/volume/drivers/rbd.py:
调用了私有方法_get_clone_depth()来判断depth,调用_flatten()来实现flatten操作,当然flatten过程经历一系列过程,在parent volume上创建snapshot,对snapshot加保护、再执行clone,然后flatten,这个过程一样可以通过rbd 命令来完成。
(2)librbd
创建RADOSClient,连接到ceph rados,这里也是先调用clone()去执行,再触发flatten()操作,和我预期不同,flatten的过程比想象中还要复杂,才疏学浅,对整个过程的了解还需要更多的时间,只能先用根据自己的理解画出一张流程图表示一下:
以上所述就是小编给大家介绍的《别让bug跑了,通过问题理解ceph的克隆过程 荐》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming Collective Intelligence
Toby Segaran / O'Reilly Media / 2007-8-26 / USD 39.99
Want to tap the power behind search rankings, product recommendations, social bookmarking, and online matchmaking? This fascinating book demonstrates how you can build Web 2.0 applications to mine the......一起来看看 《Programming Collective Intelligence》 这本书的介绍吧!