一:关于自动化的基础知识:
1.1:当前代码部署的实现方式:
#当前代码部署的实现方式:
运维纯手工scp到web服务器
纯手工登录git服务器执行git pull或svn服务器执行svn update更新代码
通过xftp上传代码
开发打压缩包上传到服务器然后解压
#缺点:
1.需要运维全程参与,占用大量的工作时间
2.上线时间比较慢
3.人为造成的失误较多,管理比较混乱
4.回滚复杂而且慢,还不及时
1.2:运行环境规划:
开发环境:开发者本地有自己的环境,然后运维需要设置开发环境的公用服务,例如开发数据库、redis、memcached等
测试环境:功能测试环境和性能测试环境
预生成环境:由生产环境集群中的某一个节点担任测试,此节点只做测试不对外提供服务
生产环境:直接对外提供服务的环境
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函数