一、背景
由于公司服务器都是阿里云服务器,会涉及到项目的频繁更新,于是就有了服务更新的一整套流程。整体思路是:从测试环境拉取代码,程序灰度更新,代码从灰度拉到生产,线上服务重启。
二、从测试环境拉取代码
2.1 执行时shell
1. 到测试环境机器给待更新的服务打tar包
2. 把打好的tar包传给执行这个项目的机器,/target目录是软链接,链接到一个共享nas盘,其他机器挂载这个盘就可以实现数据共享
3. 删除测试环境的tar包
#!/bin/sh
ip="47.XXX.209.34"
projectName=${project//_/-}
path=/programs/$project
echo path:$path
echo projectName:$projectName
echo "ssh -p 3721 root@${ip} \"cd $path/target/ ; ls -t; tar -zcf $projectName-new.tar.gz $projectName\""
echo "scp -P 3721 root@$ip:$path/target/$projectName-new.tar.gz /target"
echo "ssh -p 3721 root@${ip} \"rm -f $path/target/$projectName-new.tar.gz\""
ssh -p 3721 root@${ip} "cd $path/target/ ; ls -t; tar -zcf $projectName-new.tar.gz $projectName"
scp -P 3721 root@$ip:$path/target/$projectName-new.tar.gz /target
ssh -p 3721 root@${ip} "rm -f $path/target/$projectName-new.tar.gz"
echo "scp ok"
三、程序灰度更新
3.1 项目配置
1. 项目名称
2.更新或只回到生产
3.是否更新xml
4.项目运行的节点,221为灰度机器,挂载了26的共享nas盘
3.2 执行时shell
1. 通过字符串截取得到容器名称,并停止这个容器,删除原项目的目录【本质是项目编译完的target下的项目目录】
2. 从/env-b拉取生产代码,灰度机器221共享了生产B环境代码nas盘
3.进入更新目录,删除WEB-INF文件夹,解压从测试环境拉过来的压缩包
4.更新灰度环境项目目录,根据条件判断是否需要更新xml【properties里面配置的是生产数据库配置,一般不需要变更】
5.启动更新项目的docker容器
#!/bin/sh
projectVals=(${projectVal//,/ })
project=${projectVals[1]}
echo `date "+%Y-%m-%d %H:%M:%S"`
echo $project
scProjectDir="/env-b/$project"
projectDir="/opt/app/$project"
updateDir="/opt/update/$project"
containerName=${projectVals[0]}
echo "docker stop $containerName"
docker stop $containerName
echo "删除原项目$projectDir"
rm -rf $projectDir
echo "拉取生产代码$scProjectDir"
cp -r $scProjectDir $projectDir
if [ "true" = "$isFlush" ] ; then
echo "开始更新代码"
cd /opt/update
rm -rf $project/WEB-INF
tar -zxf /plk/target/$project-new.tar.gz
cd ${projectDir}
rm -rf WEB-INF/lib WEB-INF/classes/com
cp -r ${updateDir}/WEB-INF/lib WEB-INF
cp -r ${updateDir}/WEB-INF/classes/com WEB-INF/classes
if [ "yes" = "$xml" ] ; then
echo "rm -rf $project/WEB-INF/classes/*.xml"
echo "cp -r $updateDir/WEB-INF/classes/*.xml $project/WEB-INF/classes/"
rm -rf WEB-INF/classes/*.xml
cp -r $updateDir/WEB-INF/classes/*.xml WEB-INF/classes/
fi
fi
echo "docker start $containerName"
docker start $containerName
sleep 35s
tail -n 400 /docker/tomcat/logs/$containerName/catalina.out
四、拉取代码到生产
4.1 项目配置
1.项目名称
2.项目运行节点,10.8.0.159这台机器是监控看板
4.2 执行时shell
1. 根据传入的参数备份线上项目代码,159共享了有生产B环境代码的nas盘
2.删除线上代码的lib和com文件夹,从灰度环境拷贝class目录到生产环境
#!/bin/sh
today=`date "+%Y%m%d-%H%M"`
bakDir="/bak"
projectDir="/opt/apps/$project"
echo "bakDir=$bakDir"
echo "projectDir=$projectDir"
echo "today=$today"
cd /opt/apps
echo "backup and update"
echo "tar -zcf $bakDir/$project-$today.tar.gz $project"
tar zcf $bakDir/$project-$today.tar.gz $project
echo "rm -rf $project/WEB-INF/lib $project/WEB-INF/classes/com"
rm -rf $project/WEB-INF/lib $project/WEB-INF/classes/com
echo "scp -r [email protected]:/opt/app/$project/WEB-INF/lib $project/WEB-INF/lib"
scp -r [email protected]:/opt/app/$project/WEB-INF/lib $project/WEB-INF/lib
echo "scp -r [email protected]:/opt/app/$project/WEB-INF/classes/* $project/WEB-INF/classes"
scp -r [email protected]:/opt/app/$project/WEB-INF/classes/* $project/WEB-INF/classes/
五、线上服务重启
5.1 项目参数
1.项目名称
2.项目运行节点,10.8.0.159这台机器是监控看板
5.2 执行时shell
1. 获取项目名称,服务所在ECS的ip地址,提供者暴露的dubboPort【如果一个服务默认的端口为0,说明他仅仅是一个消费者】。
2.传入stop通过dubboadmin.py把提供者在zookeeper上置为空
3.远程到需要更新应用的ECS上,传入tomcatname和b作为panme 【ppset.sh脚本会把PP_OPTS参数暴露出来,tomcat启动的时候JVM会加载这些参数】
4.如果是第一台更新的机器,输出日志【因为只要一台更新成功了,后面的机器就不会报错了】
5.给服务启动预留休息时间,启动完之后启动下一台机器的服务【每台业务服务器都挂载了有生产代码的nas盘】
#!/bin/sh
today=`date "+%Y%m%d-%H%M"`
projectVals=(${projectVal//,/ })
tomcatName=${projectVals[0]}
hoststr=${projectVals[1]}
dubboPort=${projectVals[2]}
i=0
echo "restart"
hosts=${hoststr//;/ }
if [ "redpacket" = "$tomcatName" ] ; then
count=1
fi
for host in ${hosts[@]}
do
if (( $dubboPort > 0 )) ; then
echo "dubbo pause service"
echo "cd /monitor/python ; python dubboadmin.py 10.8.0.75 $host $dubboPort stop"
cd /monitor/python ; python dubboadmin.py 10.8.0.75 $host $dubboPort stop
sleep 2s
fi
echo "ssh root@$host \"/plk/ppset.sh $tomcatName b ; sh /opt/restart.sh $tomcatName\""
ssh root@$host "/plk/ppset.sh $tomcatName b ; sh /opt/restart.sh $tomcatName"
if (( i == 0 )) ; then
sleep 35s
ssh root@$host "tail -n 500 /opt/$tomcatName-tomcat/logs/catalina.out"
fi
(( i++ ))
echo "$host $tomcatName restart end sleep $sleepTime s"
sleep $sleepTime
done
if (( $dubboPort > 0 )) ; then
sleep 30
for host in ${hosts[@]}
do
echo "dubbo pause service"
echo "cd /monitor/python ; python dubboadmin.py 10.8.0.75 $host $dubboPort start"
cd /monitor/python ; python dubboadmin.py 10.8.0.75 $host $dubboPort start
done
fi
附:
1. ppset.sh脚本,PP_OPTS参数会在tomcat启动的时候加载到JVM里面,实现pinpoint的服务注册。
ipaddr=$(ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}'|head -1);ipaddr=${ipaddr:5}
pname=${1}-${2}
echo proj=${pname}
export PP_OPTS=" -javaagent:/opt/app/pinpoint-agent-1.8.2/pinpoint-bootstrap-1.8.2.jar -Dpinpoint.agentId=${pname}-${ipaddr} -Dpinpoint.applicationName=${pname} "
echo "$PP_OPTS"
2. dubboadmin.py 脚本
1.引入kazoo模块连接到生产zookeeper
2.通过getchildren方法获得"/dubbo/"+str(intf)+"/providers"子目录节点,把获取到的接口放到providerList列表
3.根据传入的参数判断zookeeper需要执行的操作
%3A,%3F,%3D这些是url编码,详情见ttps://grox.net/utils/encoding.html
#!/usr/bin/env python
from kazoo.client import KazooClient
import logging
import sys
logging.basicConfig()
zk = KazooClient(hosts=str(sys.argv[1])+":2181")
zk.start()
acl=sys.argv[4]
providerList=[]
server_addr=str(sys.argv[2])+"%3A"+str(sys.argv[3])
print server_addr
interfaces=zk.get_children("/dubbo")
for intf in interfaces:
providers=zk.get_children("/dubbo/"+str(intf)+"/providers")
for provider in providers:
if provider.find(server_addr) > -1:
providerList.append(str(provider))
for provider in providerList:
str1=provider.split("%3F",1)[0]
str2=str1.split("%2F")[3]
str1=str1.replace("dubbo","override")
str1=str1+"%3Fcategory%3Dconfigurators%26disabled%3Dtrue%26dynamic%3Dfalse%26enabled%3Dtrue"
str3="/dubbo/"+str2+"/configurators/"+str1
print str3
try:
if acl == "stop":
zk.ensure_path(str3) //创建str3空的node
else:
zk.delete(str3) //删除str3的子node
except Exception as e:
print ('error', e)
finally:
print ('ok')
zk.stop()