因为上了独服,就不能依靠VPS供应商的备份了,其实大部分经济型VPS都没备份的,但是人家硬盘起码是RAID1+0,就算物理故障了只要不太严重不是电脑爆炸硬盘全毁之类的还是能修复的。独立服务器就不一样了,为了省钱不上RAID,不租备份,所有备份都得自己折腾了,这些配置折腾起来实在烦人,还是得记下来省下以后再搜索的功夫。
一、同步服务器的选择
备份服务器不需要好的CPU或者内存,只要求硬盘大,网络好就行了,对我来说100G以上的备份空间是必须的,然而100G以上的VPS压根就没几家会提供,便宜的更是难找,好在一不小心发现fdcservers在搞特价,256M内存300G硬盘10Mout100Min无限带宽的VPS只要9刀,没有比这个更合适做备份服务器的了。
二、文件同步
1.rsync
文件同步一般都用rsync。
rsync -avz -e ssh /path backupuser@backuphost:/backuppath
a是全部,v是verbose输出,z是传输时压缩选项,-e ssh是通过ssh来传输,于是会让你输入密码,如果要加入cron的计划任务让它定时运行,那就要配置一下ssh的authorized_keys了。
在源主机当前用户下运行
ssh-keygen -t dsa
加若干回车什么都不填,就能在 ~/.ssh/id_dsa_pub 中生成公匙,然后把这个公匙字符串添加到目标主机用户的 ~/.ssh/authorized_keys 文件(无则新建)即可。
这个是基础,我都是用rsync先同步一遍然后在开始试着实时同步的。
2、inotify-tools + rsync
rsync+cron确实方便,但是我需要备份的小文件实在太多了,可能多达2百万,rsync性能成问题;再加上我很贪心地想要实时同步,所以就展开了google之旅。最先尝试的工具叫inotify-tools。
inotify-tools是基于linux 2.6.11内核以后加入的inotify机制的一套工具,http://inotify-tools.sourceforge.net/,debian/ubuntu官方就有源,直接 apt-get install inotify-tools 就能安装。
使用起来也很简单,直接根据说明文件写个脚本就ok了,然后就nohup一下让它在后台运行即可,只要一有“close_write”事件,就会同步过去。
#!/bin/sh # get the current path CURPATH=`pwd` inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \ -e close_write /监控路径 | while read date time dir file; do FILECHANGE=${dir}${file} # convert absolute path to relative FILECHANGEREL=`echo "$FILECHANGE" | sed 's_'$CURPATH'/__'` rsync --progress --relative -vrae 'ssh -p 22' $FILECHANGEREL 用户名@备份主机:/备份路径 && \ echo "At ${time} on ${date}, file $FILECHANGE was backed up via rsync" done
如果想要modify,create,move,attrib等事件发生时都同步,那么就要加上参数: -e close_write,modify,create,move,attrib 。如果要剔除文件/路径,可以用 -exclude 参数,后跟一个POSIX格式的正则表达式即可。
话虽如此,不知为何我按照manpage的说明这样做时同步就不太灵光了,日志文件非常混乱,根本不知道发生了什么orz。而且因为很多操作可能同时有多个inotify事件,还有类似vi等生成的临时文件也会试着同步,而exclude语法的POSIX的正则不知为何似乎是没有识别,总之因为日志不正常我也不明白毛病出在哪里。好在除了inotify-tools之外也有其他运用inotify机制的同步工具,所以再次转移阵地。
3. sersync
sersync是国人用C++写的开源的东东,在StackOverflow上看到有人推荐点进去一看还有中文,好亲切啊。
题外话,sersync居然是用NetBeans开发的,这奇葩的东东我记得超级让人讨厌的,远不如Eclipse,不知道现在好点了没。
sersync相对于inotify-tools的优势是整合了failover机制,自动识别临时文件(不知道实现得如何)不用写一堆exclude。其他的自称的优势我都不认为是优势,例如多线程机制似乎完全没有必要,增加系统开销,增加出错隐患,但是完全看不出有什么作用。服务器之间的网速单线程与多线程会有多少区别?
sersync文档很乱,只有基于web页面的;源码也没按GNU的格式来;也没有弄-help;总之让人感觉没用心做的样子(虽然我自己用的东西也经常这样,但是发出来的东西文档还是会好好写的-.-),然而使用下来却意外地顺手。
先修改备份服务器的 /etc/rsyncd.conf,修改path,host allow等值
uid=root gid=root max connections=36000 use chroot=no log file=/var/log/rsyncd.log pid file=/var/run/rsyncd.pid lock file=/var/run/rsyncd.lock [tongbu1] path=备份路径 comment = 注释 ignore errors = yes read only = no hosts allow = 192.168.0.100/24 hosts deny = *
配置好以后,以root运行 rsync -daemon
然后切到主服务器,把binary解压到主服务器,并编辑confxml.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <head version="2.5"> <host hostip="localhost" port="8008"></host> <debug start="false"/> <fileSystem xfs="false"/> <filter start="true"> <exclude expression="(.*)\.db3?"></exclude> <exclude expression="(.*)journal"></exclude> </filter> <inotify> <delete start="true"/> <createFolder start="true"/> <createFile start="false"/> <closeWrite start="true"/> <moveFrom start="true"/> <moveTo start="true"/> <attrib start="true"/> <modify start="true"/> </inotify> <sersync> <localpath watch="监控路径"> <remote ip="目标服务器IP" name="tongbu1"/> </localpath> <rsync> <commonParams params="-artuz"/> <auth start="false" users="root" passwordfile="/etc/rsync.pas"/> <userDefinedPort start="false" port="874"/><!-- port=874 --> <timeout start="false" time="100"/><!-- timeout=100 --> <ssh start="false"/> </rsync> <failLog path="/tmp/rsync_fail_log.sh" timeToExecute="60"/><!--default every 60mins execute once--> <crontab start="false" schedule="600"><!--600mins--> <crontabfilter start="false"> <exclude expression="(.*).db3?"></exclude> </crontabfilter> </crontab> <plugin start="false" name="command"/> </sersync> </head>
其中
- filter中可用正则表达式定义例外
- inotify中可选择监听事件的true/false:
- auth那一段users写目标主机的用户名,start=false说明使用的是key而不是密码
这配置文件定义得有点古怪,不说那个古怪的start标签,为毛要把remote放在localpath里面,而rsync里面居然只有用户信息没有host信息,这种设计实在让我百思不得其解。
不管怎么说,就这样一个简单的同步服务配置好了,然后执行
./sersync2 -d -n 1
就可以自动监控文件的改动并同步了。-n 1是为了让它单线程执行。
sersync最大的缺点是文档极烂和不生成日志(有没有搞错!),文档也就算了,凑合着能用就行,没有日志就有点过分了,脚本语言没有日志还能看解释器的出错信息,C++没有日志就是找死流啊。目标服务器上倒是有一堆意义不明的日志,类似file received,server connected,之类的日志,不过这貌似是rsync日志,解读不能。
4. inotify-tools ++
用了sersync一天后,发现严重的问题,也许就是多线程惹的祸,如果监听modify事件,则在大文件持续写入的时候只会同步一部分;另外,sersync其超高的内存和CPU占用也是个大问题。
无奈重新研究inotify-tools,这次被我找到症结所在了。
inotify的-exclude参数不太好用,但是过滤文件其实并不一定要用-exclude,也可以读取文件名后用shell脚本来过滤,所以改进了2的方案,新脚本如下:
# get the current path CURPATH=`pwd` SRCPATH=/blahblah DSTPATH=/blahblah DSTUSER=blahblah DSTHOST=blah.blah.blah.blah inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \ -e close_write,move,modify $SRCPATH | while read date time dir file; do # filter sqlite3 files & logs & hidden files(startswith ".") echo $file | egrep -q "(db$|db3$|journal$|log$|^\.)" if [ $? -eq 0 ]; then continue fi FILECHANGE=${dir}${file} # convert absolute path to relative FILECHANGEREL=`echo "$FILECHANGE" | sed 's_'$CURPATH'/__'` rsync --progress --relative -vrae 'ssh -p 22' $FILECHANGEREL $DSTUSER@$DSTHOST:$DSTPATH && \ echo "At ${time} on ${date}, file $FILECHANGE was backed up via rsync" done
这次效果就很不错了,到底是加入到各个linux发行版软件源的工具,比sersync稳定,内存和CPU也能让我满意了。
三、数据库同步
数据库就不能用rsync来同步了,一是因为尺码太大就算完全一样的文件rsync要用checksum之类的工具判断他们完全一样都得花不少功夫;二是因为它们会随时修改,rsync到一半内容改了那备份服务器的数据库就直接损坏了,备份就没有意义了。
1.mysql
mysql可用replication,虽然这个是用来同步数据库,减轻单数据库访问压力的,实际上完全可以用来当实时备份用。我是参照了这篇攻略。写得非常好,我就纯引用吧,略微修改了一下顺序,简化了一下操作。
1. master机授权slave用户
> GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'%' IDENTIFIED BY 'your_password'; > FLUSH PRIVILEGES;
2. master机修改my.cnf
log-bin = /home/mysql/logs/mysql-bin.log binlog-do-db=my_database server-id=1
重启mysql
3. master机锁定数据库,并导出数据库复制到slave机
> FLUSH TABLES WITH READ LOCK; # mysqldump my_database -u root -p > /home/my_home_dir/database.sql; # scp -C root@128.0.0.1:/home/my_home_dir/database.sql /home/my_home_dir/
4. master机记录log的status
# mysql -u root -p > SHOW MASTER STATUS; +---------------------+----------+-------------------------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +---------------------+----------+-------------------------------+------------------+ | mysql-bin.000001 | 21197930 | my_database,my_database | | +---------------------+----------+-------------------------------+------------------+
5. slave机修改my.cnf配置
server-id=2 master-host=128.0.0.1 master-connect-retry=60 master-user=slave_user master-password=slave_password replicate-do-db=my_database relay-log = /var/lib/mysql/slave-relay.log relay-log-index = /var/lib/mysql/slave-relay-log.index
重启数据库
6. slave机建立数据库并导入数据
> CREATE DATABASE my_database; # mysql -u root -p my_database </home/my_home_dir/database.sql
7. slave机根据master机的status设置replication点
> slave stop; > CHANGE MASTER TO MASTER_HOST='128.0.0.1', MASTER_USER='slave_user', MASTER_PASSWORD='slave_password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=21197930; > slave start;
8. master机解锁数据库
> unlock tables;
大功告成。
Tips1: 值得一提的是我在replication的时候经常碰到duplicate entry错误导致replication被终止,那篇攻略没有提及,其实可以通过slave-skip-errors=1062,来忽略这个错误,另外1053也可以考虑忽略,这样就避免了网络问题引起的错误。在my.cnf相应位置加入:
slave-skip-errors=1062,1053
Tips2: 一旦出现挂掉或者换hostip,要再同步重复3,4,6,7步骤即可,不行则必须清理日志:
slave:
rm *relay* rm master.info
master:
cd /home/mysql/logs/; rm -f *;
多master对单slave
搞定了单对单的replication,又有新的问题了:有些其他的东东为了分散风险起见放在了其他的服务器,而上述配置只支持一对一或者一对多的master-slave,但是我却想实现多对一的mysql replication。
这也是有办法的,开N个mysql instance就行了,然而不必那么麻烦,mysql配置文件自动支持多服务器,可以省事不少。
在多mysql起来以后,无非就是重复上述过程而已,偷懒我就不写了。
2.sqlite3
sqlite3不支持replication,想要同步非常得困难。好在这数据库就是我写的,所以只要我在数据库修改的同时也往同步服务器的数据库做出同样的修改即可。
不过sqlite3不支持远程访问,所以同步起来要更费一番周折:用消息队列通知同步服务器更新数据库。
消息队列就有点像加强无数倍的pipe;pipe已经是强大得不得了的东东了,MQ是强化版理所当然地也是强大得不得了。原来我都不知道有这东东,一直很苦手进程间通信应该怎么处理,后来一google发现这玩意儿流行得不得了,真是居家旅行必备的大杀器。
我用的是RabbitMQ,超喜欢他们的文档,在我看来是好的文档的典范啊,所以也就不罗嗦了,以后我要是忘记怎么用了当然也会直接去看他们文档而不是看自己的博客。
与之成反面教材的就是mysql的文档,详细是详细了,可也太琐碎,一点条理也没有,也没有范例可以参考,看完文档经常需要看看其他人写的tutorial来理清一下思路。
四、总结
文件的同步还算简单,几个方法中我还是比较推荐inotify-tools ++方法,sersync虽然有很多缺点,但是在只有小文件的情况下似乎没什么问题,还有中文的配置攻略和扣扣群(orz),e文苦手的可以一试。
而数据库的同步异常麻烦,可能因为每次配置都要锁正在运行的数据库让我压力很大的缘故吧:)定期备份才是更省心省力的方法,否则真是折腾:)
如此一来所有的文件都同步好了,虽然累了个半死,不过在遇上服务器故障的时候就完全不用慌了。:)