说明:
服务器是腾讯的云服务器(腾讯用的是linux container),远程登陆云服务器需要使用代理,在服务器中不能访问外网,所以当时也就没有去想做svn
需求:
1、 把同样的代码同步到不同的服务器中,尽量做到各个服务器都能在一个很短的时间内同步完成
注:各服务器之间是在同一个内网中的,数据传输速度很快,当服务器数量不是很多时不需要考虑带宽的问题
在做这项工作时遇到的问题:
- 各个服务器之间使用rsync命令进行同步,优点是可以进行增量同步,缺点是每次都需要输入密码
- 如何保证各个服务器之间的同步能够尽量在同一时间进行
- 脚本|程序的扩展性,当增加或减少服务器时如何只需要很少的修改即可正常进行同步
解决方法:
问题1:
rsync命令有一个-e参数,是用这个参数可以让其通过ssh进行数据同步。使用ssh就可以通过证书认证的方式达到不输入密码同步数据的效果。但是现实向来是残酷的,下面说下我解决这个问题的历程:
刚开始我用的是一个算标准的流程吧,但是这个在centos上不适用,我本机用的是ubuntu
#输入下面这条命令后会提示你输入一个密码用来加密,可以留空 #因为是给线上服务器同步数据,所以就添加了一个密码password ssh-keygen -f ~/.ssh/server #在~/.ssh/目录下会生成两个文件,server(私钥)和server.pub(公钥) #把公钥添加到线上服务器的认证列表中,因为是给多个服务器同时传送数据 #所以我把同一个公钥添加到了多个服务器上,以此减少认证的复杂度 ssh-copy-id -i ~/.ssh/server.pub serverUser@serverHost #把私钥添加到ssh-agent中 ssh-add ~/.ssh/server #报出如下错误 Could not open a connection to your authentication agent. #意思就是无法连接到认证代理,在这里用的是ssh-agent #执行命令 ssh-agent #然后再进行添加,还是报同样的错误 #经过一番搜查需要运行如下命令 eval `ssh-agent -s` #再次添加,这次OK了,添加时会提示输入证书的密码 #然后就是使用默认账号去连接线上服务器了 ssh serverUser@serverHost #直接登入成功,但是咱的目的还没有达到,因为不能每次同步之前都先执行一下上面的命令吧 #然后我就找到了keychain这个东东,网上说用来管理证书会很方便,不用再去打开一个bash #具体设置方法是编辑~/.bash_profile,在最后添加(我是参考着这个设置的:https://wiki.gentoo.org/wiki/Keychain,打开可能会有些慢) keychain ~/.ssh/id_dsa . ~/.keychain/$HOSTNAME-sh . ~/.keychain/$HOSTNAME-sh-gpg #保存之后执行一下 source ~/.bash_profile #确实也有用,但是最后我发现每次我远程登陆这个服务器的时候这个证书都可以使用,我 #还是比较喜欢每次同步时输入以此证书密码 #因此又加入了--clear,每次登陆时重新添加证书,这时问题又来了,就是每次登陆此服务 #器我需要输入两次密码,后来我想把keychain的执行命令放到脚本中去,最后宣告失败 #因为在脚本中的话keychain的设置会不能生效 #最后的解决办法是eval `ssh-agent -s`和keychain结合使用 #使用eval `ssh-agent -s`打开一个bash shell,使用keychain --clear清除证书
问题2:
通过资料得知bash是单线程,但是可以通过“&”实现同时执行多个命令,然后我就写了一个for循环,循环体后面加一个“&”符号确保所#执行配置文件,下面会讲到
source server.conf
#源路径 SOURCEDIR='/data/web'
#线上服务器的路径 DESTDIR='/data/'
#同步所需要的参数,--exclude参数指定要不需要同步的文件
#注意!--exclude=.*不要写成--exclude=".*",否则此参数会失效,具体原因我还没找到 RSYNCOPTION='-avz -i --exclude=.* --exclude=crossdomain.xml' echo "sync ${SOURCEDIR}"
#循环同步到线上服务器,下面会讲到配置 for server in $IP do {
#rsync 命令 COMMAND="rsync ${RSYNCOPTION} ${SOURCEDIR} ${USER}@${server}:${DESTDIR}"
#把所执行的命令和命令执行结果直接输出到日志文件中, 也许应该把正确日志和错误日志分开 echo $COMMAND >> ${LOGDIR} $COMMAND >> ${LOGDIR}
#在后面添加一个"&"符号可以让for循环里面所有的步骤一起执行而不需要exec by step,这就在一定程度上解决了多个服务器之间同步有延迟的情况 } & done
问题3:
脚本的扩展性,因为同步路径都是固定的,所以不需要指定路径,需要变的只有增加或减少服务器的ip地址,配置server.conf的代码如下:
#!/usr/bin/env bash # 判断是否已经运行了ssh-agent,如果不进行判断的话时间久了会出现很多ssh-agent的进程 agentExists=`ps aux|grep ssh-agent | grep -v grep` if [ -z ${agentExists} ] then
# 清除已经存在的证书
keychain --clear
else
eval `ssh-agent -s`
fi ssh-add ~/.ssh/server1 # 服务器的ip地址,多个地址以空格隔开 IP='serverHost1 serverHost2' # servers user # 因为腾讯上一个应用的所有服务器用户名都一样,所以加了这个参数,如果也有人用的是腾讯的cvm的话也不用为每个ip指定用户名了 USER='serverUser' # 日志文件,记录同步日期和同步的内容的 LOGDIR='./rsync.log' DATE=`date`
#同步时间 echo "[${DATE}]" >> $LOGDIR
这个脚本我写好有一段时间了,现在想想我不应该直接把代码同步到线上,应该在线上服务器分别建立一个同步用文件夹,然后从同步文件夹同步到线上。
还有一个解决方案就是使用linux的磁盘自动挂载技术(autofs),所有的服务器挂载同一块磁盘,读取同一块磁盘中的数据,这样只需要同步一次即可全部更新,但是这时负载均衡的一个优势就失去了。当存有代码的服务器宕机之后所有的服务器都将不可用。各有利弊吧。
加油!Boy!