LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3525|回复: 24

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

[复制链接]
发表于 2007-10-15 14:20:14 | 显示全部楼层 |阅读模式
这是我最近在做的一个开源项目,放在 sourceforge 上面了。可以从这里找到并下载:
https://sourceforge.net/projects/crablfs/

用 http 好像快一些:
http://sourceforge.net/projects/crablfs

相关的文档可以在:
http://crablfs.sourceforge.net/
找到。其中与文件系统实时镜像有关的部分在:
http://crablfs.sourceforge.net/#ru_data_man

这个项目是另一个项目的一个子项目,其最终目标,我想是做一个易管理的基于 LFS 的发行版,所以另外还有两个子项目。其中之一是我以前在论坛上发过的一个“基于用户的包管理系统”:
http://www.linuxsir.cn/bbs/showthread.php?t=277172
旨在为从源代码编译的软件包提供一种方便安全的包管理方式,例如它提供交互模式,在交互模式下可以按照安装 LFS 的方式一步步进行,这样保留了学习的过程;而在交互模式下输入的安装指令可以自动记录,并在非交互模式下自动运行,保留这些模板文件,就得到了一个你自己的定制发现版,并且是和硬件平台无关的(最终目标如此,当然中间还是会有些小困难,不过应该不难克服)。

而这个项目,是为了提供一些方便系统管理的工具集,比如备份等。在这里,我要讨论的就是一种备份方式:实时镜像加轮转。这里考虑的主要是软硬件资源比较有限的情况(穷呀),是一种比较经济的解决思路,如果你有钱,使用商业的阵列和软件也许更好。

当然,所有这些工具集,包括前面的包管理器,其实当然也可以用在其他任何发行版上面,比如我就在自己的 RHEL4 的环境中应用了这些工具。

所提供的文档都是英文的,在代码中的注释和内部文档也是英文的,因为在一开始编写的时候,要在不同的机器上工作,我自己的 LFS 系统没有加 GBK 支持,老是出乱码。而且中文编码本来就是个老大难的问题了,我自己也有些不同的想法,所以索性写英文的,在翻译中文文档,并利用 gettext 处理内部文档,一方面也提高下自己的英文水平。

由于比较忙碌,我也只能挤时间来做这个事情,所以只能一点一点的写。最后形成的文档,则会加入到
http://crablfs.sourceforge.net/

由于本人水平和经验有限,难免纰漏和错误,欢迎多多指教。

谢谢。
发表于 2007-10-15 14:30:48 | 显示全部楼层
英文的……

只好等有相关中文说明的再看看了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-15 16:02:46 | 显示全部楼层

备份和恢复概览

==备份和恢复概览==[ru_data_man_overview]
备份总是系统管理中最重要的一件事情,目前看来,无论是商业的还是开源的,
都有许多解决方案。

也许是由于我个人的肤浅吧,目前我没有使用过这样的商用软件,只知道不会
便宜,而且就我使用商业软件的经验来说,总觉得难以找到真正合适的,并且
我也不希望被绑定在某种昂贵的硬件设施上。

那么来看一看文件系统备份的问题。目前来说,对数据库的备份只能采用不同
的方法,并且已经有一些比较好的方案,所以这里不予考虑。

似乎编写一个脚本定时的运行``tar``看上去不错,但是这些归档文件将很快失去
易管理性,因为无法很容易的分别出在归档中加入了什么,同时,针对文件系统
的不同部分,可能会生成不同的归档文件。

“简单 tar”的另一个问题是不利于实现增量和差分备份,尤其是 tar 无法找出
被删除的文件,这将导致在恢复时文件系统里塞满了过时的废品(也许还有一些
敏感的东西)。这个问题可以利用``find``来得到一个低效率的解决方案,但是
还是有问题:

最重要的是:你仍然将被一个拥有多于上百万的文件/目录的真实的生产系统卡住
,因为仅仅是压缩/解压这些归档文件都是非常非常慢的(也比较消耗系统资源)...
事实上,仅仅是遍历一次它的文件系统都要耗费大量时间。在这种情况下,恢复
时的效率和易用性都没有被很好的考虑。

