1.  概述

amazon web services中的Amazon Simple Storage Service (Amazon S3)是一个很好的在线文件存储集群服务,安全可靠、接口丰富,并且收费价钱也比较合理(1G数据存储一个月只要10美分左右)。这比为了实现服务器文件备份专门准备一台服务器划算多了,而且S3的存储服务器可以选择在美国或者欧洲,于是和被备份的服务器自然形成了跨地域的分布式存储,数据抵抗自然灾害的能力由此提高了很多。本文简要介绍将Linux服务器上的文件增量备份到S3服务上的配置实现方法。

2.  Duplicity简介

官方网站位于http://www.nongnu.org/duplicity/ 。这是一个使用Python语言实现的文件增量备份工具,其利用rsync底层类库,实现仅把文件中变化了的数据存入增量备份包的工作方式(类似给文件生成Patch补丁的方式),调用pgp对数据包进行签名和加密,而且支持本地文件系统、远端ftp、ssh/scp、rsync、WebDAV、WebDAVs、HSi以及Amazon S3等非常丰富的备份存储介质。

2.1  特别注意事项!

duplicity 的文件如果损坏一点点,就会导致恢复失败。特别是在增量备份的情况下,如果有中间版本损坏,会导致从此之后的数据都完全恢复不出来(就只能试着手工抢救文件了,困难并且有相当可能仍然恢复不回来)。

所以对重要数据,应经常做全量备份(用 --full-if-older-than 参数)。未来不再更新的最终版本数据,更是应该在 duplicity 之外保留一个全量备份,来避免介质损坏。

3.  安装及配置

3.1  手工安装

其实Debian自身已经提供了Duplicity的软件包,但Debian etch带的Duplicity是古老的0.4.2版本,还不支持向S3服务发送备份。我们得另想办法搞定高版本的。

  1. 安装Duplicity运行所需要的组件:apt-get install librsync1 python-central python python gnupg python-gnupginterface
  2. 准备Duplicity源代码安装所需要的编译环境:apt-get install build-essential duplicity
  3. 安装源代码安装所必须的头文件:apt-get install python-dev librsync-dev
  4. 下载http://savannah.nongnu.org/download/duplicity/duplicity-0.5.00.tar.gz
  5. tar zvxf解压上一步获得的压缩包
  6. python setup.py install
  7. 应该已经顺利安装成功了,“duplicity --version”应该显示是0.5.00版本

先不管S3,我们先玩一下duplicity增加一点感性认识:

 duplicity /etc file:///tmp/dup_test
注意这里“file:”后面是三个斜杠。运行之后程序会问“GnuPG passphrase:”,并且让再确认输入一次,这大致可以理解成是一个标记此次备份的签名,如果duplicity在以前进行过的备份中找不到这个签名,则会进行完整备份,否则进行增量备份。这个签名不可以为空,此次测试,随便输入两次test就可以了。运行完成之后在/tmp/dup_test文件夹就可以看到生成的备份包了。

如果我们不希望duplicity运行时候问“GnuPG passphrase:”怎么办呢,可以把这个参数设置成PASSPHRASE环境变量。比如我们在刚才运行duplicity之前,可以先执行“export PASSPHRASE=test”就可以把手动输入签名的步骤给省了。

下面我们来考虑和S3之间的联动,这需要python-boto这个包,但是etch里面还没有提供这个软件包。没关系,我们可以通过easy_install来自行安装:

  1. 下载http://peak.telecommunity.com/dist/ez_setup.py ,并用python执行这个文件(python ez_setup.py,执行时应该是需要root权限),系统会自动生成easy_install命令。
  2. easy_install boto
  3. 这样会通过easy_install安装python-boto的1.4c版本。

boto需要进行一些设置才能正常工作。首先要在http://aws.amazon.com 注册Amazon Simple Storage Service (Amazon S3)服务的帐号,并且确定付款方式(比如通过Visa信用卡支付美元)。然后查找自己访问S3服务的ID和Key(在AWS注册并登陆后,页面右上角附近有一个写着“Your Web Services Account”的黄色下拉菜单,鼠标在这个菜单上停留,选择“AWS Access Identifiers”即可以查到自己的Access Key ID以及Secret Access Key,这两个相当于访问S3用的用户名和密码)。这两个参数通过环境变量传递给duplicity(对于0.5.00版本来说),也就是需要:

 export AWS_ACCESS_KEY_ID=<your access key>
export AWS_SECRET_ACCESS_KEY=<your access key>
这样以后duplicity以及boto就会使用这组ID和Key来访问S3存储空间。

此时如果我们把前面测试用的指令改成

 duplicity /etc s3+http://BucketName
