首页 > 编程知识 正文

MIRROR组合,mirror彩蛋

时间:2023-05-03 22:17:42 阅读:105846 作者:1583

rbd-mirror技术的内部人士(这篇文章是在发送微信号后搬过来的)很清楚,当ceph发布Jewel版本时,release具有名为rbd mirroring的块存储的重要特性rbd mirroring是两个群集之间的异步镜像机制。 通过rbd-mirror服务,依赖于image的journaling特性来实现群集之间的一致image复制。

(1) rbd-mirror介绍

如上图所示,每个cluster都有一个RBD MIRROR模块,您可以在本地同步远程cluster中的image更改。 其中RBD MIRROR模块是一种称为rbd-mirror服务的新服务。

)2) rbd mirroring配置管理

rbd mirroring的配置是通过rbd mirror子命令完成的,如下所示:

现在,您可以看到localcluster的rbd pool启动了镜像模式。 Info的结果包含两个信息。

a )模式: pool。

rbd mirror目前支持两种模式的mirroring:image模式,用户必须手动指定需要mirror的image。 第二种模式是pool模式,该模式会为pool中的所有image自动启动镜像。 今后rbd引入namespace后,可能会出现新的mirror模式,只制作一个namespace的mirror。

b ) peers :0 ba 36216-787 c-4085-9272-bf1e 50516129

该UUID由remote的cluster作为local cluster的peer的UUID,记录了远程地区的信息。 如上图所示,启动rbd-mirror服务时,rbd-mirror进程会去查找remote.conf,将其作为remote的配置文件,并使用client.admin用户的信息

为了更透彻地理解这个过程,我们必须在code上dive,以便更清楚地看到。

从上面的图中可以看出,我们在mirror pool enable的时候,实际上进行了大约6个步骤的操作。

luser :运行" rbdmirrorpoolenablepoolpool "命令

在命令行中执行shell命令并调用rbd可执行文件。 此过程与所有其他linux命令执行过程没有区别。

lrbdcommand :调用API:rbd.mirror _ mode _ set (;

rbd可执行文件运行并解析参数后,转至代码文件src/tools/rbd/action/mirror pool.cc。 此文件处理所有rbd mirrorpool子命令。 然后解析为下一个参数enable。 函数: execute_enable_disable (); 此函数处理enable和disable操作。 此函数调用librbd的api。

至此,整个过程的处理从rbd command交给了librbd。

L3.libRBD:mirror_mode_set (开始请求: cls _ client 3360: mirror _ mode _ set );

在libRBD中,首先接收api请求的是rbd类。 然后RBD类调用librbd内部的相应处理函数(src/librbd/librbd.cc )。

然后,librbd的内部函数会检查当前的mirror mode,如果与要设置的mode相同,则直接返回。

如果mode需要修改,请进行mode修改。 中间有很多准备和检查操作,最重要的是下面的代码。

调用cls_client模块的界面进行模式设置。

l 4. cls_client :发送远程呼叫请求: OSDOP(ceph_OSD_op_call ) ) ) ) )。

首先,需要介绍cls模块。 cls/cls_client实现了远程呼叫机制。 客户端封装要在cls_client模块中执行的操作,并将CEPH_osd_OP_CALL的osdOp发送给OSD,OSD在接收到命令后执行要调用的函数并进行适当的处理。

在此实用方案中,需要进行mode_set操作。 不用说,让我们直接看看cls_client是如何实现的。

一共做了两件事:

(1)第1378行,将输入输入到in_bl中,即我们想要设定的模式。 该in_bl按照osdOp传递给osd端,osd可以根据in_bl得到想要设定的模式值。

/p>

第二:1382行,调用ioctx的exec()。Ioctx 是一个连接到指定pool的io上下文。Ioctx的exec()函数接受五个参数:第一个参数指明需要作用的object名字,在这里是RBD_MIRRORING,实际上是一个在rbd_types.h中的宏定义:

所以我们这次的请求是作用在rbd_mirroring这个object当中的。这个object是mirror相关的所有元数据对象。第二个参数指明是哪个模块的请求,我们这里是rbd,第三个参数指明需要调用的函数名,这里是"mirror_mode_set"。第四个参数是输入的bufferlist,这里的in_bl包含了我们想要设置的mode值。第五个参数是out_bl,表示返回值的bufferlist。

 