因此,利用现代高速、巨大并且便宜的磁盘,一些同步的机制被应用于备份和
恢复,例如``rsync``, ``amanda`` ... 同时利用 Linux 的 hard link 能力,
你可以为每一个同步点保留一个完全备份,但实际上却只是一个增量变化(可以
认为这时一种象快照的方式,通常利用轮转 rotate 实现)。

有一个 ruby 程序``pdumpfs``同时利用了 rsync 和 rotate(``pdumpfs-rsync``)
,我在 FreeBSD 上用过一段时间。

利用这种工具,我们可以使用前数小时之内的数据来急救一个崩溃的系统,例如,
如果备份系统如下:
```
[PHP]使用等宽字体显示:

    +----------+                +----------+
    |  worker  | --- rsync ---> |  rescue  |
    +----------+                +----------+[/PHP]
```
那么就可以使用 "rescue" 主机来代替 "worker"主机,通常只需要在 "rescue"
主机上做一些符号链接就可以了。

一种更经济的拓扑结构是这样的:
```
[PHP]使用等宽字体显示:

    +----------+
    |  worker  | --- rsync -----------\
    +----------+                      |
       ......                         |
                                      |
    +----------+                      |
    |  worker  | --- rsync -----------\
    +----------+                      |
                                      V
    +----------+                +----------+
    |  worker  | --- rsync ---> |  backup  |
    +----------+                +----------+
         |                            |
    [take_over]                       |
         |                            |
         V                            |
    +----------+                      |
    |  rescue  | <------------------ NFS
    +----------+[/PHP]
```
利用这种模式,你可以得到一个缓冲器,因此可以得到更多的时间来处理和恢复
崩溃的系统。很显然,这种模式也非常经济,因为你可以仅仅使用两台主机就
备份和救护(高可用)多于三台的主机(多对一)。

这里之所以使用 NFS 是因为直接利用拷贝来恢复将会是一个及其消耗时间和资源
的方式,因此是不可行的。例如,一个真实的在线生产系统,使用 SCSI 磁盘,
100 MBit 网卡和网线,50GB 的数据/大约 200 万文件/目录(其中目录数量为 10
万左右),将需要超过两个小时才能把所有文件拷贝回来(可以建立一个
SNMP/Cacti 来观察这个过程)。

尽管 SCSI 接口是
``target0:0:0: FAST-80 WIDE SCSI 160.0 MB/s DT (12.5 ns, offset 62)``
(dmesg 输出),这里 MB/s 是 MBps(MBits/s),所以 3 MBytes/s 实际上是在获取
(拷贝)这些大量的小文件时可以预期的上限,而 30 MBits/s 则是可预期的网络
带宽的上限,在这种情况下,实际的瓶颈在磁盘 I/O 上。

如果传输一个有数 GBytes 的大文件,情况则完全不同:磁盘 I/O 可以超过
11 MBytes/s 而网络带宽是 98 MBits/s,所以这时网络成为了瓶颈,此时磁盘和
网络的性能都可以认为被充分利用了。

我们必须考虑前面那种情况,因为那是在生成系统中我们通常遇到的情况,计算
一下时间:
```
sh$ echo "50 * 1024 / 3 / 60 / 60" | bc -l
4.74074074074074074074
```
这意味着从备份中恢复需要超过 5 小时的时间。即便将磁盘 I/O 提高一倍:
```
sh$ echo "50 * 1024 / 6 / 60 / 60" | bc -l
2.37037037037037037037
```
仍然要花费超过 2 个小时。这完全不能被接受。

所以关键是:**尽可能消除或减少在救护时的拷贝和遍历文件系统的操作**

