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

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

主要涉及到兩個工具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設置相應的鉤子事件:
基於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"

#目標主機,測試機一、測試機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.實際效果
基於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

動靜分離發佈:爲了知足前端和後端的不一樣的發佈需求,提升發佈效率,採用了動靜分離發佈。純靜態文件直接同步到各服務器,只需幾秒鐘。動態文件發佈則需編譯,調用負載均衡,重啓tomcat等,需1-2分鐘。

代碼質量分析: 發佈完,sonar自動分析開發人員代碼倉庫的代碼質量,做爲後期改進。

代碼同步:採用rsync ssh模式進行代碼同步到目標服務器

調用負載均衡api:經過python sdk調用騰訊雲負載均衡api,來上下線服務器。

日誌記錄: 每條命令的執行結果進行記錄。

定義代碼配置中心:一套代碼須要放在幾個環境中運行,因此定義了不一樣的配置區分不一樣的環境, 發佈時拷貝相對應的配置文件到目標服務器,這樣就達到了只需管理一套代碼,運行在不一樣的環境。

代碼回滾:有時發佈上去的代碼有問題,這時能夠緊急回滾,此處提供了按commitid來回滾和按文件回滾兩種方式

發佈過程當中會對網站狀態進行判斷,若是打開不是200,則不上線。
9.消息通知:採用qqbot機器人自動發消息通知到羣,讓團隊瞭解發佈狀態。qqbot採用smartqq協議,因爲騰訊已下線,這裏能夠採用其餘機器人插件(如酷Q),原理同樣。

更多精彩,關注公衆號
基於Gitlab+Jenkins的代碼自動化發佈

相關文章
相關標籤/搜索