1. 程式人生 > >基於Gitlab+Jenkins的程式碼自動化釋出

基於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設定相應的鉤子事件:
基於Gitlab+Jenkins的程式碼自動化釋出
2.在jenkins中配置git外掛
基於Gitlab+Jenkins的程式碼自動化釋出

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 [email protected]$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 [email protected]$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 [email protected]$host1:$dest 
    log

    arg="釋出$host2的靜態程式碼"     
    rsync -e 'ssh -o stricthostkeychecking=no -p22' -qapgolr --progress --delete $src [email protected]$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.實際效果
基於Gitlab+Jenkins的程式碼自動化釋出
開發人員自動化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),原理一樣。