這裏所講的自動化發佈是指代碼從提交到倉庫,到發佈到目標服務器的整個過程。前端
主要涉及到兩個工具Gitlab,Jenkins,要完成自動化還須要rsync,qqbot,log,ant、shell腳本,python等。java
Gitlab:咱們主要用它來作代碼的倉庫python
Jenkins:用來執行任務的持續集成,構建等。
1、大致的自動化思路:git
開發人員push代碼到gitlab,觸發webhook,調用jenkins job。 jenkins job 執行拉取代碼,編譯,調用loadblance,下架部分服務器更新代碼,驗證更新後的可用性,上線;再下架另外一部分服務器,更新代碼,再上線。 更新完後,將本次發佈的狀態信息推送給項目組。web
2、實際工做中,咱們遇到的比以上要複雜。spring
服務器環境包括:測試環境,開發環境,預發佈環境,生產環境等。 代碼倉庫又分爲多個分支:master分支,開發分支,項目分支,本地分支等。shell
所以要完成整個過程自動化,還須要整合多分支,多環境的狀況。apache
3、測試環境的自動化思路:windows
1.建一個dev分支用來專門發佈測試環境,此分支只容許開發人員合併代碼和push,不能直接在上面改代碼。 2.開發人員開發一個功能,先在本地建一個本地分支,寫完後合併到dev,而後push到gitlab,gitlab觸發鉤子事件,調用jenkins完成項目的自動化部署。後端
以上2點看似已經實現了自動化發佈,但實踐發現,開發人員仍需花很多時間在代碼的提交 ,切換分支,合併分支,push代碼等重複而繁瑣的工做上。因而這裏我作了對GIT操做的自動化,將提交、切換、合併、push整合整合起來成工具,後面會列出工具代碼。
4、預發佈環境的自動化思路:
1.預發佈的自動化採用和測試環境同樣的思路,只是dev分支換成了master分支。 2.master分支:咱們用它來發布預發佈和生產環境,對沒錯兩套環境用同一分支,此分支只有項目經理有權限push,普通開發沒權限操做。
以上2點看似也實現了代碼的自動化發佈,但實際工做中項目經理一樣也要花很多時間在代碼的提交 ,切換分支,合併分支,push代碼等重複繁瑣的工做,所以這裏也要解決項目經理Git操做的自動化,後面列出工具。
5、生產環境的自動化發佈:
生產環境的發佈其實只是取消了webhook的自動觸發jenkins job,改成手動點擊發布,主要是爲發佈安全考慮。
6、實操:
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" #目標主機,測試機一、測試機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
動靜分離發佈:爲了知足前端和後端的不一樣的發佈需求,提升發佈效率,採用了動靜分離發佈。純靜態文件直接同步到各服務器,只需幾秒鐘。動態文件發佈則需編譯,調用負載均衡,重啓tomcat等,需1-2分鐘。
代碼質量分析: 發佈完,sonar自動分析開發人員代碼倉庫的代碼質量,做爲後期改進。
代碼同步:採用rsync ssh模式進行代碼同步到目標服務器
調用負載均衡api:經過python sdk調用騰訊雲負載均衡api,來上下線服務器。
日誌記錄: 每條命令的執行結果進行記錄。
定義代碼配置中心:一套代碼須要放在幾個環境中運行,因此定義了不一樣的配置區分不一樣的環境, 發佈時拷貝相對應的配置文件到目標服務器,這樣就達到了只需管理一套代碼,運行在不一樣的環境。
代碼回滾:有時發佈上去的代碼有問題,這時能夠緊急回滾,此處提供了按commitid來回滾和按文件回滾兩種方式
發佈過程當中會對網站狀態進行判斷,若是打開不是200,則不上線。
9.消息通知:採用qqbot機器人自動發消息通知到羣,讓團隊瞭解發佈狀態。qqbot採用smartqq協議,因爲騰訊已下線,這裏能夠採用其餘機器人插件(如酷Q),原理同樣。
更多精彩,關注公衆號