在小文件和大文件直接巨大的差别源自于文件系统自身的原理和结构。以 Ext3fs
为例,其物理磁盘被分割成很多组(groups),因此元数据(metadata)以及目录和
文件散布在磁盘的各个部分,这对于随机存储是有利的,但在遍历这样的操作中,
内核不得不取得所有的元数据,因此需要耗费大量的时间在磁盘寻道上。而大
文件则通常在磁盘上有连续的数据块。

也许你会考虑使用 RAID,不过我看,如果考虑到写速率,那么 RAID10 也只能
预期两倍的速度,则仍然需要超过一个小时的时间。这还只是考虑拷贝的时间,
你可能还需要一些其他的操作,例如改变文件的属主和权限,这同样需要递归的
遍历。也许还有其他一些辅助操作 ...

目前我还不是太清楚 **Reiserfs**,但我不期望 5 倍以上的提示,即使利用
RAID,我想加上相关操作,一个小时总是需要的。

然而即使使用上面提到的救护方式,你仍然会损失自上一次 rsync 运行后的所有
数据,因为:

rsync 机制是不完美的(当然这个世界上也没有什么是完美的),因为 rsync 不是
实时的。每一次调用 rsync,它必须取得一个需要传输的文件的列表,而要获取
这个列表,它需要对源端的文件系统做一次遍历,比较每一个源文件和目标文件
的时间戳并执行相应操作。随着文件系统越来越大,越来越多的时间和资源将被
耗费在同步上,你必须增加两次运行的间隔,否则系统可能会进入下旋状态,
但是你本意上应该是要减小这个间隔,因为更大的时间间隔意味着损失更多的
文件。在一个关键的服务上,这样的损失可能无法接受。

rsync 另外有一个 "vanished" 问题,在得到列表和进行实际文件传输之间,源
文件系统可能改变,一些目录/文件可能被创建、更改,或者被删除,前者不是
一个很重要的问题,但后者可能是。例如 pdumpfs-rsync 可能会在这样的
"vanished" 的地方终止,留下余下的文件没有被同步。不过现在的 rsync 应该
已经足够健壮可以简单的忽略这些警告。

无论你使用哪种机制,tar/find 或 rsync, ...,仍然有一个挑战:通常情况下
你可能只想备份文件系统的一个部分,同时排出其中的一些部分,例如有 web
站点的 php 程序产生的大量的 cache 文件,...; 这些部分的识别应该易于管理
,最好是在你第一次指定之后能够有计算机自动完成。

到目前为止,针对文件系统的实时镜像,我还没有找到一个开源的解决方案,
所以我自己写了一个,希望对你们有用。(事实上,就在昨天(**9 月 18 日**,
2007),我发现了几个,例如商业的 PeerFs,以及开源的 DRBD,目前我还不是
非常了解它们,只有一些概念,我会找个时间学习和实验它们,看看有没有更好
的解决方案)。

必须说明的是,这个 C/S 应用程序,我称之为 ``mirrord/fs_mirror``,在
``cutils`` 这个包里面,并不是一个完全绝对的实时备份工具,它只是"近实时"
,也就是说,在一般的情况下(正常的系统负载和磁盘 I/O,以及足够的网络带宽
),在运行 mirrord agent 的 server 主机和运行 fs_mirror 的备份客户端之间
,会有一个一般少于 1s 的延迟,而其消耗的系统资源基本上可以忽略
。详细的
细节在 "[Design #ru_data_man_design]" 一节。

很明显,到目前为止,mirrord/fs_mirror **只适用于拥有小文件的文件系统**,
对于频繁变动的大文件例如日志文件和数据库,已经有许多其他的应用了。

作为总结,看一下备份和恢复包含的几个方面的问题:
+ 通常有两种类型的备份:镜像或者说热备份,以及周期性的备份。

为了使恢复过程尽可能快,镜像是必要的。其另一个作用是应付硬件故障(尽管
RAID 可以处理磁盘故障,但并不能处理 CPU/内存/电源/... 故障等,而为所有
这些设备都配备冗余又过于昂贵)。