exec()函数接受这五个参数之后,会做这么几件事情,新建一个::ObjectOperationrd;然后构造成一个Objecter::Op,通过objecter->submit_op()提交这个op给osd。并且等待结果返回。

 

这个过程涉及到client和osd 交互的设计,也是很有意思,不过不是本文的重点,可以另外开一个主题进行详细分析。这里我们只需要知道,ioctx->exec() 将远程调用的请求发送给了osd,并等待执行结果,就行了。

 

l  5. cls:根据远程调用请求,执行操作:

mirror_mode_set() {

     cls_cxx_map_set_val();

}

 

在osd端,osd接收到OSDOp,分发到PrimaryPG进行处理:


do_osd_ops()就做了一件事,打开我们指定的class(4993行),也就是rbd,然后找到我们指定的method(4996行),也就是mirror_mode_set,然后执行这个method(5011行)。

 

那osd端cls的mirror_mode_set()又做了些什么呢?在src/cls/rbd/cls_rbd.cc中,可以看到,函数mirror_mode_set();


操作很简单,调用cls_cxx_map_set_val()设置了rbd_mirroring这个object的一个key-value值,ceph里面这种值叫做omap值。

 

至此,rbd_mirroring的mode值已经修改结束了。我们可以看到通过rados命令确认一下修改结果:


可以看到 mirror_mode的结果是2,也就是我们设置的pool 模式。


以上就是命令rbd mirror pool enable rbd pool 的整个执行过程,这样我们就已经将rbdpool的mirror 模式改成了pool 模式。现在我们还需要通过命令:rbd --cluster local mirror pool peer add rbdclient.admin@remote 给rbd pool 添加一个peer。这个过程和上面类似,区别就在于enable是修改mirror_mode的值,add peer是添加一个omap值到rbd_mirroring的object里面。

 

至此,一个pool的mirroring 配置就结束了,接下来需要做的就是启动rbd-mirror服务,执行mirroring事务了。

(3)      rbd-mirror 服务

下面我们来看看rbd-mirror服务。


上图列出了rbd-mirror里面几个非常重要的类以及他们之间的关系,包括Mirror,ClusterWather,Replayer,PoolWather,ImageReplayer。下面我们来看看这几个类是怎样相互配合完成rbd mirroring的。

 

Class Mirror: 这是rbd-mirror 服务的主类,一个rbd-mirror服务其实可以看做就是一个Mirror类的实例。Mirror里面有几个很重要的成员,m_local_cluster_watcher,这是一个ClusterWatcher的实例,用来watch本地cluster的mirror状态的,包括mirrormode和peer变化。m_replayers,这是一个std::map<PoolPeer,std::unique_ptr<Replayer> > 类型的成员变量。用来记录所有的PoolPeer和Replayer的对应关系。除了这两个重要的成员变量,Mirror类还有一个很重要的函数:update_replayers()。这个函数用来更新m_replayers变量,当m_local_cluster_watcher发现我们需要更新replayers,就会调用update_replayers()。

 

Class ClusterWatcher: 这个类顾名思义是用来watch 一个cluster的,他通过读取cluster里面每个pool的mirror相关元数据(如前文所述,这些元数据都存放在rbd_mirroring的object里面),得到我们需要做mirroring的所有pool以及这个pool的peer信息。他有两个很重要的函数值得注意,refresh_pools()用来刷新最新的pool和peer的关系。get_pool_peers()得到最新的pool和peer的关系列表。

 

Class Replayer: 这个类是用来做replay操作的。一个replayer对应一个pool。实际上,在最新的ceph里面,我们已经将replayer重命名为PoolReplayer。Replayer有两个很重要的成员变量,m_pool_watcher,这是一个PoolWatcher的实例,用来watch这个pool里面的image情况。m_image_replayers这是一个std::map<std::string,std::unique_ptr<ImageReplayer<> > >类型的实例。保存了这个pool里面所有image和image对应的ImageReplayer的关系。除了这两个成员变量,Replayer里面还有一个线程,m_replayer_thread。这个线程用来执行这个pool的watch和update操作。

 

