Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1485452
  • 博文数量: 230
  • 博客积分: 474
  • 博客等级: 下士
  • 技术积分: 1955
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 18:40
文章分类

全部博文(230)

文章存档

2020年(3)

2019年(3)

2018年(12)

2017年(13)

2016年(11)

2015年(55)

2014年(74)

2013年(39)

2012年(2)

2011年(18)

我的朋友

分类: LINUX

2014-08-05 12:03:28

1.概述

GlusterfsGeo-replication提供了一种持续,异步,增量数据备份策略,可以通过局域网,广域网,英特网来进行。使用Geo-replication,我们能够在存储环境建立数据冗余,提供数据的灾难恢复功能。

Geo-replication使用master-slave模式:

1、Master- 代表一个卷;

2、Slave-可能是一个卷或者一个文件夹;

 局域网异地迁移示意图



广域网异地迁移示意图

万维网异地迁移示意图



2.功能测试

2.1卷设置

Master端,在启动异地迁移的时候,会进行卷选型设置geo-replication.indexing:on

Volume Name: gg

Type: Distribute

Status: Started

Number of Bricks: 2

Transport-type: tcp

Bricks:

Brick1: 10.28.1.96:/data/g1

Brick2: 10.28.1.96:/data/g2

Options Reconfigured:

geo-replication.indexing: on

同时修改marker配置文件,即将xtime打开,目地是以后上传到master端的文件会被标识上时间戳:

volume swift-marker

      type features/marker

      option volume-uuid 40f278e4-4769-4206-a33b-67064a853c61

      option timestamp-file /etc/glusterd/vols/swift/marker.tstamp

      option xtime on

      option quota off

      subvolumes swift-io-threads

 end-volume

2.2文件迁移

rsync -sS -aR ./2364.txt ./2366.txt ./2362.txt ./2365.txt ./2368.txt ./2367.txt ./2363.txt /tmp/liuhong

2.3查看迁移后文件的xtime

xattr.getxattr("aums.txt",'trusted.glusterfs.81fa9632-e2d1-4337-aee8-83afa72728df.xtime')

'Ob\xdcV\x00\x06\x01\x12'

>>> b=struct.unpack('!II','Ob\xdcV\x00\x06\x01\x12')

>>> b

(1331878998, 393490)

 

 

3.源码分析

3.1.启动过程

注:由于时间等原因,暂不做命令行解析,参数合法性检查,rpc通信等部分的分析,而是对异地迁移的核心业务做分析,以后补上该部分。

 

异地迁移启动和迁移过程



上图说明:

1)  设置卷标识,对master端的卷设置参数geo-replication.indexing:on;同时修改每个brick的marker配置文件内的参数xtime为on;

ret = glusterd_set_marker_gsync (volinfo);//为卷设置相应参数

 

2)  存储slave端的uuid等信息;

3)  在准备开始同步的阶段,首先检查输入的master,slave同步进程是否已经存在;检查异地迁移master端卷的工作目录是否存在,如果不存在则创建,之后会有异地迁移的状态文件与进程文件存储在该目录下:

-rwxr-xr-x 1 root root 6 Mar 21 10:25 gluster%3A%2F%2F10.28.1.96%3Ar1.pid//存储了进程号

//存储了异地迁移的状态号

-rwxr-xr-x 1 root root 7 Mar 21 10:49 gluster%3A%2F%2F10.28.1.96%3Ar1.status

 

4)  检查master端异地迁移卷的日志目录是否存在,如果不存在则创建;

5)  生成一个命令,执行调用起python脚本gsyncd.py的shell文件,如:

/usr/local/libexec/glusterfs/gsyncd --monitor -c /etc/glusterd/geo-replication/gsyncd.conf :g1 10.28.1.96::m1

6)  在gsyncd.py中,首先进行参数检查,参数准备和相关初始化;

7)  如果带了监控参数,启动监控器,监控器文件monitor.py会根据进程运行情况设置运行状态OK,starting...,faulty,并且会将这些状态设置到状态文件内,当要查询运行状态的时候,会直接到这些文件查询;

8)  Ssh与slave端建立无密码密钥认证;

9)  卷挂载,会将master端卷与slave端卷挂到的master端的一个临时文件夹下,以便于后面目录项对比;

def connect(self):

        def umount_l(d):

            time.sleep(0.2) # XXX temporary workaround(工作区)

            argv = ['umount', '-l', d]

            #os.spawnvp(mode, file, args)

            #argv[0]=umount

            return os.spawnvp(os.P_WAIT, argv[0], argv)

        #创建挂载卷的临时文件夹

        d = tempfile.mkdtemp(prefix='gsyncd-aux-mount-')

       。。。。。。。。。。。。。。。。。。。。。。。。。。。。

            if os.spawnvp(os.P_WAIT, argv[0], argv):#将卷挂载到客户端的d目录下

                raise RuntimeError("command failed: " + " ".join(argv))

      。。。。。。。。。。。。。。。。。。。。。。。。。。。。

            os.chdir(d)#改变当前工作目录到d,即进入挂载的目录,这样后面执行crawl时才能用路径"."

            #执行挂载的卸载

            if umount_l(d) != 0:

                raise RuntimeError("umounting %s failed" % d)

            #卸载成功后

            mounted = False

        finally:

            try:

                #如果挂载状态,则卸载

                if mounted:

                    umount_l(d)

                os.rmdir(d)#删除临时文件夹

