自动化部署一:自动化部署基础与实现

一:关于自动化的基础知识:

1.1:当前代码部署的实现方式:

#当前代码部署的实现方式:
运维纯手工scpweb服务器
纯手工登录git服务器执行git pullsvn服务器执行svn update更新代码
通过xftp上传代码
开发打压缩包上传到服务器然后解压

#缺点:
1.需要运维全程参与,占用大量的工作时间
2.上线时间比较慢
3.人为造成的失误较多,管理比较混乱
4.回滚复杂而且慢,还不及时

1.2:运行环境规划:

开发环境:开发者本地有自己的环境,然后运维需要设置开发环境的公用服务,例如开发数据库、redismemcached
测试环境:功能测试环境和性能测试环境
预生成环境:由生产环境集群中的某一个节点担任测试,此节点只做测试不对外提供服务
生产环境:直接对外提供服务的环境

1.2.1:为什么有预生成环境?

可能是生成环境预测试环境的数据库或数据库版本不一样导致语句出现问题
或者是生成环境调用的接口不一样,例如支付接口在测试环境无法调用

1.3:设计一套生成环境的代码自动化部署系统:

1.4:总体规划流程:

一个服务的集群节点数量,是一次部署还是分次部署

一键回滚到上个版本

一键回滚到任意版本

代码保存在SVN还是Git

获取指定分支或master的指定版本号的代码,svn指定版本号,git指定tag标签,或直接拉取某个分支

配置文件差异化,即测试环境和生产环境的配置文件不一样,如IP不一样或主机名不一样或数据库连接不一样等等

代码仓库和实际的差异,即配置文件是否放在代码仓库中,如果保存在git则所有人都可以从配置文件看到数据库用户密码等信息,可以使用单独分支保存配置文件,或配置文件只在部署服务器的某个项目的目录,比如是config.example

如何更新代码,java tomcat需要重启

测试部署后的web页面是否可以正常访问是否是想要的页面

并行(saltstack)或并行(shell)的问题,涉及到分组部署重启服务

如何执行,shell执行还是web执行

1.5:总体规划图如下:

二:实现代码自动化部署

2.1:通过shell脚本实现,后续会写一个python版的,shell脚本规划如下:

2.1.1:各web服务器添加一个uid相同的普通用户,而且所有的web服务都以此普通用户启动,默认情况下所有的wenb服务除了负载均衡之外都不能监听80端口,比如可以监听8008端口

2.1.2:部署服务器的用户登录其他服务器免密码登录,因此需要做秘钥认证,在各主机执行以下命令:

# useradd www -u 1010

# su – www

$ ssh-keygen 

#将部署机www用户的公钥复制到各web服务器的 /home/www/.ssh/authorized_keys或执行ssh-copy-id  www@192.168.10.102

$ chmod 600 /home/www/.ssh/authorized_keys

2.1.3:测试部署服务器可以免秘钥用www用户登录各个web服务器

2.2:编写shell脚本:

2.2.1:#完成第一节点,主题框架完成:

 

#!/bin/bash
#Author: Zhangjie

#脚本位置等变量
SHELL_NAME="deploy.sh"  #脚本名称
SHELL_DIR="/home/www" #脚本路径
SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.LOG" #脚本执行日志
LOCK_FILE="/tmp/deploy.lock"

#代码变量
CODE_DIR="/deploy/code/deploy" #代码目录
CONFIG_DIR="/deploy/config" #配置文件目录
TMP_DIR="/deploy/tmp" #临时目录
TAR_DIR="/deploy/tar" #打包目录


usage(){ #使用帮助函数
	echo $"$0使用帮助:$0 + [deploy | rollback]"
}

code_get(){
	echo "code_get"
}

code_build(){
	echo code_build
}

code_config(){
	echo conde_config
}

code_tar(){
	echo conde_tar
}

code_scp(){
	echo code_scp
}

cluster_node_remove(){
	echo cluster_node_remove
}

code_deploy(){
	echo code_deploy
}	

config_diff(){
	echo config_diff
}

code_test(){
	echo code_test
}

cluster_node_in(){
	echo cluster_node_in
}

rollback(){
	echo rollback
}

touch_key_file(){
    touch /tmp/deploy.lock
    echo "keyfile"
}