这里BucketName是在Amazon上的一个Bucket名称,只有在Bucket下面,才能存储文件。如果指定的这个Bucket不存在,那么没关系,duplicity会自动创建对应的Bucket;但是如果这个Bucket名字已经被别人使用了,那么软件会报错提示(S3应该是以XML格式给出出错提示,需要仔细观察和阅读)。对Bucket明明规则的限制则似乎和网址URL中能够正常使用的字符是一致的。

如果要取回备份的文件,可以类似这样:

 duplicity s3+http://BucketName /home/tmp
这里注意取回时的PASSPHRASE要和备份时的一致,否则无法成功取回文件。

duplicity的详细使用说明参见“man duplicity”或者http://www.nongnu.org/duplicity/duplicity.1.html

3.2  从deb包进行安装

手工安装会使得Debian的dpkg管理工具搞不清python-boto和duplicity包的当前状况,因此lenny下我倾向于使用官方源里面的duplicity版本(0.4.11,基本上够用了)。etch下则还是古老的0.4.2,只好试着将高版本Debian的deb包弄下来想办法搞定版本依赖关系来安装。所幸只要不是最新的包,与etch的版本依赖的不匹配情况并不十分严重。我在etch环境下重新编译了duplicity 0.4.12-2以及python-boto 1.2a-1的deb包,经测试是完全可以在etch下正常工作的。可以下载后直接使用dpkg -i指令安装,并相应用apt-get install -f来自动安装其他依赖的软件包。这两个deb文件下载见(很可能已经不可用了,因为现在etch下面的Python版本比下面这两个deb文件编译时所指定依赖的Python版本更新一些……):

软件配置细节请参考手工安装的说明,但有一点是不同的:duplicity 0.4.12版本不是通过环境变量来接收S3访问ID和Key的,而是如下这样:编辑/etc/boto.cfg文件或者~/.boto文件,输入内容

 [Credentials]
aws_access_key_id = <your access key>
aws_secret_access_key = <your access key>
其中大于号和小于号要一并删掉改为自己的ID和Key。实际上也就是duplicity没有管S3的ID和Key的事儿,是让boto自己进行处理的。

3.3  在Mac下安装

Darwin Ports有提供这个包,虽然不算很新。装好Darwin Ports之后,“sudo port install duplicity”即可,详细可以参照http://duplicity.darwinports.com/

4.  安全性探讨

我的理解是duplicity在生成备份文件包的时候用pgp加密过了,所以理论上备份文件包被别人弄走了也不怕,不过我没有仔细确认过这一点。

另外,S3有自身的权限控制机制,参见S3 ACLs。我的S3空间默认就是屏蔽公众访问的,所以权限控制这一块其实我也没有更仔细地去确认过。。

5.  问题与技巧

5.1  关于S3访问工具

如果想要登陆S3操作已经存储的文件备份,那么最好还是有一个对应的客户端才方便。Mac下可以使用Transmit。考虑所有平台的话,有开源的Java客户端实现JetS3t Cockpit。另外听说收钱的Jungle Disk也是相当好用的。

绝大多数 Linux 发行版带有一个叫 s3cmd 的包,是一个基于 Python 完成的命令行工具,功能非常全。

5.2  关于403 Forbidden错误

有时访问S3时,会得到一个S3ResponseError错误,并且说403 Forbidden。那是因为duplicity所在主机的时间与S3服务器差别太大(超过了10分钟)造成的。仔细看错误提示,里面有给出服务器当前时间和客户端当前时间。可以用ntpdate等互联网对时服务校正时间,或者干脆用“date --set="2008-01-23 13:47:43"”这样的指令手工进行修正。

5.3  关于Invalid SSH password错误

有时访问ssh或者scp时,会得到Invalid SSH password错误提示,虽然已经用类似ssh://user:pass@host这样的格式设置了正确的登陆密码。此时应当放弃在ssh://中写入密码,并且给duplicity增加--ssh-askpass运行参数,即可以正确完成向ssh或者scp进行的数据备份工作。

5.4  一次备份多个目录的方法

当需要一次备份多个目录时,可以这么写:

 duplicity --include /etc --include /var/www --exclude '**' / file:///usr/local/backup

5.5  备份任务的自动执行

这里要用到Cron了。我在root用户下用crontab -e设定每天凌晨3点执行/boot/script/backup.sh脚本:

 0 3 */1 * * /root/script/backup.sh
backup.sh脚本中的内容则是:
 export PASSPHRASE=backup
export AWS_ACCESS_KEY_ID=<your access key>
export AWS_SECRET_ACCESS_KEY=<your access key>
duplicity --include /etc --include /var/www --exclude '**' / s3+http://elias.cn-backup
注意这里要把elias.cn-backup改成你自己的bucket名字。

不要忘记在0.5.00以前版本的duplicity编辑/etc/boto.cfg或者root/.boto文件设置访问S3的ID和Key,并且给backup.sh用“chmod +x”添加可执行权限。