10)调用master的GMaster(self,args[0]).crawl_loop(),循环递归,进行文件的检查和复制,在crawl_loop中调用如下:

t = Thread(target=keep_alive)#新建一个线程执行keep_alive,保持master与slave间的链接畅通

t.start()

       #只要self.terminate为None,这会一致crawl

        while not self.terminate:#中止

            self.crawl()//循环执行crawl方法

11)crawl方法是异地迁移的核心方法,在该方法中,首先对master,slave端卷信息进行检查和获取,然后master与slave端以路径“.”即根目录开始进行递归对比,具体过程如下:

a)每个path先从master端获得路径的xtime(该值在文件写入的时候由markerxlator写入),即xtl,如果不存在会临时生成;

if not xtl:#xtl最开始为None

            xtl = self.xtime(path)#通过xattr获得xtime记录的值,local

            if isinstance(xtl, int):#如果xtl为整型,这将路径加入failjob

                self.add_failjob(path, 'no-local-node')

                return

b)然后从slave端获得该路径的xtime,即xtr;检查slave端该文件夹是否存在,如果不存在则在slave端创建该文件夹;

c)然后将xtr与xtl进行对比,如果xtl

d)如果xtl>xtr,则遍历master端路径path下所有的目录项,获得集合dem;

 

dem = self.master.server.entries(path)

同时读取slave端该path下的所有目录项,目录项集合为des;

try:

            des = self.slave.server.entries(path)#罗列出slave路径下的列表

        #此处有个一致性检查错误,即如果master端该路径为目录,而slave端为文件,则将该文件删除,然后创建该路径为目录

        except OSError:#如果slave端path不存在或者不是目录,清理path

            self.slave.server.purge(path)

            try:

                #在slave端以path为路径创建目录

                self.slave.server.mkdir(path)

                #重新再次返回slave端path路径下所有的目录项

                des = self.slave.server.entries(path)

e)通过dd = set(des) -set(dem)算出slave端有的而master没有的目录项,将他们删除,因为对应slave来说,它有的而master没有的应该为脏数据;

f)遍历dem中的每一个目录项,将他们的xtime与slave端path的xtime进行对比,如果他们比slave端xtime更大则需要同步;

for e in dem:#遍历master下的所有目录项

            #获得每个项的绝对路径

            e = os.path.join(path, e)#e为文件或者文件夹名

            #通过xtime没有找到对应的xtime,则会设置时间戳,并且返回xtime

            xte = self.xtime(e)#获得master每个文件的xattr内的时间戳

            if isinstance(xte, int):#如果xte为整型,则警告

                logging.warn("irregular不规则的 xtime for %s: %s" % (e, errno.errorcode[xte]))

            #将master端文件,文件夹的时间戳与slave端父目录的时间戳进行对比

            #这样就不用去遍历slave端的文件夹下所有的文件,及其文件夹

            elif xte > xtr:#如果master时间戳大于slave端该文件的时间戳

                chld.append((e, xte))#将对象与master差到的时间戳加入元组

h)判断chld元组内的文件类型,如果为链接,则直接在slave端创建链接,且设置xtime;

if stat.S_ISLNK(mo):#判断是否为链接

                #在slave端创建一个链接名叫e的链接到e

                if indulgently(e, lambda e: self.slave.server.symlink(os.readlink(e), e)) == False:

                    continue#创建链接失败

              #e:代表对象路径;xte:代表时间;adct:代表要设置的属性

              #将xte设置为slave端文件的xattr的内容

                self.sendmark(e, xte, adct)#创建链接成功,设置其xattr,与属性,即只是保证了用户,跟组一致

如果为文件,将其放入同步队列准备通过rsync进行同步;

elif stat.S_ISREG(mo):

                logging.debug("syncing %s ..." % e)

                #syncer为一个同步器对象

                pb = self.syncer.add(e)#将e加入需要同步文件的列表

                def regjob(e, xte, pb):

                    if pb.wait():

                        logging.debug("synced " + e)

                        self.sendmark(e, xte)#设置文件的扩展属性

                        return True

                    else:

                        logging.error("failed to sync " + e)

               #e:为具体每个文件的路径;pb为需要同步的文件队列

               #同步路径path下的文件e,设置该文件的xattr值为xte

                self.add_job(path, 'reg', regjob, e, xte, pb)

如果为文件夹,递归调用crawl,准备遍历其子目录项;

  elif stat.S_ISDIR(mo):

                adct['mode'] = mo#在adct中加上mode参数

               

                if indulgently(e, lambda e: (self.add_job(path, 'cwait', self.wait, e, xte, adct),

                                             self.crawl(e, xte),

                                             True)[-1], blame=e) == False:

其他文件类型忽略同步。

注:一般清晰下,遍历递归是在无限循环的。

3.2.停止过程

 相对于启动过程,停止过程相对比较简单,仅仅涉及到删除相关slave信息与进程文件和状态文件

上图说明:

1)通过slave名获得其uuid;

2)通过uuid删除其在start的时候记录的信息;

3)通过从进程文件中读取的进程号来关闭进程,然后删除进程文件;

阅读(5551) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~