分类: 虚拟化
2015-03-13 15:05:44
GlusterfsGeo-replication提供了一种持续,异步,增量数据备份策略,可以通过局域网,广域网,英特网来进行。使用Geo-replication,我们能够在存储环境建立数据冗余,提供数据的灾难恢复功能。
Geo-replication使用master-slave模式:
1、Master- 代表一个卷;
2、Slave-可能是一个卷或者一个文件夹;
局域网异地迁移示意图
广域网异地迁移示意图
万维网异地迁移示意图
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 |
rsync -sS -aR ./2364.txt ./2366.txt ./2362.txt ./2365.txt ./2368.txt ./2367.txt ./2363.txt /tmp/liuhong |
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) |
注:由于时间等原因,暂不做命令行解析,参数合法性检查,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:
其他文件类型忽略同步。
注:一般清晰下,遍历递归是在无限循环的。
相对于启动过程,停止过程相对比较简单,仅仅涉及到删除相关slave信息与进程文件和状态文件
上图说明:
1)通过slave名获得其uuid;
2)通过uuid删除其在start的时候记录的信息;
3)通过从进程文件中读取的进程号来关闭进程,然后删除进程文件;
3.2.停止过程