Class PoolWatcher: 这个类是用来watch一个pool的。他通过定时做refresh()来更新m_images,m_images表示pool里面需要做mirror的image列表。这个类中有两个很重要的函数:refresh_images()和get_images()。 refresh_images()会定时去访问rbd_mirroring读取现在这个pool里面需要做mirror的image,然后更新到m_images。get_images()直接返回m_images,也就是最新的需要做mirror的image列表。

 

Class ImageReplayer: 这是最终执行replay操作的类,这个类很重要。他有一个m_remote_journaler,这个成员变量用来连接到remote cluster里面我们需要mirror的那个image的journal。m_local_journal这是local image的journal。 m_local_replay,这是 m_local_journal的replayer,用来将remote的journal event在本地的journal做replay。我们会定时调用bootstrap()函数对这个image 进行replay操作。

 

那么到底ImageReplayer是怎样对一个image进行replay操作的呢?

实际上有两种同步方式,event和image。event表示对这个image的一些操作,比如resize,会记录到journal里面,然后event通过librbd的journal/replay机制来实现同步。Image方式是通过sync point来实现。在每次同步的时候做一个snapshot,作为sync point,然后进行数据拷贝。当remote 的image没有完整的journal来做replay的时候,我们就必须使用sync point的方式来做image 同步。比如我们将一个使用了很久的image加入到mirroring里面。

 

关于这个设计,可以参考https://www.spinics.net/lists/ceph-devel/msg24169.html, 这是Josh Durgin在设计rbd-mirror的时候发出来的邮件。里面说明了为什么使用这种journaling的方式来做mirroring。邮件里面列出了三种方式, snapshot, log-structed rbd,和现在使用的journaling。最终选择了journaling,主要原因是journaling综合了snapshot和log-structed rbd两种方式的优点。对于event,使用log-struct的方式来记录并replay,对于没有event的image数据,使用snapshot做sync point的方式来同步。

 

那下面从两个方面来介绍一下ImageReplayer是怎样工作的。

第一,  event replay。首先我们需要知道,现在有哪些event我们会放到journal里面?


如上,这是我们现在所支持的所有event,以resize为例我们来看看rbd-mirror是怎样让remote的image resize 也发生到local的image上的。


当用户执行resize操作将image做resize的时候,remote cluster的librbd会在journal里面记录一个ResizeEvent。之后,local cluster的replayer会定期唤起ImageReplayer去做image 同步操作。ImageReplayer会通过m_remote_journaler去获取remote cluster里面对应image的journal的entry。然后通过m_local_replay去decode并且调用local cluster的librbd来处理。其实就是librbd里面的journal/replay机制。这样remote cluster里面的resize操作就同步到local cluster里面来了。

 

第二,  image 同步。除了event的同步,当image没有完整的journal保存所有的event的时候,就需要使用syncpoint的方式来同步了。


当我们将一个image 通过rbd mirror image enable test 的方式加入到mirror中时,我们localcluster的PoolWatcher会发现这个image,并放到m_images里面。Replayer拿到images为这个image起动ImageReplayer,ImageReplayer会调用create_local_image()创建一个新的image来作为mirrored image。然后使用image_sync()来做image的同步。大概过程如下:


可以看到,这个过程将一个image的所有数据都进行了同步。

 

(4)      rbd-mirror TODO

至此,我们已经了解了rbd-mirror的基本实现原理。包括rbd-mirror的架构设计,rbd mirroring的配置,以及rbd-mirror数据同步。下面我们再来看看rbd mirroring的发展计划。

(1)      update notify

根据上文我们可以看到,rbd-mirror是通过polling的模式来进行数据同步的,这种方式简单但不够高效且消耗太多资源。我们需要一种notification的机制来让rbd-mirror知道什么时候需要更新。

(2)      rbd-mirror HA

我们可以看到rbd-mirror这个服务是独立的,没有提供高可用机制。那么这个容灾方案就不够安全,所以我们需要rbd-mirror的HA方案,包括Active/Passive 或者Active/Active。

         以上所有的分析和代码都是基于ceph最新的stable版本Karen的。实际上我提到的这两个问题,在upstream里面已经有了设计,期待下一个release的ceph里面能够解决这些问题。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。