delete_key_file(){
 	rm -rf /tmp/deploy.lock
}

main(){ #主函
  if [ -f $LOCK_FILE ];then #先判断锁文件在不在
    echo "锁文件已存在,请稍后执行,退出..." && exit 10 #如果有锁文件直接退出
  fi
  DEPLOY_METHOD=$1 #避免出错误将脚本的第一个参数作为变量
  case $DEPLOY_METHOD in #判断第一个参数
    deploy) #如果第一个参数是deploy就执行以下操作
	touch_key_file #执行部署之前创建锁。如果同时有其他人执行则提示锁文件存在
	code_get;
	code_build;
	code_config;
	code_tar;
	code_scp;
	cluster_node_remove;
	code_deploy;
	config_diff;
	code_test;
	cluster_node_in;
	delete_key_file #执行完成后删除锁文件
	;;
    rollback) #如果第一个参数是rollback就执行以下操作
	touch_key_file #回滚之前也是先创建锁文件
	rollback;
	delete_key_file #执行完成删除锁文件
	;;

    *) #其他输入执行以下操作
	usage;
    esac	
}

main $1 #执行主函数并把第一个变量当参数

2.2.2:完成脚本第二阶段,实现代码部署:

 

#!/bin/bash
#Author: Zhangjie

#服务器节点:
# for i in `seq 1 20`;do echo 192.168.10.$i;done #web服务器,多主机多用for循环
NODE_LIST="192.168.10.101 192.168.10.102" #自定义的web服务器列表

#日志日期和时间变量
LOG_CDATE=`date "+%Y-%m-%d"` #如果执行的话后面执行的时间,此时间是不固定的,这是记录日志使用的时间
LOG_CTIME=`date  "+%H-%M-%S"`

#代码打包时间变量
CDATE=$(date "+%Y-%m-%d") #脚本一旦执行就会取一个固定时间赋值给变量,此时间是固定的
CTIME=$(date "+%H-%M-%S")

#脚本位置等变量
SHELL_NAME="deploy.sh"  #脚本名称
SHELL_DIR="/home/www" #脚本路径
SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.LOG" #脚本执行日志文件路径
LOCK_FILE="/tmp/deploy.lock" #锁文件路径

#代码变量
PRO_NAME="web-demo" #项目名称的函数
CODE_DIR="/deploy/code/web-demo" #从版本管理系统更新的代码目录
CONFIG_DIR="/deploy/config/web-demo" #保存不同项目的配置文件,一个目录里面就是一个项目的一个配置文件或多个配置文件
TMP_DIR="/deploy/tmp" #临时目录
TAR_DIR="/deploy/tar" #打包目录


usage(){ #使用帮助函数
	echo $"$0使用帮助:$0 + [deploy | rollback]"
}

writelog(){ #写入日志的函数
	LOGINFO=$1 #将参数作为日志输入
	echo "${CDATA} ${CTIME}: ${SHELL_NAME}:${LOGINFO}" >> ${SHELL_LOG}
}

code_get(){ #获取代码的函数
	writelog "code_get"
	cd ${CODE_DIR} && echo " git pull"  #进入到代码目录更新代码,此处必须免密码更新,此目录仅用于代码更新不能放其他任何文件
	/bin/cp -rf ${CODE_DIR} ${TMP_DIR}/ #临时保存代码并重命名,包名为时间+版本号,准备复制到web服务器
	API_VER="123456789"
}

code_build(){ #代码构建函数
	echo code_build
}

