1. 程式人生 > >基於Gitlab+Jenkins的代碼自動化發布

基於Gitlab+Jenkins的代碼自動化發布

time nag 動靜 cer 發送 最新代碼 -i python3.6 git工具

這裏所講的自動化發布是指代碼從提交到倉庫,到發布到目標服務器的整個過程。

主要涉及到兩個工具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的代碼自動化發布