但是镜像不能解决:误操作(可能有上千个用户的上千个子目录的数据需要备份,
也许某一天,某个用户告诉你他/她想要回滚到几天前的状态)、病毒或入侵。
如果其中之一确实发生了,那么你只能依赖于周期性的备份档案。

所以备份应该同时包括这两个方面,而这两个方面都可以用 mirrord/fs_mirror
来实现。

+ 为了更快的恢复,文件的拷贝和目录遍历都应该尽可能限制,所以最好的方法
是直接用救护主机替换崩溃的主机,或者从备份主机中导出相应的目录和文件
系统以缓冲压力,因此可以使用在上面提到的 rsync 相同的拓扑结构,细节在
后续章节中讨论(如果你只关系如何使用 mirrord/fs_mirror,可以直接跳到
"[Usage Manual #ru_data_man_usage]" 一节)。

+ 可管理性非常重要,你可能希望只备份整个文件系统的若干部分,区别它们(
例如与平台相关和平台无关的)。在被包含的部分,仍然可能希望排除一些用处
不大的内容。mirrord/fs_mirror 通过``fs_info``模块提供了这个功能。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-15 16:10:23 | 显示全部楼层
我不知道放在这个版块和不合适,毕竟这个东西其实可以在任何 Linux 发行版上运行的,我只不过是顺着 LFS 一路在做而已。如果版主觉得放在别的版块更合适,麻烦移动一下。

谢谢。
回复 支持 反对

使用道具 举报

发表于 2007-10-15 17:27:50 | 显示全部楼层
首先对楼主的专研精神钦佩一下

然后,看了一下没太看明白,和快照有什么区别?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-15 17:30:46 | 显示全部楼层
你说的是什么快照?是 LVM 的快照吗?
回复 支持 反对

使用道具 举报

发表于 2007-10-15 22:05:21 | 显示全部楼层
就放在 LFS 版块好了。最爱 LFS 了。
下午就看到了这文章的,当时w3m字符下好像无法回帖。晚上才把桌面装上。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-16 11:37:46 | 显示全部楼层
我原来的文档是首先说设计的思路的。这里我想还是首先说说 mirrord/fs_mirror
的基本使用可能更好。不过为了更好的理解,先粗略地解释一下其基本原理。

mirrord/fs_mirror 利用了最近 Linux 内核(从 2.6.12 开始)提供的一种新特性,
称之为 inotify,它可以监控文件系统发生的变化,并向上层调用它的应用程序
抛出事件,这些应用捕捉到这些事件以后,可以查出是那些文件发生了那些变化,并
进行相应的操作,因为避免了遍历和比较,又是由内核来实现的,所以是一个很轻量
级的解决办法。

mirrord/fs_mirror 就是利用这种特性,在两台主机之间做文件系统的镜像,
这种镜像因此可以非常接近实时状态,并且也不需要消耗太大的系统资源,
因此为备份和高可用提供了一种途径。