code_config(){ #配置文件函数
	writelog "conde_config"
	/bin/cp -rf ${CONFIG_DIR}/base/*   ${TMP_DIR}/${PRO_NAME} #将配置文件放在本机保存配置文件的临时目录,用于暂时保存代码项目
	PKG_NAME="${PRO_NAME}"_"${API_VER}"_"${CDATE}-${CTIME}" #定义代码目录名称
	cd ${TMP_DIR} && mv ${PRO_NAME} ${PKG_NAME} #重命名代码文件为web-demo_123456-20160505-21-20-10格式
}

code_tar(){ #对代码打包函数
	writelog "conde_tar"
	cd ${TMP_DIR} && tar czvf ${PKG_NAME}.tar.gz ${PKG_NAME} #将目录打包成压缩文件,便于网络传输
	writelog "${PKG_NAME}.tar.gz 打包成功" #记录打包成功的日志
}

code_scp(){ #代码压缩包scp函数
	writelog  "code_scp"
	for node in ${NODE_LIST};do #循环服务器节点列表
		scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot #将压缩后的代码包复制到web服务器的/opt/webroot
	done
}

cluster_node_remove(){ #将web服务器从代理移除函数
	writelog "cluster_node_remove"
}

code_deploy(){ #代码解压部署函数
	writelog "code_deploy"
	for node in ${NODE_LIST};do #循环服务器节点列表
		ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分别到web服务器执行压缩包解压命令
		ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" #整个自动化的核心,创建软连接
        done
}	

config_diff(){ #拷贝差异文件函数
	writelog "config_diff"
	scp ${CONFIG_DIR}/other/192.168.10.102.server.xml 192.168.10.102:/webroot/web-demo/server.xml  #将差异项目的配置文件scp到此web服务器并以项目结尾
}

code_test(){
	echo code_test
}

cluster_node_in(){
	echo cluster_node_in
}

rollback(){
	echo rollback
}

touch_key_file(){
    touch /tmp/deploy.lock
    echo "keyfile"
}

delete_key_file(){
 	rm -rf /tmp/deploy.lock
}

main(){ #主函
  if [ -f $LOCK_FILE ];then #先判断锁文件在不在
    echo "锁文件已存在,请稍后执行,退出..." && exit 10 #如果有锁文件直接退出
  fi
  DEPLOY_METHOD=$1 #避免出错误将脚本的第一个参数作为变量
  case $DEPLOY_METHOD in #判断第一个参数
    deploy) #如果第一个参数是deploy就执行以下操作
	touch_key_file #执行部署之前创建锁。如果同时有其他人执行则提示锁文件存在
	code_get;
	code_build;
	code_config;
	code_tar;
	code_scp;
	cluster_node_remove;
	code_deploy;
	config_diff;
	code_test;
	cluster_node_in;
	delete_key_file #执行完成后删除锁文件
	;;
    rollback) #如果第一个参数是rollback就执行以下操作
	touch_key_file #回滚之前也是先创建锁文件
	rollback;
	delete_key_file #执行完成删除锁文件
	;;

    *) #其他输入执行以下操作
	usage;
    esac	
}

main $1 #执行主函数并把第一个变量当参数

#在此阶段的脚本执行结果:

#可以将代码发布至后端服务器,下一阶段将完成代码回滚的功能

2.2.3:更改web服务的主目录为代码发布的目录:

#本次使用nginx,使用apache也是要更改一下web目录即可:

user  www www; #以www用户和组启动nginx
worker_processes  2;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       8899; #避免与原有的端口冲突特改为8899,访问的时候也要访问8899
        server_name  localhost;
        location / {
            root   /webroot/web-demo; #将web目录改为shell脚本部署的代码目录
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

2.2.4:启动nginx并访问两台web服务器测试:

# /usr/local/nginx-1.8.1/sbin/nginx

2.2.5:更新代码并提交再部署,然后查看web页面信息:

$ echo “hehe  test2” > /deploy/code/web-demo/index.html

$ ./code_deploy.sh  deploy

2.2.6:完成脚本第三阶段,实现测试等功能:

#!/bin/bash
#Author: Zhangjie

#服务器节点:
# for i in `seq 1 20`;do echo 192.168.10.$i;done #主机多用for循环
NODE_LIST="192.168.10.101 192.168.10.102"
PRE_LIST="192.168.10.101"
GROUP1_LIST="192.168.10.102"

#日志日期和时间变量
LOG_CDATE=`date "+%Y-%m-%d"` #如果执行的话后面执行的时间,此时间是不固定的,这是记录日志使用的时间
LOG_CTIME=`date  "+%H-%M-%S"`

#代码打包时间变量
CDATE=$(date "+%Y-%m-%d") #脚本一旦执行就会取一个固定时间赋值给变量,此时间是固定的
CTIME=$(date "+%H-%M-%S")

#脚本位置等变量
SHELL_NAME="deploy.sh"  #脚本名称
SHELL_DIR="/home/www" #脚本路径
SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.LOG" #脚本执行日志文件路径
LOCK_FILE="/tmp/deploy.lock" #锁文件路径

#代码变量
PRO_NAME="web-demo" #项目名称的函数
CODE_DIR="/deploy/code/web-demo" #从版本管理系统更新的代码目录
CONFIG_DIR="/deploy/config/web-demo" #保存不同项目的配置文件,一个目录里面就是一个项目的一个配置文件或多个配置文件
TMP_DIR="/deploy/tmp" #临时目录
TAR_DIR="/deploy/tar" #打包目录


usage(){ #使用帮助函数
	echo $"$0使用帮助:$0 + [deploy | rollback]"
}

writelog(){ #写入日志的函数
	LOGINFO=$1 #将参数作为日志输入
	echo "${CDATA} ${CTIME}: ${SHELL_NAME}:${LOGINFO}" >> ${SHELL_LOG}
}

code_get(){ #获取代码的函数
	writelog "code_get"
	cd ${CODE_DIR} && echo " git pull"  #进入到代码目录更新代码,此处必须免密码更新,此目录仅用于代码更新不能放其他任何文件
	/bin/cp -rf ${CODE_DIR} ${TMP_DIR}/ #临时保存代码并重命名,包名为时间+版本号,准备复制到web服务器
	API_VER="123456789"
}

code_build(){ #代码构建函数
	echo code_build
}

code_config(){ #配置文件函数
	writelog "conde_config"
	/bin/cp -rf ${CONFIG_DIR}/base/*   ${TMP_DIR}/${PRO_NAME} #将配置文件放在本机保存配置文件的临时目录,用于暂时保存代码项目
	PKG_NAME="${PRO_NAME}"_"${API_VER}"_"${CDATE}-${CTIME}" #定义代码目录名称
	cd ${TMP_DIR} && mv ${PRO_NAME} ${PKG_NAME} #重命名代码文件为web-demo_123456-20160505-21-20-10格式
}

code_tar(){ #对代码打包函数
	writelog "conde_tar"
	cd ${TMP_DIR} && tar czvf ${PKG_NAME}.tar.gz ${PKG_NAME} #将目录打包成压缩文件,便于网络传输
	writelog "${PKG_NAME}.tar.gz 打包成功" #记录打包成功的日志
}

code_scp(){ #代码压缩包scp函数
	writelog  "code_scp"
	for node in ${PRE_LIST};do #循环服务器节点列表
		scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot #将压缩后的代码包复制到web服务器的/opt/webroot
	done
	for node in ${GROUP1_LIST};do #循环服务器节点列表
		scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot #将压缩后的代码包复制到web服务器的/opt/webroot
	done
}

cluster_node_remove(){ #将web服务器从代理移除函数
	writelog "cluster_node_remove"
}

pre_deploy(){ #代码解压部署函数,预生产节点
	writelog "code_deploy"
	for node in ${PRE_LIST};do #循环服务器节点列表
		cluster_node_remove  ${node} #部署之前将节点从前端负载删除
		ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分别到web服务器执行压缩包解压命令
		ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" #整个自动化的核心,创建软连接
        done
}	

pre_test(){ #预生产主机测试函数
        for node in ${PRE_LIST};do #循环预生产主机列表
            curl -s --head http://${node}:8899/index.html | grep "200 OK" #测试web界面访问
        if [ $? -eq 0 ];then  #如果访问成功
            writelog " ${node} Web Test OK!" #记录日志
            echo " ${node} Web Test OK!"
	    cluster_node_add ${node} #测试成功之后调用添加函数把服务器添加至节点,
            writelog " ${node} add to cluster OK!" #记录添加服务器到集群的日志
        else #如果访问失败
            writelog "${node} test no OK" #记录日志
            echo "${node} test no OK"
            delete_key_file #调用删除锁文件函数
            break #结束部署
        fi
        done

}

group1_deploy(){ #代码解压部署函数
	writelog "code_deploy"
	for node in ${GROUP1_LIST};do #循环服务器节点列表
		cluster_node_remove $node  
		ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分别到web服务器执行压缩包解压命令
		ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" #整个自动化的核心,创建软连接
        done
	scp ${CONFIG_DIR}/other/192.168.10.102.server.xml 192.168.10.102:/webroot/web-demo/server.xml  #将差异项目的配置文件scp到此web服务器并以项目结尾}	

group1_test(){#预生产主机测试函数for node in ${PRE_LIST};do#循环预生产主机列表
            curl -s --head http://${node}:8899/index.html | grep "200 OK"#测试web界面访问if[ $?-eq 0];then#如果访问成功
            writelog " ${node} Web Test OK!"#记录日志
            echo " ${node} Web Test OK!"
	    cluster_node_add
            writelog " ${node} add to cluster OK!"#记录将服务器 添加至集群的日志else#如果访问失败
            writelog "${node} test no OK"#记录日志
            echo "${node} test no OK"
	    delete_key_file #调用删除锁文件函数break#结束部署fidone}

config_diff(){#拷贝差异文件函数
	writelog "config_diff"for node in ${NODE_LIST};do#循环主机列表
	    curl -s --head http://${node}:8899/index.html | grep "200 OK"#测试web界面访问if[ $?-eq 0];then#如果访问成功
	    writelog " ${node} Web Test OK!"#记录日志
	    echo " ${node} Web Test OK!"else#如果访问失败
	    writelog "${node} test no OK"#记录日志
	    echo "${node} test no OK"break#结束部署fidone}

code_test(){
	echo code_test
}
cluster_node_remove(){#将web服务器添加至前端负载
	writelog $1 #记录服务器从前端负载删除的日志}

cluster_node_add(){#将web服务器添加至前端负载
	echo cluster_node_add
}

rollback(){
	echo rollback
}

touch_key_file(){
    touch /tmp/deploy.lock
    echo "keyfile"}

delete_key_file(){
 	rm -rf /tmp/deploy.lock
}

main(){#主函if[-f $LOCK_FILE ];then#先判断锁文件在不在
    echo "锁文件已存在,请稍后执行,退出..."&& exit 10#如果有锁文件直接退出fi
  DEPLOY_METHOD=$1 #避免出错误将脚本的第一个参数作为变量case $DEPLOY_METHOD in#判断第一个参数
    deploy)#如果第一个参数是deploy就执行以下操作
	touch_key_file #执行部署之前创建锁。如果同时有其他人执行则提示锁文件存在
	code_get;#获取代码
	code_build;#如果要编译执行编译函数
	code_config;#cp配置文件
	code_tar;#打包
	code_scp;#scp到服务器
	cluster_node_remove;
	pre_deploy;#预生产环境部署
	pre_test;#预生产环境测试
	group1_deploy;#生产环境部署
	group1_test;#生产环境测试
	config_diff;#cp差异文件
	code_test;#代码测试
	delete_key_file #执行完成后删除锁文件;;
    rollback)#如果第一个参数是rollback就执行以下操作
	touch_key_file #回滚之前也是先创建锁文件
	rollback;
	delete_key_file #执行完成删除锁文件;;*)#其他输入执行以下操作
	usage;#调用使用方法函数esac}

main $1 #执行主函数并把第一个变量当参数

#执行过程:

2.3:代码回滚设计:

2.3.1:正常回滚是回滚已经在web服务器部署过的版本,因此就不需要获取代码打包和部署的过程了

列出回滚版本

将模板服务器移除集群

执行回滚

重启和测试

将模板服务器假如集群

2.3.2::实现任意版本回滚:

 

#!/bin/bash
#Author: Zhangjie

#服务器节点:
# for i in `seq 1 20`;do echo 192.168.10.$i;done #主机多用for循环
NODE_LIST="192.168.10.101 192.168.10.102"
PRE_LIST="192.168.10.101"
GROUP1_LIST="192.168.10.102"

#日志日期和时间变量
LOG_CDATE=`date "+%Y-%m-%d"` #如果执行的话后面执行的时间,此时间是不固定的,这是记录日志使用的时间
LOG_CTIME=`date  "+%H-%M-%S"`

#代码打包时间变量
CDATE=$(date "+%Y-%m-%d") #脚本一旦执行就会取一个固定时间赋值给变量,此时间是固定的
CTIME=$(date "+%H-%M-%S")

#脚本位置等变量
SHELL_NAME="deploy.sh"  #脚本名称
SHELL_DIR="/home/www" #脚本路径
SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.LOG" #脚本执行日志文件路径
LOCK_FILE="/tmp/deploy.lock" #锁文件路径

#代码变量
PRO_NAME="web-demo" #项目名称的函数
CODE_DIR="/deploy/code/web-demo" #从版本管理系统更新的代码目录
CONFIG_DIR="/deploy/config/web-demo" #保存不同项目的配置文件,一个目录里面就是一个项目的一个配置文件或多个配置文件
TMP_DIR="/deploy/tmp" #临时目录
TAR_DIR="/deploy/tar" #打包目录

#回滚服务器列表
ROLLBACK_LIST="192.168.10.101 192.168.10.102"

usage(){ #使用帮助函数
	echo $"$0使用帮助:$0 deploy or rollback + code version"
}

writelog(){ #写入日志的函数
	LOGINFO=$1 #将参数作为日志输入
	echo "${CDATA} ${CTIME}: ${SHELL_NAME}:${LOGINFO}" >> ${SHELL_LOG}
}

code_get(){ #获取代码的函数
	writelog "code_get"
	cd ${CODE_DIR} && echo " git pull"  #进入到代码目录更新代码,此处必须免密码更新,此目录仅用于代码更新不能放其他任何文件
	/bin/cp -rf ${CODE_DIR} ${TMP_DIR}/ #临时保存代码并重命名,包名为时间+版本号,准备复制到web服务器
	API_VER="123456789"
}

code_build(){ #代码构建函数
	echo code_build
}

code_config(){ #配置文件函数
	writelog "conde_config"
	/bin/cp -rf ${CONFIG_DIR}/base/*   ${TMP_DIR}/${PRO_NAME} #将配置文件放在本机保存配置文件的临时目录,用于暂时保存代码项目
	PKG_NAME="${PRO_NAME}"_"${API_VER}"_"${CDATE}-${CTIME}" #定义代码目录名称
	cd ${TMP_DIR} && mv ${PRO_NAME} ${PKG_NAME} #重命名代码文件为web-demo_123456-20160505-21-20-10格式
}

code_tar(){ #对代码打包函数
	writelog "conde_tar"
	cd ${TMP_DIR} && tar czvf ${PKG_NAME}.tar.gz ${PKG_NAME} #将目录打包成压缩文件,便于网络传输
	writelog "${PKG_NAME}.tar.gz 打包成功" #记录打包成功的日志
}

code_scp(){ #代码压缩包scp函数
	writelog  "code_scp"
	for node in ${PRE_LIST};do #循环服务器节点列表
		scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot #将压缩后的代码包复制到web服务器的/opt/webroot
	done
	for node in ${GROUP1_LIST};do #循环服务器节点列表
		scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot #将压缩后的代码包复制到web服务器的/opt/webroot
	done
}

cluster_node_remove(){ #将web服务器从代理移除函数
	writelog "cluster_node_remove"
}

pre_deploy(){ #代码解压部署函数,预生产节点
	writelog "code_deploy"
	for node in ${PRE_LIST};do #循环服务器节点列表
		cluster_node_remove  ${node} #部署之前将节点从前端负载删除
		echo  "pre_deploy, cluster_node_remove ${node}"
		ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分别到web服务器执行压缩包解压命令
		ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" #整个自动化的核心,创建软连接
        done
}	

pre_test(){ #预生产主机测试函数
        for node in ${PRE_LIST};do #循环预生产主机列表
            curl -s --head http://${node}:8899/index.html | grep "200 OK" #测试web界面访问
        if [ $? -eq 0 ];then  #如果访问成功
            writelog " ${node} Web Test OK!" #记录日志
            echo " ${node} Web Test OK!"
	    cluster_node_add ${node} #测试成功之后调用添加函数把服务器添加至节点,
            writelog "pre,${node} add to cluster OK!" #记录添加服务器到集群的日志
        else #如果访问失败
            writelog "${node} test no OK" #记录日志
            echo "${node} test no OK"
            delete_key_file #调用删除锁文件函数
            break #结束部署
        fi
        done

}

group1_deploy(){ #代码解压部署函数
	writelog "code_deploy"
	for node in ${GROUP1_LIST};do #循环服务器节点列表
		cluster_node_remove $node  
		echo "group1, cluster_node_remove $node"
		ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分别到web服务器执行压缩包解压命令
		ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" #整个自动化的核心,创建软连接
        done
	scp ${CONFIG_DIR}/other/192.168.10.102.server.xml 192.168.10.102:/webroot/web-demo/server.xml  #将差异项目的配置文件scp到此web服务器并以项目结尾}	

group1_test(){#预生产主机测试函数for node in ${PRE_LIST};do#循环预生产主机列表
            curl -s --head http://${node}:8899/index.html | grep "200 OK"#测试web界面访问if[ $?-eq 0];then#如果访问成功
            writelog " ${node} Web Test OK!"#记录日志
            echo "group1_test,${node} Web Test OK!"
	    cluster_node_add
            writelog " ${node} add to cluster OK!"#记录将服务器 添加至集群的日志else#如果访问失败
            writelog "${node} test no OK"#记录日志
            echo "${node} test no OK"
	    delete_key_file #调用删除锁文件函数break#结束部署fidone}

config_diff(){#拷贝差异文件函数
	writelog "config_diff"for node in ${NODE_LIST};do#循环主机列表
	    curl -s --head http://${node}:8899/index.html | grep "200 OK"#测试web界面访问if[ $?-eq 0];then#如果访问成功
	    writelog " ${node} Web Test OK!"#记录日志
	    echo " ${node} Web Test OK!"else#如果访问失败
	    writelog "${node} test no OK"#记录日志
	    echo "${node} test no OK"break#结束部署fidone}

code_test(){
	echo code_test
}
cluster_node_remove(){#将web服务器添加至前端负载
	writelog $1 #记录服务器从前端负载删除的日志}

cluster_node_add(){#将web服务器添加至前端负载
	echo cluster_node_add
}


touch_key_file(){
    touch /tmp/deploy.lock
    echo "keyfile"}

delete_key_file(){
 	rm -rf /tmp/deploy.lock
}

rollback(){#代码回滚主函数if[-z $1 ];then
	delete_key_file #删除锁文件
	echo "请输入参数"&& exit 3;fi#echo ${ROLLBACK_VER},123case $1 in#把第二个参数做当自己的第一个参数 
	list)
	    ls -l /opt/webroot/*.tar.gz
	    ;;*)
	    rollback_funk $1;esac}

rollback_funk(){for node in ${ROLLBACK_LIST};do#循环服务器节点列表
	    ssh ${node}"rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo"#立即回滚到指定的版本,$1即指定的版本参数
	    echo "${node} rollback success!"done}

main(){#主函if[-f $LOCK_FILE ];then#先判断锁文件在不在
    echo "锁文件已存在,请稍后执行,退出..."&& exit 10#如果有锁文件直接退出fi
  DEPLOY_METHOD=$1 #避免出错误将脚本的第一个参数作为变量
  ROLLBACK_VER=$2
  case $DEPLOY_METHOD in#判断第一个参数
    deploy)#如果第一个参数是deploy就执行以下操作
	touch_key_file #执行部署之前创建锁。如果同时有其他人执行则提示锁文件存在
	code_get;#获取代码
	code_build;#如果要编译执行编译函数
	code_config;#cp配置文件
	code_tar;#打包
	code_scp;#scp到服务器
	cluster_node_remove;
	pre_deploy;#预生产环境部署
	pre_test;#预生产环境测试
	group1_deploy;#生产环境部署
	group1_test;#生产环境测试
	config_diff;#cp差异文件
	code_test;#代码测试
	delete_key_file #执行完成后删除锁文件;;
    rollback)#如果第一个参数是rollback就执行以下操作#touch_key_file #回滚之前也是先创建锁文件
	rollback $ROLLBACK_VER;#delete_key_file #执行完成删除锁文件;;*)#其他输入执行以下操作
	usage;#调用使用方法函数esac}

main $1 $2 #执行主函数并把第一个变量当参数

执行结果:

2.3.2:紧急回滚,直接获取上一个版本,可以每次部署将版本放在一个文件,紧急回滚的时候读取此文件的版本进行代码版本回滚

紧急回滚只能回滚到上一个版本,设计思路为每次部署代码就将名称保存在一个文件里面,下次部署时将版本号进行追加,因此每次回滚都是直接取到的文件当中保存的倒数第二个版本名称,然后将之前的连接删除再重新链接到此版本的名称即可

转自http://blogs.studylinux.net/?p=2005

原文地址:https://www.cnblogs.com/zcyy/p/15599051.html