5.6  备份MySQL

duplicity自己只能备份文件系统上的东西,我们可以通过与其他脚本的配合来解决MySQL、SVN等服务的备份问题。比如我们把backup.sh改成这样:

 export PASSPHRASE=backup
export AWS_ACCESS_KEY_ID=<your access key>
export AWS_SECRET_ACCESS_KEY=<your access key>
mysqldump --password=YourSqlPass --opt --skip-extended-insert database > /root/backup/backup-file.sql
duplicity --include /etc --include /var/www --include /root/backup --exclude '**' / s3+http://elias.cn-backup

5.7  “GnuPG exited non-zero”错误

如果Duplicity使用过程中遇到如下错误并造成程序运行中止:

  File "/var/lib/python-support/python2.4/GnuPGInterface.py", line 639, in wait
    raise IOError, "GnuPG exited non-zero, with code %d" % (e << 8)
IOError: GnuPG exited non-zero, with code 131072

简单来讲,这是因为Duplicity运行过程中调用GnuPG进行处理时,GnuPG出错了。这时应该查询系统的错误信息日志(比如dmesg)看看到底GnuPG是为什么出错。我当时遇到这个问题时,是GnuPG把系统内存用光了导致出错。我备份后删除了历史备份存档,让Duplicity需要检查的历史备份深度减少,就可以大致改善这个内存占用问题。(比如,让Duplicity删除15天以前的备份存档。)

5.8  “Invalid data - SHA1 hash mismatch for file”时如何手工挽救数据

参考 Restoring by Hand 一文。

主要原理是先用 gpg 解密 duplicity 的备份包,然后用 tar 解包就是了。类似这样:

gpg --multifile --decrypt duplicity-full.20110127T131352Z.*.difftar.gpg
和这样:
 for t in duplicity-full.20110127T131352Z.*.difftar; do tar xf $t; done

5.9  让 Duplicity 更健壮可靠

有时(比如由于网络原因,或者多个 duplicity 指令同时在运行并且在备份同一份文件),duplicity 的备份过程会没有完全完成,在 S3 等存储上留下不完整的包,这有可能影响后续备份的正确进行。所以通常我会在执行 duplicity 的备份指令之前,先执行一下

 duplicity cleanup --force s3+http://elias.cn-backup
指令来清除不完整的备份包。

一定要注意避免对 cleanup 指令使用 --extra-clean 参数,这个参数会在遇到一个损坏包时,删除整个备份链条上的增量备份包的索引,导致全部备份无法用于 restore 。

如果确实不放心,可以考虑利用 s3cmd 工具的 sync 指令,给 duplicity 的 S3 存储空间做一个镜像,以便误删除数据时可以找回。用 Amazon 官方的 CLI 工具应该也能做到同样效果。

 aws s3 sync s3://bucket1/folder1 s3://bucket2/folder2
再或者,也许开启 S3 空间的数据版本记录,也有助于这种找回。

5.10  让 Duplicity 的文件存储更健壮可靠

由于存储介质本身的可靠性问题,在某些小概率情况下,Duplicity 生成的具体备份数据存储文件可能会出现极其微小的损坏,于是导致在恢复数据时 gpg 解密失败(gpg 特别严格,几乎无法接受哪怕是一个字节的错误),可以让 Duplicity 在生成备份数据文件的时候,用 par2 做一个冗余恢复记录(默认会多占用 10% 左右的存储空间)。

Duplicity 大致从 0.7 版本开始能比较靠谱地支持这个特性,使用方法是在目标存储介质的 url 前加 “par2+”这样的前缀,例如“s3://bucket2/folder2”就改成写“par2+s3://bucket2/folder2”。

6.  其他类似解决方案

其实Perl实现的backup-manager也是能够备份到S3上的,etch自带的版本就可以。但是试着向S3进行备份,会报错。查看/var/log/user.log,能看到详细的错误提升,原因是缺少了Net::Amazon::S3这个库。可以用“cpan -i Net::Amazon::S3”来安装,也可以直接用Debian里面的libnet-amazon-s3-perl这个包(这个包etch里面没有……得自己想办法盗用高版本的)。此外还得安装libxml2-dev这个包。如果仍然不能正常工作的话,就看user.log的错误提示吧。

但是backup-manager工作在S3上有一个重大问题:当上传3M的文件时,backup-manaer会占用系统26M左右的内存;上传单个文件10M大小的文件时,大约占用64M左右的内存。这怕是有点儿恐怖。因此虽然backup-manager别的功能都感觉相当不错,仍然是放弃了它,改用duplicity。而duplicity无论怎样使用,占用的内存都保持在10M左右。

GlossyBlue theme adapted by David Gilbert
Powered by PmWiki