一.发布系统要解决的问题
一个比较完善的发布系统首先肯定是要能发布文件,其次当发布出现问题时要能支持撤销,避免长时间无法解决问题影响产品的正常运营。
对于发布脚本语言如PHP,shell加入语法检查,有语法错误阻止本次发布。
二.原理
我们正在使用的发布系统基于PHP+shell开发,发布的时候填好文件列表,从当前机器A发布到目标机器B,发布之前A机器通过ssh协议认证登陆到B机器执行shell命令备份待发布的文件列表
然后使用rsync从A机器同步文件列表到B机器,发布完成后写入当前发布记录到数据库,当需要回滚的时候将对应的备份文件覆盖到B机器即可。
三.要解决的问题
1.使用rsync以及备份目标机器文件需要免密登陆
A机器到B机器实现免密登陆,需要使用ssh-keygen生成当前用户的登陆秘钥,把当前用户的公钥追加到目标机器用户的 .ssh/authorized_keys文件中
以下shell是把当前机器的公钥追加到目标机器的authorized_keys文件
USER=www IP=192.168.1.100 #B机器ip PORT=22 #B机器SSH协议端口 cat /home/${USER}/.ssh/id_rsa.pub | ssh ${USER}@${IP} 'cat>>/home/${USER}/.ssh/authorized_keys'
后续无需输入密码即可在A机器免密登陆B机器
2.发布到多台机器
如果目标机器有多台,可以循环发布到目标机器。如果每次更新的文件过大,可以发布到一台中心节点服务器,然后使用sersync同步到其它机器。https://code.google.com/archive/p/sersync/downloads
修改config.xml rsync节点<ssh start="true"/>,并使用免密登陆的用户启动sersync服务
#!/bin/bash #------------------------------------------------------------------------------------------------------- #从当前机器发布文件到远程机器,如果是分布式机器,以一台机器为主节点通过sersync同步到其它节点 # # #参数说明 # 发布 #$1:执行动作send #$2:远程目标机器ip #$3:远程目标机器端口 #$4:当前机器发布根目录 #$5:远程目标机器发布根目录 #$6:远程目标机器备份根目录 #$7:发布版本号 #$8:文件列表 多个文件或者目录使用","隔开 # # # 回滚 #$1:执行动作roll #$2:远程目标机器ip #$3:远程目标机器端口 #$4:当前机器发布根目录 #$5:远程目标机器发布根目录 #$6:远程目标机器备份根目录 #$7:回滚版本号 #---------------------------------------------------------------------------------------------------- source /etc/profile umask 022 readonly TARGET_IP=$2 #远程目标机器ip readonly TARGET_PORT=$3 #远程目标机器ip readonly USER=www #执行远程shell命令用户 readonly SOURCE_ROOT=$4 #当前机器发布根目录 readonly TARGET_ROOT=$5 #远程目标机器发布目录 readonly BACKUP_ROOT=$6 #远程目标机器备份路径 readonly execssh="/usr/bin/ssh -p ${TARGET_PORT} -o StrictHostKeyChecking=no ${USER}@${TARGET_IP}" #在远程目标机器执行shell命令 #发布 function send(){ IFS="," error=0 aFile=($1) for file in ${aFile[@]} do if [ ! -e "${SOURCE_ROOT}/${file}" ];then fileList+={$file} error=1 fi done if [ "${error}" != 0 ];then echo "file list is not exists in ${SOURCE_ROOT}" echo $fileList exit 1 fi eval "${execssh} ${TARGET_ROOT}/cron/sendfile.sh backup ${1} ${2} ${TARGET_ROOT} ${BACKUP_ROOT}" if [ "$?" == 0 ];then cd ${SOURCE_ROOT} fileList=$(echo "${1}" | tr "," " ") #/usr/bin/rsync -avzR -e ssh ${fileList} ${USER}@${TARGET_IP}:${TARGET_ROOT} eval "/usr/bin/rsync -avzR '-e ssh -p ${TARGET_PORT}' ${fileList} ${USER}@${TARGET_IP}:${TARGET_ROOT}" else echo "back up faild!!!" exit 2 fi exit 0 } #回滚 function roll(){ $execssh "tar zxf ${BACKUP_ROOT}/${1}.bak.tar.gz -C ${TARGET_ROOT}" echo 'ok' } #备份 function backup(){ target_root=$3 back_root=$4 cd ${target_root} IFS="," aFile=($1) fileList="" for file in ${aFile[@]} do if [ -e "${target_root}/${file}" ];then fileList+="$file " fi done if [ -n "${fileList}" ];then if [ ! -d ${back_root} ];then mkdir -p ${back_root} if [ "$?" != 0 ];then echo "mkdir backup dir ${back_root} fail" exit 1 fi fi tar czf ${back_root}/${2}.bak.tar.gz $1 if [ "$?" == 0 ];then echo "backup file ${back_root}/${2}.bak.tar.gz!" fi fi } function argsCheck(){ if [ -z $2 ] || [ -z $3 ] || [ -z $4 ] || [ -z $5 ] || [ -z $6 ] || [ -z $7 ];then echo "useage ${0} send|roll TARGET_IP TARGET_PORT SOURCE_ROOT TARGET_ROOT BACKUP_ROOT VER" exit 1 fi if [ ! -d $4 ];then echo "SOURCE_ROOT ${SOURCE_ROOT} is not exsists!" exit 2 fi } case $1 in "send") argsCheck "$@" send $8 $7 #文件列表版本号 ;; "backup") backup $2 $3 $4 $5 ;; "roll") argsCheck "$@" roll $7 #版本号 ;; *) echo "useage ${0} send|roll TARGET_IP TARGET_PORT SOURCE_ROOT TARGET_ROOT BACKUP_ROOT VER" ;; esac
#!/bin/bash #------------------------------------------------------------------------------------------------------- #从当前机器发布文件到远程机器,如果是分布式机器,以一台机器为主节点通过sersync同步到其它节点 # # #参数说明 # 发布 #$1:执行动作send #$2:远程目标机器ip #$3:远程目标机器端口 #$4:当前机器发布根目录 #$5:远程目标机器发布根目录 #$6:远程目标机器备份根目录 #$7:发布版本号 #$8:文件列表 多个文件或者目录使用","隔开 # # # 回滚 #$1:执行动作roll #$2:远程目标机器ip #$3:远程目标机器端口 #$4:当前机器发布根目录 #$5:远程目标机器发布根目录 #$6:远程目标机器备份根目录 #$7:回滚版本号 #---------------------------------------------------------------------------------------------------- source /etc/profile umask 022 readonly TARGET_IP=$2 #远程目标机器ip readonly TARGET_PORT=$3 #远程目标机器ip readonly USER=www #执行远程shell命令用户 readonly SOURCE_ROOT=$4 #当前机器发布根目录 readonly TARGET_ROOT=$5 #远程目标机器发布目录 readonly BACKUP_ROOT=$6 #远程目标机器备份路径 readonly execssh="/usr/bin/ssh -p ${TARGET_PORT} -o StrictHostKeyChecking=no ${USER}@${TARGET_IP}" #在远程目标机器执行shell命令 #发布 function send(){ IFS="," error=0 aFile=($1) for file in ${aFile[@]} do if [ ! -e "${SOURCE_ROOT}/${file}" ];then fileList+={$file} error=1 fi done if [ "${error}" != 0 ];then echo "file list is not exists in ${SOURCE_ROOT}" echo $fileList exit 1 fi eval "${execssh} ${TARGET_ROOT}/cron/sendfile.sh backup ${1} ${2} ${TARGET_ROOT} ${BACKUP_ROOT}" if [ "$?" == 0 ];then cd ${SOURCE_ROOT} fileList=$(echo "${1}" | tr "," " ") #/usr/bin/rsync -avzR -e ssh ${fileList} ${USER}@${TARGET_IP}:${TARGET_ROOT} eval "/usr/bin/rsync -avzR '-e ssh -p ${TARGET_PORT}' ${fileList} ${USER}@${TARGET_IP}:${TARGET_ROOT}" else echo "back up faild!!!" exit 2 fi exit 0 } #回滚 function roll(){ $execssh "tar zxf ${BACKUP_ROOT}/${1}.bak.tar.gz -C ${TARGET_ROOT}" echo 'ok' } #备份 function backup(){ target_root=$3 back_root=$4 cd ${target_root} IFS="," aFile=($1) fileList="" for file in ${aFile[@]} do if [ -e "${target_root}/${file}" ];then fileList+="$file " fi done if [ -n "${fileList}" ];then if [ ! -d ${back_root} ];then mkdir -p ${back_root} if [ "$?" != 0 ];then echo "mkdir backup dir ${back_root} fail" exit 1 fi fi tar czf ${back_root}/${2}.bak.tar.gz $1 if [ "$?" == 0 ];then echo "backup file ${back_root}/${2}.bak.tar.gz!" fi fi } function argsCheck(){ if [ -z $2 ] || [ -z $3 ] || [ -z $4 ] || [ -z $5 ] || [ -z $6 ] || [ -z $7 ];then echo "useage ${0} send|roll TARGET_IP TARGET_PORT SOURCE_ROOT TARGET_ROOT BACKUP_ROOT VER" exit 1 fi if [ ! -d $4 ];then echo "SOURCE_ROOT ${SOURCE_ROOT} is not exsists!" exit 2 fi } case $1 in "send") argsCheck "$@" send $8 $7 #文件列表版本号 ;; "backup") backup $2 $3 $4 $5 ;; "roll") argsCheck "$@" roll $7 #版本号 ;; *) echo "useage ${0} send|roll TARGET_IP TARGET_PORT SOURCE_ROOT TARGET_ROOT BACKUP_ROOT