LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: Chowroc

[原创][涅磐系列]文件系统近实时镜像工具及其高可用应用

[复制链接]
 楼主| 发表于 2007-10-16 20:26:51 | 显示全部楼层
Post by d00m3d
网站页面超长,看来得花点时间消化才行


只有 http://crablfs.sourceforge.net/#ru_data_man 那部分和镜像有关,我是一个大项目一直做下来的,前面和这个关系不大,可以不看的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-17 10:39:23 | 显示全部楼层

为高可用(HA)设计备份系统

===为高可用(HA)设计备份系统===
在[概述 #ru_data_man_overview]一节关于 rsync 的部分的示例中,我已经讨论
过为高可用进行备份的问题。使用 mirrord/fs_mirror,只需要将"rsync"替换为
"mirrord",就得到了这样的拓扑:
```
[PHP]使用等宽字体显示:

    +----------+  
    |  worker  | -[mirrord] -----------\
    +----------+                       |
       ......                          |
                                       |
    +----------+                       |
    |  worker  | -[mirrord] -----------\
    +----------+                       |
                                       V
                                  [fs_mirror]
                                       |
    +----------+                  +----------+
    |  worker  | -[mirrord] --->  |  backup  |
    +----------+                  +----------+
         |                             |
    [take_over]                        |
         |                             |
         V                             |
    +----------+                       |
    |  rescue  | <------------------- NFS
    +----------+[/PHP]
```
这种多对一的备份是比较经济的。如果一个工作主机故障,你可以使用救护主机
替换它,同时利用一些高可用的手段,例如
[The High Availability Linux Project #http://www.linux-ha.org/]
``heartbeat``项目。

为了使在备份主机上的"fs_mirror"能够镜像若干工作主机上的文件系统,应该
使用如下方法调用 fs_mirror:
```
client# fs_mirror -v -o host=worker01 -o fssync.user=mirror -o fssync.port=21 \
        --password-from=/root/.fs_mirror/secrets:worker01 --datadir=/data/fs_mirror/worker01/
client# fs_mirror -v -o host=worker02 -o fssync.user=mirror -o fssync.port=21 \
        --password-from=/root/.fs_mirror/secrets:worker02 --datadir=/data/fs_mirror/worker02/
```

保证主机名可以被解析,以及用户"mirror"存在与服务器上,并拥有整个需要
镜像的文件系统的读取权限(推荐使用 Linux ACL)。

如前所述,使用这种方法,你可以吧不同主机的文件系统镜像到不同的子目录中
,然后创建若干正确的符号链接,以利于接管。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-17 10:40:14 | 显示全部楼层

以 IDS 的方式运行

===以 IDS 的方式运行===[ru_data_man_usage_ids]
mirrord/fs_mirror 一个预料之外的用处是作为 IDS(Intrusion Detection
System 入侵监测系统)来运行,此时使用 fs_sync 的"REPORT"机制,这可以在
某种程度上被看作是实时的``tripware``。在服务器端,将那些不会频繁变动的
文件系统部分置于 mirrord(inotify) 的监控之下,例如所有的可执行文件,
以及配置文件,在客户端,以 "REPORT" 模式运行 fs_mirror,则在服务器上的
所有重要的变更都能够及时的被报告。

在 客户端上为这种 IDS 应用实现一个 GUI 看上去不错,这样我就可以在我的
PC 机上跑了,例如公司的 Windows 系统。

目前来说,这个问题还没有深入考虑,一些比较重要的特性例如 SSL 支持还
没有加入,不过这种使用方式是可以肯定的。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-22 13:32:17 | 显示全部楼层

来自一位网友的反馈

如果没有 2.6 内核,也许你可以看看 Linux FAM,不过这个项目已经停了很长时间了,而且也是需要编译内核的。FAM 是从 IRIX 移植过来的。也许直接编译 2.6 内核更直接呢?我不太清楚能否在 2.4 内核上编译 inotify。

DRBD 也许可以工作在 2.4 内核,不过好像镜像磁盘不能挂载。具体我没有试过。

对了,我想你是从 python.cn  的邮件列表里面看到这个项目的吧,我想把邮件转过去,因为别人也可能会遇到类似的问题,行吗?


On 10/19/07, 俊杰蔡 <yzcaijunjie@gmail.com> wrote:
> 您好:
>       我目前在笔记本上测试,系统为ubuntu,安装libdb4.4,mirrord_config.py中配置如下:
>   1 import tree
>   2 options =  tree.Tree('mirrord')
>   3 o = options
> 我使用sudo mirrord运行,依然会出现如上错误。
>
> 另外,我想问下,由于内核版本原因,我们目前的内核没有到2.6, 所以没有inotify特性,所以如果我需要移植,就要找其它的技术来代替,你对此方面有何建议呢?
>
> 谢谢
>
>
> 在07-10-18,Roc Zhou < chowroc.z@gmail.com> 写道:
>
> > 您好:
> > 这个问题应该是 Berkeley DB 抛出的异常,您先看一下您系统中 BDB 是否安装了(应该是叫做 db4 的一个包),它的版本是什么。
> >
> > 另外,您给我看一下 /etc/python/mirrord_config.py 配置是怎样的,您设置的  o.datadir 是否有写权限?
> >
> > 当然现在 mirrord/fs_mirror 还有许多局限性需要去完善,这些局限性在文档中我也说明了。对于上百台服务器,您也可以看看其他的项目,比如 GFS;关于镜像,还有象 BRDB 这样的项目,我在文档里面也提及了的,因为 mirrord/fs_mirror 不适合频繁变动的大文件。
> >
> > 这个项目是开源项目,使用了 GPL v2.0,您当然可以自由扩展,只要遵循 GPL 就可以了。当然我也很高兴您能告诉我做了那些改进。
> >
> > 谢谢。
> >
> >
> >
> > On 10/18/07, 俊杰蔡 < yzcaijunjie@gmail.com > wrote:
> > > 您好:
> > >      
> > >        我对uLFS中的备份功能比较感兴趣,目前我所从事的工作是管理上百台服务器,其中备份策略比较简单,虽然也用到一些同步工具和自己写的一些脚本,但是没有一个完整的备份架构。所以花了点时间看了下你备份的原理,很清晰。不过由于我们备份的特殊性,不能直接使用。我想问下是否可以在你的项目上作些扩展?
> > >       另外,我下载并安装了工具,但无法正常运行:报错如下
> > > cjj@cjj-laptop:/var$ mirrord  
> > > Traceback (most recent call last):
> > >   File "/usr/local/bin/mirrord", line 172, in <module>
> > >     md = mirrord.Mirrord(_identities, _datadir, _loglen)
> > >   File "/usr/lib/python2.5/site-packages/cutils/mirrord.py", line 1009, in __init__
> > >     self.shared.wdirs = Snapdb(path=path)
> > >   File "/usr/lib/python2.5/site-packages/cutils/fs_snap.py", line 394, in __init__
> > >     self.dbfile = bsddb.btopen(path, 'n')
> > >   File "/usr/lib/python2.5/bsddb/__init__.py", line 323, in btopen
> > >     d.open(file, db.DB_BTREE, flags, mode)
> > > bsddb.db.DBNoSuchFileError: (2, 'No such file or directory')
> > > Daemon PID 6870
> > > cjj@cjj-laptop:/var$
> > >
> > > 由于python使用时间不是太长,所以请教一下这个问题。
> > >
> > >
> > > 谢谢
> > >  
> >  
>
>
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-22 13:33:19 | 显示全部楼层
这个问题是我对于 Berkeley DB 的异常处理还不够到位。因为我当时两天之内试了好几种方案,包括很多原型和测试的工作,最后才选择了 BDB 的,时间有点仓促。

如果遇到 ImportError 这样的异常,通常是说明没有安装 BDB,也就是 db4 这个包。而上面的报错是因为 mirrord 不会自动创建目录,这个目录是由 fs_info 脚本创建的,而我没有对这个异常进行捕捉并给出正确的说明,因为之前我都会先运行 fs_info 的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-27 23:41:50 | 显示全部楼层

关于一个 Berkeley DB 的问题

遇到一个比较奇怪的问题。

在初始化阶段完成之后,进入实时同步状态。我在 3 台主机上都部署了的,而且都运行了将近半个月,其中有一台主机有一天不知道怎么回事,我发现 fs_mirror 从 mirrord 接受到了空记录。正常的情况下,应该得到这样的内容:
"CREATE:/var/www/html"
"FWRITE:/var/www/html/index.php"
"DELETE:/var/www/html/temp"
"MOVE''/var/www/html/aa', '/var/www/html/bb')"
...
但不应该有空记录。这导致 fs_mirror 进入了死循环。

我让 fs_mirror 从断点重启,但仍然还是如此,而且 DEBUG 发现每次出问题都在同一个 serial 号(我使用 Berkeley DB 作为日志记录,serial 号作为其 key),所以我怀疑问题出在 BDB,但不知道怎么测试以确定问题。

我首先尝试在 python 中直接打开原来的数据库:
[PHP]>>> import bsddb
>>> x = bsddb.btopen("/var/mirrord/wmlog"
>>> len(x)
623748
>>> x["6854"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/bsddb/__init__.py", line 223, in __getitem__
    return _DeadlockWrap(lambda: self.db[key])  # self.db[key]
  File "/usr/lib/python2.5/bsddb/dbutils.py", line 62, in DeadlockWrap
    return function(*_args, **_kwargs)
  File "/usr/lib/python2.5/bsddb/__init__.py", line 223, in <lambda>
    return _DeadlockWrap(lambda: self.db[key])  # self.db[key]
KeyError: '6854'
>>> x[str(6854)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/bsddb/__init__.py", line 223, in __getitem__
    return _DeadlockWrap(lambda: self.db[key])  # self.db[key]
  File "/usr/lib/python2.5/bsddb/dbutils.py", line 62, in DeadlockWrap
    return function(*_args, **_kwargs)
  File "/usr/lib/python2.5/bsddb/__init__.py", line 223, in <lambda>
    return _DeadlockWrap(lambda: self.db[key])  # self.db[key]
KeyError: '6854'
>>> x.first()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/bsddb/__init__.py", line 278, in first
    rv = _DeadlockWrap(self.dbc.first)
  File "/usr/lib/python2.5/bsddb/dbutils.py", line 62, in DeadlockWrap
    return function(*_args, **_kwargs)
_bsddb.DBNotFoundError: (-30990, 'DB_NOTFOUND: No matching key/data pair found')[/PHP]

即便我把 mirrord 停止之后,仍然是这样的错误。

然后,我尝试把数据库拷贝和移动出来,然后打开新的数据库文件:
[PHP]>>> import bsddb
>>> x = bsddb.btopen("/tmp/wmlog")
>>> len(x)
0[/PHP]
长度为 0,getitem 操作和上面报错一样。

为什么把数据文件拷贝出来以后会有这种问题呢?尤其是 len() 的结果为 0?

我只好重新运行 mirrord,重建 BDB 数据文件,到现在没有再遇到这种问题。

不知道这个偶然性的问题是怎么回事?有哪位对 BDB 比较了解的能够赐教一二吗?
回复 支持 反对

使用道具 举报

发表于 2007-10-28 06:45:26 | 显示全部楼层
不详,也许数据库版里的兄弟会比较懂吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-31 14:24:25 | 显示全部楼层
一个很偶然的错误,不知道是怎么回事。最主要的是重新打开 BDB 有问题让我不解。

不过可以先放在一边。之前讲的是如何使用。下面我说一说设计上的一些思路。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-31 14:25:38 | 显示全部楼层

mirrord/fs_mirror 设计

==mirrord/fs_mirror 设计==[ru_data_man_design]
===inotify===
为了实现镜像,应用程序必须知道自上一次传输文件变更后到现在的这个间隔
之间,又有哪些内容发生了变化,而 rsync 需要遍历这个文件系统来比较时间戳
,而只是一个比较消耗资源且不可能实时的过程。

而 mirrord/fs_mirror 能够避免遍历和比较,这通过最近的 Linux 内核(自
2.6.12 起)所提供的一个新功能:inotify 来实现。

inotify 可以监控指定目录的所有变更,这就是说,当你创建、删除、移动、
改变目录/文件,或改变在这些目录中的普通文件的内容时(非递归),inotify
将抛出事件,而应用程序可以捕捉和排队这些事件,找出导致这些事件的文件
的名字,并进行相应的操作(这里 mirrord 将记录这个动作及其相应的文件名,
并告知 fs_mirror 传输这些记录,fs_mirror 此时做相应的创建目录、删除、
移动或传输文件等操作)。

尽管 inotify 不提供递归功能,应用程序却可以递归地将目录加入到 watches
列表中,这些 watches 表明了这些目录需要被 inotify 监控,这些标识在内核
中只是一些数字描述符,所以不占多少内存,并且这个功能有内核提供,因此是
轻量级的。

关于 inotify 的细节,可以在 Internet 上找到一些介绍:

[Kernel Korner - Intro to inotify #http://www.linuxjournal.com/article/8478]
[Monitor Linux file system events with inotify #http://www-128.ibm.com/developer ... rary/l-inotify.html]
[Lightweight filesystem notifications #http://blog.printf.net/articles/ ... ystem-notifications]

或者阅读 Linux 内核中关于 inotify 的源代码:
```
./include/linux/inotify.h
./Documentation/filesystems/inotify.txt
./fs/inotify.c
```

在这个项目,mirrord/fs_mirror 使用了一个 Python 包``pyinotify``:
http://pyinotify.sourceforge.net/
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-31 14:26:55 | 显示全部楼层

故事一

===故事一===
最简单的一种思路来自于我的一个同事,那就是利用硬链接来记录这些文件系统
的变化,通过 NFS 导出这些包含硬链接的目录给远程主机,同时在备份主机上
运行的守护进程定期的将这些导出记录移动到它自己的本地目录,象这样:
```
使用等宽字体显示:
[PHP]
             (events)                              \
   [inotify] ----> [mirrord]           worker host \ rescue/backup host
       |               |(invoke)                   \
       |               |                           \
__original-fs__ == HardLink ==> __records__ == MoveOnNFS ==> [fs_mirror]
                                                   \[/PHP]
```

尽管这看起来简单,但这种模式是有问题的。有几个问题是无法通过这种“简单
硬链接”来解决的:
+ 首先无法知道哪些内容被“删除”了,因此 fs_mirror 无法同步这些“删除”
操作,“移动”同理因为“移动”隐含着某种“删除”。

事实上,在 inotify 中,"MOVE"和"DELETE"的行为是不同的(在后面的章节中
讨论),而简单的硬链接无法区分这些差异。

你可能会建议建立一个"DELETE"目录并只记录"DELETE"操作?但这导致应用模型
过于复杂,并且:

+ 操作的顺序是很重要的(显然不可能在创建一个文件之前就删除它,或者在其
上层目录都不存在的情况下写一个文件,...),而“简单硬链接”无法记录这些
操作的顺序。

+ fs_mirror 必须通过 NFS 从记录目录中移出目录/文件,否则记录目录会变得
越来越大,这将导致性能的下降。

+ 这里,由 fs_mirror 进行的移动操作是有问题的,因为,它不是一个原子操作
。显然,"move"是由"copy"(事实上这里必须通过 NFS 进行 copy,因此比简单的
copy 还要复杂一些)和随后的"remove"组成,因此不可能在一个系统调用中完成
,即使是"copy"也可能要调用好几次甚至更多的 read/write 系统调用,这些都
可能导致记录的丢失。

用一个例子来说明:
```
使用等宽字体显示:
[PHP]
    file1 <=== hardlink === file2
      A                       |
      |                       V
WRITE_CLOSE                 (mv)

  procedure:

      |                       |
      |                       |<-- COPY_COMPLETED
      |                       |
      |<-- WRITE_CLOSE        |
      |                       |
      |                       |<-- REMOVE[/PHP]
```
WRITE_CLOSE 是一个在文件写操作发生时产生的事件,如果你已经阅读 inotify
的文档,这不难理解。当这个事件产生的时候,inotify 处理器进行相应的“硬
链接”操作,从 file1 到 file2,假设这个 file2 已经由上一次 WRITE_CLOSE
产生,因此这一次 WRITE_CLOSE 应该忽略"OSError: file exists"这样的异常,
这样在“拷贝”的时候,只有上一次变更的内容被传输,而“删除”操作只是
unlink 刚刚创建的(可能只有几毫秒)这个硬链接文件 file2,但并没有同步上次
变更的内容,因此这些内容被丢失了。如果这个文件直到系统崩溃的时候都没有
再变化过,那么它的这个变更就永远丢失了。一个文件不是什么大问题,但是
没有任何保证只有少数文件会被这种机制影响。而我们知道,即使“拷贝”本身
也不是原子的,因此这会导致目标文件系统出现大量的“漏洞”。

+ 可能会有某种需要,将一个文件系统镜像到好几台客户主机上,或者说“多路
镜像”。而简单硬链接无法达到这个目的,因为在 fs_mirror "moved" 硬链接
文件之后,记录就等于被“抹除”了。

+ 这看上去更像是一种脚本的解决办法,而不是一个产品级别的解决方案,既然
backup 是一个关键应用,最好是使用经过严格测试的产品,而不是几个简单的
脚本。

+ 简单硬链接不够灵活,因为你必须将所有文件都放在同一个文件系统中,因此
意味着你必须调整每一个主机。事实上,在每台工作主机上都设置 NFS 有些烦人
,而且这种“推”模式不够安全——你不得不通过 NFS 导出关键数据。

+ 最后,既然你需要在物理硬盘上建立结构,这可能会导致性能下降。

% EOL

因此应该使用一个更好的策略来金路这些文件系统的变更。既然现在 mirrord
使用 pyinotify,我建议你看一看它的文档。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表