一些关于 inotify 的文档:
[Kernel Korner - Intro to inotify #http://www.linuxjournal.com/article/8478]
[Monitor Linux file system events with inotify #http://www-128.ibm.com/developer ... ary/l-inotify.html]
[Lightweight filesystem notifications #http://blog.printf.net/articles/ ... stem-notifications]

Linux 内核里的源代码:
```
./include/linux/inotify.h
./Documentation/filesystems/inotify.txt
./fs/inotify.c
```

目前 mirrord/fs_mirror 利用了一个 pyinotify 项目,它是在 inotify 上包裹
了一个 Python 层作为模块。
http://pyinotify.sourceforge.net/
回复 支持 反对

使用道具 举报

 楼主| 发表于 2007-10-16 11:39:44 | 显示全部楼层

mirrord/fs_mirror 的简单使用

==使用手册==[ru_data_man_usage]
===安装===
包含 mirrord/fs_mirror 的包是``cutils``,是``ulfs``的一个子项目。uLFS
意思是"**Your** own customed and easily managable **LFS**(Linux From
Scratch) distrubution",它包含一些工具以达到这个目的,例如
[User Based Package Management(包管理系统) #ru_package_man],
Configuration Sharing Management(配置共享管理),以及这个文件系统备份和
近实时镜像(也许在将来我可以实现一个完全实时的应用)。

在需要备份的服务器上安装 mirrord,此时需要 Python 2.4 或更高的版本,
以及 pyinotify-0.7.1:
```
sh$ tar xfz Python-2.5.1.tar.gz
sh$ cd Python-2.5.1
sh$ ./configure --prefix=/usr
sh$ make
sh# make install

sh$ tar xfj pyinotify-0.7.1.tar.bz2
sh$ cd pyinotify-0.7.1
sh$ python setup.py build
sh# python setup.py install
```

在备份主机客户端运行 fs_mirror,只需要 Python 即可。

然后要安装 caxes-0.1.2,这也是``ulfs``的另一个子项目,可以在``cutils``
相同的下载页面取得,它包括我定义的若干辅助的数据结构,例如 Python Tree
```
sh$ tar xfz caxes-0.1.2.tar.gz
sh$ cd caxes-0.1.2
sh$ python setup.py build
sh# python setup.py install
```

最后,安装 cutils-0.1.1:
```
sh$ tar xfz cutils-0.1.1.tar.gz
sh$ cd cutils-0.1.1
sh$ python setup.py build
sh# python setup.py install --install-scripts=/usr/local/bin
```

===简单使用===
参考 [The Second Story #ru_data_man_design_second] 中的图,为备份或镜像
一个文件系统,首先需要告诉备份/镜像程序要备份些什么。可以运行``fs_info``
来做这件事:
```
server# fs_info -a t:/var/www/html system
```
这会将"/var/www/html"加入到备份/镜像包含列表中,并分配一个对文件系统
部分进行标识的标识名"system",这个标识在你第一次运行这个命令的时候创建
,你也可以使用其他名字,例如"www", "web", "work" 等...

只有最上层的目录是必需的,所以:
```
server# fs_info -a t:/var/www/html \
-a t:/var/www/html/include system
```
与前面那个命令的效果完全一样("/var/www/html")。

为了将一些目录/文件排除在备份/镜像之外,使用"x:"标志:
```
server# fs_info -a t:/var/www/html \
-a x:/var/www/html/cache system
```
这会将"/var/www/html/cache"加入到排除列表当中。

如果有一个列表,可以直接将其加入:
```
server# fs_info -a tL:/tmp/included_01 \
-a tL:/tmp/included_02 \
-a xL:/tmp/excluded_01 system
```

fs_info 实际的效果就是向 $datadir/$identity/.{t,x}_files 中追加条目,
所以你也可以直接编辑这两个文件,或者使用 shell 脚本重定向功能:
```
server# find /var -maxdepth 1 >$datadir/$identity/.t_file
```
不过 fs_info 可以为你做若干有效性检查的操作,例如判断文件/目录是否存在
等。无论如何,选择权在你手里。

$datadir 默认是"/var/fs_info",你可以通过编辑
"/etc/python/fs_info_config.py"来调整它为"/var/mirrord",以适应 mirrord
的需要,因为 mirrord 的默认值就是"/var/mirrord",这个值也可以通过编辑
"/etc/python/mirrord_config.py"来调整。

这些配置文件都直接使用"ython Tree"定义的数据结构,在前面提到的``caxes``
包里面。

$identity 默认为"system",如前所述。

这些配置也可以自命令行改变,例如:
```
server# fs_info -o datadir=/var/mirrord -a t:/var/www/html
```

**注意**,inotify 的默认内核参数值太小:
```
16384 /proc/sys/fs/inotify/max_queued_events
8192  /proc/sys/fs/inotify/max_user_watches
```

"max_user_watches"的大小取决于要备份的这部分文件系统包含多少目录,可以
用:
```
sh# find $path -type d | wc -l
```
来统计一下,并保证这个值大于统计结果。

"max_queued_events"意味着 inotify 管理的队列的最大长度,文件系统变化越
频繁,这个值就应该越大。如果你在日志中看到""** Event Queue Overflow **"
这样的消息,这就意味着"max_queued_events"太小,并且自此之后对于文件系统
的监控都是不准确的,你应该在调整参数后重启"mirrord"。

这些设置之后,就可以启动 mirrord 来监控和记录源文件系统的变化了:
```
server# mirrord
# OR verbose:
server# mirrord -v
```

你可以自配置文件"/etc/python/mirrord_config.py"或命令行来调整 mirrord
的行为,例如 wmLog 的长度(o.length, 此值越大,就会保留越多的记录,这
可以为长时间停止的 fs_mirror 客户端从断点重启提供更大的方便,但同时也
意味着在读取和变更 wmLog 时需要更多的资源),绑定的接口和端口,以及
socket 的超时,或需要镜像的 identity 标识集。

mirrord 启动之后,等待一段时间(取决于硬件、文件系统类型以及需要
备份/镜像的目录/文件数量),当"boot init finished"的消息被打印的时候,就
可以切换到客户端并启动 fs_mirror 了。

后续的版本将会消除这个等待阶段,使你可以在启动 mirrord 之后立即启动
fs_mirror。

在启动 fs_mirror 之前,必须保证客户端能够与 mirrord 通信并进行普通文件
的传输(只需要普通文件传输),因此必须在防火墙上允许 2123 端口(或选择另
一个端口),以及你选择用来传输普通文件的端口 -- 就象在
[The Second Story #ru_data_man_design_second] 和
[The Fifth Story: Monitor #ru_data_man_design_monitor]
中所描述的那样,mirrord/fs_mirror 的设计支持很多文件传输机制,例如 FTP,
NFS, SMB/CIFS 等等... 不过目前来说,我只实现了两种:REPORT 和 FTP。

"REPORT" 仅仅打印由 fs_mirror 接收到的 wmLog 中的动作,这仍然是一个很
有用的特性,在后面讨论。

"FTP" 同步机制将创建目录,删除或移动(递归)目录/文件,这些都可以在本地
完成,而只有对普通文件的写操作需要通过 FTP 协议进行实际的文件传输操作,
这种方式降低了在服务器上的资源消耗。注意整个文件都会被传输,而不是仅仅
被变更的部分,所以这**不是**一个字节级别的传输,因此不适用于大的并且
变化频繁的文件,或者说,如果大小和变化频度的乘积过大,或"size/seconds"
的值太小(这里 seconds 表示自第一次传输以来所经过的间隔),都不适合传输。
未来也许会在客户端做一个计算以判断是否应该传输。

这些同步机制定义在 cutils.fs_sync 模块中。

在需要镜像的源文件系统的主机上创建一个明文"mirror"或"backup"的用户是
一个好主意,这个用户应该拥有这部分文件系统的读权限,这可以利用 Linux
的 ACL 功能来实现,并使这个用户能够通过 FTP 访问,也许你想进一步使用
FTP SSL。到目前为止,认证机制还没有实现。

编辑 fs_mirror 的配置文件"/etc/fs_mirror_config.py"以获取关于配置的跟多
信息。

此时,启动 fs_mirror:
```
client# fs_mirror
# OR verbose:
client# fs_mirror -v
```
"fs_mirror --help"将提供命令行的更多信息。

作为一个例子,你可能会使用如下的参数:
```
client# fs_mirror -v -o host=roc \
-o fssync.user=root \
-o fssync.port=2121 \
--password-from=/root/.fs_mirror/secrets:roc \
--datadir=/data/fs_mirror/roc
```
现在,镜像的目录/文件将被放在 /data/fs_mirror/$hostname 中,选项"-o"
(--option) 与 /etc/python/fs_mirror_config.py 中 o.* 拥有相同的含义,
如前所述。这里,hostname 和两个文件同步的参数被改变了,当然要保证
hostname 可以被解析。

如果 verbose 模式打开了的话,在屏幕上可能会有这样的消息:
```
Daemon PID 7094
Connecting to the Mirrord: ('roc', 2123) ...
Conected.
Server reply 'OK' for 'START'
Server reply 'OK' for 'SN:b5e0ce480c925184dbdb6f23a62ddc6d,872302'
Received next serial message: 'SERIAL:862303'
WRITE FILE '/var/Counter/data/5597.dat', PREFIX '/data/fs_mirror/roc'
Received next serial message: 'SERIAL:862306'
CREATE DIR '/var/www/html/sample231.com/syssite', PREFIX 'data/fs_mirror/roc'
WRITE FILE '/var/www/html/sample231.com/sysstie/rec.php', PREFIX '/data/fs_mirror/roc'
DELETE DIR/FILE '/var/www/html/sample231.com/sysstie/rec.php.old', PREFIX '/data/fs_mirror/roc'
...
```

有一个普通文件"$datadir/sn"用来记录 session 和 serial number:
```
sh# cat /data/fs_mirror/roc/sn
b5e0ce480c925184dbdb6f23a62ddc6d,872302
```
这个文件在 fs_mirror 需要从断点重启的时候被使用。它使用一个文件锁
(fcntl.LOCK_EX)来避免镜像两个文件系统到一个目录,或同时镜像一个文件系统
两次(运行两个同样的 fs_mirror 实例)。

在[概述 #ru_data_man_overview]一节说过,轮转机制是必需的。目前
fs_mirror 还没有内置的轮转实现,这只能在以后来实现它,但是可以自己写
一个脚本做这个事情。在 SVN 的 repository 里面的"prototypes"子目录里有
两个例子:prototypes/fs_mirror_rotate.py 和 prototypes/mirror_rotate。
这两个脚本如下:

prototypes/mirror_rotate:
```
[PHP]#!/bin/sh

hosts="p01 2121
p02 2121
p03 2121"

cd `dirname $0`

# for host in hosts; do
echo "$hosts" | while read host port; do
    pid=`ps aux | grep "fs_mirror.*$host" | grep -v 'grep' | awk '{print $2}'`
    if [ $? -eq 0 ]; then
        if [ -n "$pid" ]; then kill $pid; fi \
        && ./fs_mirror_rotate.py $host \
        && /usr/local/bin/fs_mirror -v \
            -o host=$host \
            -o fssync.user=root \
            -o fssync.port=$port \
            --passwd-from=/root/.mirror/secretshost \
            --datadir=/data/fs_mirror/$host/
    fi \
    && cat /data/fs_mirror/$host/www/prima/usermap \
        | awk -F, '{printf("%s %s %s %s %s\n", $1, $2, $3, $4, $5)}' \
        | while read perm uid user gid site; do
            chown $uid.$gid /data/fs_mirror/$host/www/users/$site -R
            # gid always be ftpd
        done
done[/PHP]
```

prototypes/fs_mirror_rotate.py:
```
[PHP]#!/usr/bin/python
# -*- encoding: utf-8 -*

import os,sys,shutil
import time
import datetime

mirror = "/data/fs_mirror"
backdir = "/data/hosts"
try:
    host = sys.argv[1]
except IndexError:
    print >> sys.stderr, "Lack of a host identity"
    sys.exit(1)

day = datetime.date(*time.localtime()[:3])
new = os.path.normpath("%s/%s" % (mirror, host))
NL = len(new)
old = os.path.normpath("%s/%s/%s" % (backdir, host, str(day)))
OL = len(old)
shutil.move(new, old)
for root, dirs, files in os.walk(old):
    d_new = os.path.normpath("%s/%s" % (new, root[OL:]))
    os.mkdir(d_new)
    # status = os.lstat(root)
    # perm = status[0]
    # os.chmod(d_new, perm)
    # uid = status[4]
    # gid = status[5]
    # os.chown(d_new, uid, gid)
    try:
        os.chmod(d_new, 0755)
    except OSError:
        pass
    # print "CREATE DIR '%s'" % d_new
    for fname in files:
        f_new = os.path.normpath("%s/%s" % (d_new, fname))
        f_old = os.path.normpath("%s/%s" % (root, fname))
        os.link(f_old, f_new)
        # Hard link will reserve the permission and ownership of a file
        try:
            os.chmod(f_new, 0644)
        except OSError:
            pass
        # print "HARD LINK '%s' -> '%s'" % (f_old, f_new)

interval = datetime.timedelta(days=14)
overdue_day = day - interval
overdue_dir = os.path.normpath("%s/%s/%s" % (backdir, host, str(overdue_day)))
try:
    shutil.rmtree(overdue_dir)
except OSError, (errno, strerr):
    if errno == 2:
        print strerr
    else:
        raise[/PHP]
```

示例归档:
```
[root@stor p01]# ls -l
total 144
drwxr-xr-x+  6 root     root     4096 Oct  2 03:10 2007-10-03
drwxr-xr-x+  6 root     root     4096 Oct  3 03:10 2007-10-04
drwxr-xr-x+  6 root     root     4096 Oct  4 03:10 2007-10-05
drwxr-xr-x+  6 root     root     4096 Oct  5 03:10 2007-10-06
drwxr-xr-x+  6 root     root     4096 Oct  6 03:10 2007-10-07
drwxr-xr-x+  6 root     root     4096 Oct  7 03:10 2007-10-08
drwxr-xr-x+  6 root     root     4096 Oct  8 03:10 2007-10-09
drwxr-xr-x+  6 root     root     4096 Oct  9 03:10 2007-10-10
drwxr-xr-x+  6 root     root     4096 Oct 10 03:10 2007-10-11
drwxr-xr-x+  6 root     root     4096 Oct 11 03:10 2007-10-12
drwxr-xr-x+  6 root     root     4096 Oct 12 03:10 2007-10-13
drwxr-xr-x+  6 root     root     4096 Oct 13 03:10 2007-10-14
drwxr-xr-x+  6 root     root     4096 Oct 14 03:10 2007-10-15
drwxr-xr-x+  6 root     root     4096 Oct 15 03:10 2007-10-16
[root@stor p01]# ls 2007-10-16/etc/httpd/conf.d -l
total 112
-rw-r--r--  9 root root    58 Oct  8 13:41 bw_mod.conf
-rw-r--r--  9 root root   187 Oct  8 13:41 mod_caucho.conf
-rw-r--r--  9 root root  2965 Oct  8 13:41 site.conf
-rw-r--r--  9 root root 10919 Oct  8 13:41 ssl.conf
-rw-r--r--  9 root root 85876 Oct  8 13:41 virtual.conf
```
硬链接数是 8,这意味着上次启动 mirrord 实在 2007-10-08。

注意 fs_sync 会在传输一个普通文件之前先做 unlink,以避免破坏轮转的备份
,既然轮转脚本实际做的就是简单的创建目录和普通文件硬链接。

到目前为止,只有变更动作(CREATE, FWRITE, DELETE, MOVE)及其相应的路径名
被传输,所以如果目录/文件有属主/权限的需求,当前的 mirrord/fs_mirror
自身不能解决,要知道改变属主和权限涉及到遍历操作,因此比较消耗资源。
所以我在上面改变轮转脚本令每天其改变一次属主和权限,这当然不是一个很好
的办法,特别是在没有统一认证中心的情况下。这种更强的功能将在一个未来的
版本中实现。

使用一个认证中心是比较合理的,例如 NIS, LDAP 或 Kerberos,因为保持权限
和属主的一致是很有必要的。

**我必须指出客户端的 fs_mirror 到目前还没有彻底测试**
**这也是我时间有限,经验不足的缘故**
**因此你可能需要多花一些时间做调整**
回复 支持 反对

使用道具 举报

发表于 2007-10-16 20:20:02 | 显示全部楼层
网站页面超长,看来得花点时间消化才行
回复 支持 反对

使用道具 举报

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

本版积分规则

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