基於Gitlab+Jenkins的代碼自動化發布
主要涉及到兩個工具Gitlab,Jenkins,要完成自動化還需要rsync,qqbot,log,ant、shell腳本,python等。
Gitlab:我們主要用它來做代碼的倉庫
Jenkins:用來執行任務的持續集成,構建等。
一、大體的自動化思路:
開發人員push代碼到gitlab,觸發webhook,調用jenkins job。 jenkins job 執行拉取代碼,編譯,調用loadblance,下架部分服務器更新代碼,驗證更新後的可用性,上線;再下架另一部分服務器,更新代碼,再上線。 更新完後,將本次發布的狀態信息推送給項目組。
二、實際工作中,我們遇到的比以上要復雜。
服務器環境包括:測試環境,開發環境,預發布環境,生產環境等。 代碼倉庫又分為多個分支:master分支,開發分支,項目分支,本地分支等。
因此要完成整個過程自動化,還需要整合多分支,多環境的情況。
三、測試環境的自動化思路:
1.建一個dev分支用來專門發布測試環境,此分支只允許開發人員合並代碼和push,不能直接在上面改代碼。 2.開發人員開發一個功能,先在本地建一個本地分支,寫完後合並到dev,然後push到gitlab,gitlab觸發鉤子事件,調用jenkins完成項目的自動化部署。
以上2點看似已經實現了自動化發布,但實踐發現,開發人員仍需花不少時間在代碼的提交 ,切換分支,合並分支,push代碼等重復而繁瑣的工作上。於是這裏我做了對GIT操作的自動化,將提交、切換、合並、push整合整合起來成工具,後面會列出工具代碼。
四、預發布環境的自動化思路:
1.預發布的自動化采用和測試環境一樣的思路,只是dev分支換成了master分支。 2.master分支:我們用它來發布預發布和生產環境,對沒錯兩套環境用同一分支,此分支只有項目經理有權限push,普通開發沒權限操作。
以上2點看似也實現了代碼的自動化發布,但實際工作中項目經理同樣也要花不少時間在代碼的提交 ,切換分支,合並分支,push代碼等重復繁瑣的工作,因此這裏也要解決項目經理Git操作的自動化,後面列出工具。
五、生產環境的自動化發布:
生產環境的發布其實只是取消了webhook的自動觸發jenkins job,改為手動點擊發布,主要是為發布安全考慮。
六、實操:
1.設置webhook,對測試環境job和預發布環境job設置相應的鉤子事件:
2.在jenkins中配置git插件
3.配置jenkins job,這裏我用shell腳本做一系列的job,不需要像網上安裝各種繁瑣的插件。
#!/bin/bash
#本案例中WORKSPACE=/root/.jenkins/workspace/dev_test
#源目錄
src="$WORKSPACE/WebRoot/"
#發布的目標目錄
dest="/usr/local/apache-tomcat-6.0.39/webapps/xiangmu/"
#目標主機用戶
user="root"
#目標主機,測試機1、測試機2
host1="10.111.111.1"
host2="10.111.111.2"
# 需要發布的文件列表
cd $WORKSPACE
change_file_list=`git diff --name-only HEAD~ HEAD`
echo "需要發布的文件列表:$change_file_list"
#定義機器人發布消息功能函數
function qqbot_deploy(){
proj_name=`git show $commitid --pretty=format:"%s" |sed -n 1p`
author=`git show $commitid --pretty=format:"%an" |sed -n 1p`
now_time=`date "+%Y-%m-%d %H:%M:%S"`
qq send group IT群 "
QQ消息機器人:
【測試環境】 發布時間:$now_time 自動化發布完成
項目:$proj_name 狀態:$status $solution
發布的文件列表:$change_file_list
發布人:$author" &
}
# 定義執行命令成功或失敗日誌記錄功能函數
function log(){
if [ $? -eq 0 ];then
echo "執行‘$arg‘成功"
else
echo "執行‘$arg‘失敗"
status="發布失敗"
qqbot_deploy
exit 1
fi
}
# 定義java編譯功能函數
function ant_shell(){
#jdk需要基於1.7,ant低於1.9編譯
cd $WORKSPACE
arg="編譯"
#ant >/dev/null 2>&1
ant
log
# 一套代碼,定義配置文件中心,拷貝不同的配置文件到不同的環境。這裏拷貝測試環境配置文件到測試環境
\cp -rf conf/xiangmu/dev/system_dev.properties WebRoot/WEB-INF/classes/system.properties
\cp -rf conf/xiangmu/dev/ApplicationContext_dev.xml WebRoot/WEB-INF/classes/spring/ApplicationContext.xml
\cp -rf conf/xiangmu/dev//ApplicationContext-service_dev.xml WebRoot/WEB-INF/classes/spring/ApplicationContext-service.xml
}
# 定義java發布功能函數
function deploy_java(){
#1.編譯
ant_shell
#2.對e互助測試1的操作
#2.1修改測試機1的負載均衡的值為0
arg="修改負載均衡的值"
python ModifyLoadBalancerBackends_test1_value0.py
log
sleep 3
#2.2發布測試機1的代碼,rsync安靜模式同步
arg="發布$host1代碼"
rsync -e ‘ssh -o stricthostkeychecking=no -p22‘ -qapgolr --progress --delete $src $user@$host1:$dest
log
#2.3重啟測試機1的tomcat
ssh $host1 /home/tomcat/ver/restart_tomcat.sh&
sleep 18
status_code1=`curl -I -m 10 -o /dev/null -s -w %{http_code} http://$host1/planweb/index.do`
if [ $status_code1 -eq 200 ]
then
echo "http://$host1/planweb/index.do 打開正常"
else
echo "http://$host1/planweb/index.do 打開失敗"
#發布失敗通知消息到qq群
solution="原因和方案:可能tomcat啟動時間過長的誤報,延長sleep時間,重新發布一次"
status="發布失敗"
qqbot_deploy
exit 1
fi
#3.上線測試機1,並下線測試機2,修改測試機1的負載均衡值為10,修改測試機2的值為0
arg="修改負載均衡的值"
python ModifyLoadBalancerBackends_test1_value10.py
log
#4.對測試機2的操作
#4.1發布代碼到測試機2
arg="發布$host2代碼"
rsync -e ‘ssh -o stricthostkeychecking=no -p22‘ -aqpgolr --progress --delete $src $user@$host2:$dest
log
#4.2重啟測試機2的tomcat
ssh $host2 /home/tomcat/ver/restart_tomcat.sh&
sleep 22
status_code2=`curl -I -m 10 -o /dev/null -s -w %{http_code} http://$host2/planweb/index.do`
if [ $status_code2 -eq 200 ]
then
echo "http://test.xiangmu.com 打開正常"
#4.3上線測試機2,修改測試1的值為10,全部上線
arg="修改負載均衡的值"
python ModifyLoadBalancerBackends_test2_value10.py
log
echo "測試機$host1,$host2上線java代碼完成"
#動態文件,發布成功消息通知到qq群
status="發布成功"
qqbot_deploy
#退出循環
exit 0
else
echo "http://test.xiangmu.com打開失敗"
#發布失敗通知消息到qq群
status="發布失敗"
solution="原因和方案:可能tomcat啟動時間過長,重置clb,查看tomcat啟動log"
qqbot_deploy
exit 1
fi
}
# 定義靜態文件發布功能函數,無需重啟tomcat
function deploy_static(){
arg="發布$host1的靜態代碼"
rsync -e ‘ssh -o stricthostkeychecking=no -p22‘ -qapgolr --progress --delete $src $user@$host1:$dest
log
arg="發布$host2的靜態代碼"
rsync -e ‘ssh -o stricthostkeychecking=no -p22‘ -qapgolr --progress --delete $src $user@$host2:$dest
log
}
#異步執行:代碼質量分析。
function code_quality_analysis(){
if [ ! -f "sonar-project.properties" ];then
echo -e "sonar.projectKey=dev_test \nsonar.host.url=http://localhost:9000/sonar \nsonar.projectName=dev_test \nsonar.projectVersion=1.0 \nsonar.sources=src \nsonar.java.binaries=build/WEB-INF/classes" >sonar-project.properties
fi
BUILD_ID=
/usr/local/sonar-scanner/bin/sonar-scanner &
echo "開始異步運行代碼質量分析"
}
#定義代碼發布功能函數
function deploy(){
#標記靜態文件出現的次數,解決遇到靜態文件重復同步多次的問題
count=0
#遍歷發布的文件列表
for i in $change_file_list;
do
# 遇到有java等後綴需要編譯的文件,則編譯發布。
if [ "${i##*.}"x = "java"x ] || [ "${i##*.}"x = "xml"x ] || [ "${i##*.}"x = "properties"x ];then
#發布編譯的代碼
deploy_java
# 如果是前端靜態文件,則無需編譯直接發布。第一次遇到靜態發布一次,再遇到靜態則不發布。
elif [ ! "${i##*.}"x = "java"x ] || [ ! "${i##*.}"x = "xml"x ] || [ ! "${i##*.}"x = "properties"x ] && [ $count -eq 0 ];then
#發布靜態文件
deploy_static
#標記為1,記已同步一次代碼
count+=1
fi
done
#全靜態文件,發送消息通知到qq群
status="發布成功"
echo "全靜態文件發布完成"
qqbot_deploy
}
#緊急回滾措施,只是發布回滾,實際git上面沒回滾,事後得修改原先bug重新提交發布。
function rollback(){
cd ${WORKSPACE}
echo "commitid:$commitid"
if [ ! $commitid ] && [ ! $file ];then
echo "commitid和file至少填一個"
exit 1
elif [ ! $commitid ] && [ $file ];then
#回滾某個文件到上一次的更改
num=2
commitid=`git log -n $num --pretty=format:"%H" $file |sed -n ${num}p`
git checkout $commitid $file
deploy
else
arg="回滾"
# 回滾到指定的commit版本,或者回滾指定版本的某個文件
# git log WebRoot/campaign/daily/share.js 可以查看share.js最近修改的記錄
git checkout $commitid $file
log
#發布回滾的版本
deploy
fi
}
case $select in
Deploy)
echo "select:$select"
commitid=`git rev-parse remotes/origin/dev`
#發布
deploy
;;
Rollback)
echo "select:$select"
#回滾
rollback
;;
*)
echo "*select:$select"
commitid=`git rev-parse remotes/origin/dev`
deploy
;;
esac
4.實際效果
開發人員自動化git工具
#coding:utf-8
#author:laocao
#date: 20181225
#測試環境運行在python3.6 windows上
import os
from time import sleep
code_dir = "D:\\proj\\xiangmu"
#code_dir = "D:\git\xiangmu-git"
print("------------提×××並本地分支的代碼到dev----------")
print("註意:本程序運行的的前提,代碼必須在D:\proj\xiangmu中")
print("")
print("")
os.chdir(code_dir)
print("當前工作目錄:" + os.getcwd())
#輸入分支名稱,拉取遠端dev最新代碼到本地
my_branch=input(‘請輸入您本地的分支名稱:‘)
os.system("git checkout %s" % my_branch)
os.system("git pull origin dev")
#輸入項目描述,並提交到本地
desc=input(‘請輸入項目描述:‘)
os.system("git add -A")
os.system("git commit -am ‘%s‘" % desc)
# 切換到dev分支,拉取dev最新代碼
os.system("git checkout dev")
os.system("git pull")
#合並本地分支到dev,並推送
os.system("git merge %s" % my_branch)
os.system("git push")
#返回本地分支
os.system("git checkout %s" % my_branch)
print("提交成功,返回本地分支%s" % my_branch)
input("按任意鍵退出")
6.項目經理自動化git工具,根據commitid合並
#coding:utf-8
#author:jorden
#date: 20181225
#測試環境運行在python3.6 windows上
import os
from time import sleep
code_dir = "D:\\proj\\xiangmu"
#code_dir = "D:\git\xiangmu-git"
print("------------合並dev的代碼到master----------")
print("註意:本程序運行的的前提,代碼必須在D:\proj\xiangmu中")
print("")
print("")
os.chdir(code_dir)
print("當前工作目錄:" + os.getcwd())
# 切換到dev分支,拉取dev最新代碼
os.system("git checkout dev")
os.system("git pull")
# 切換到master分支,拉取master最新代碼
os.system("git checkout master")
os.system("git pull")
dev_commitid=input(‘請輸入dev中需要合並的commitid:‘)
print(dev_commitid)
os.system("git cherry-pick " + dev_commitid)
print("dev_commitid: %s" % dev_commitid)
os.system("git push")
print("git自動合並完成")
input("按任意鍵退出")
7.項目經理自動化工具,根據文件合並版本。
#coding:utf-8
#author:laocao
#date: 20181225
#測試環境運行在python3.6 windows上
import os
from time import sleep
code_dir = "D:\\proj\\xiangmu"
#code_dir = "D:\git\xiangmu-git"
print("------------合並dev的代碼到master,按文件合並----------")
print("註意:本程序運行的的前提,代碼必須在D:\proj\xiangmu中")
print("")
print("")
os.chdir(code_dir)
print("當前工作目錄:" + os.getcwd())
# 切換到dev分支,拉取dev最新代碼
os.system("git checkout dev")
os.system("git pull")
# 切換到master分支,拉取master最新代碼
os.system("git checkout master")
os.system("git pull")
#dev_commitid=input(‘請輸入dev中需要合並的commitid:‘)
file_list = input(‘請輸入dev中需要合並的文件列表:‘)
os.system("git checkout dev " + file_list)
print("file_list: %s" % file_list)
#輸入項目描述,並提交到本地master
desc=input(‘請輸入項目描述:‘)
os.system("git add -A")
os.system("git commit -am ‘%s‘" % desc)
os.system("git push")
print("git自動合並完成")
input("按任意鍵退出")
8.腳本完成了如下功能:
編譯:根據實際項目,這裏用的ant。也可以maven
動靜分離發布:為了滿足前端和後端的不同的發布需求,提高發布效率,采用了動靜分離發布。純靜態文件直接同步到各服務器,只需幾秒鐘。動態文件發布則需編譯,調用負載均衡,重啟tomca等,需1-2分鐘。
代碼質量分析: 發布完,sonar自動分析開發人員代碼倉庫的代碼質量,作為後期改進。
代碼同步:采用rsync ssh模式進行代碼同步到目標服務器
調用負載均衡api:通過python sdk調用騰訊雲負載均衡api,來上下線服務器。
日誌記錄: 每條命令的執行結果進行記錄。
定義代碼配置中心:一套代碼需要放在幾個環境中運行,所以定義了不同的配置區分不同的環境, 發布時拷貝相對應的配置文件到目標服務器,這樣就達到了只需管理一套代碼,運行在不同的環境。
代碼回滾:有時發布上去的代碼有問題,這時可以緊急回滾,此處提供了按commitid來回滾和按文件回滾兩種方式
發布過程中會對網站狀態進行判斷,如果打開不是200,則不上線。
9.消息通知:采用qqbot機器人自動發消息通知到群,讓團隊了解發布狀態。qqbot采用smartqq協議,由於騰訊已下線,這裏可以采用其他機器人插件(如酷Q),原理一樣。
基於Gitlab+Jenkins的代碼自動化發布