初創團隊持續集成的落地與實現(gitlab+python)

持續集成概念

持續集成是一種軟件開發實踐,即團隊開發成員經常集成他們的工作,通過每個成員每天至少集成一次,也就意味着每天可能會發生多次集成。每次集成都通過自動化的構建(包括編譯,發佈,自動化測試)來驗證,從而儘早地發現集成錯誤。 --馬丁福勒


git工作分支

持續集成的前提必須要有一個健壯且分明的版本工具,毫無疑問我們這裏使用git作爲版本工具

這裏只簡單說一下各個分支的作用,想了解更多關於git工作流知識,請點擊深入理解學習Git工作流

  • feature/* 功能分支,用於一個新的功能的開發
  • hotfix/* 熱修復分支,用於對線上環境的bug熱修復
  • develop/* 測試分支,測試環境對應的分支
  • master分支,預上線環境分支

對於hotfix和feature分支允許開發者push,對於develop和master分支只允許開發者merge。


本文原理分析圖示

初創團隊持續集成的落地與實現(gitlab+python)

  • 首先開發者完成代碼後git push到gitlab服務器,通過gitlab上事先設定好的系統鉤子來觸發一個post請求到後端的webserver服務器。
  • 後端webserver服務器收到請求後通過gitlabCI.py分析來源分支與項目組,然後交給不同的shell腳本處理。
  • 通過shell腳本來更新不同環境的項目代碼,如果是開發分支的話還需要配置nginx並推送訪問url至釘釘。
  • ELK服務器監控php的項目報錯日誌,開發者通過查看然後及時進行debug。

webserver對git請求的具體處理圖示

初創團隊持續集成的落地與實現(gitlab+python)


開發環境(dev_branch)處理流程

初創團隊持續集成的落地與實現(gitlab+python)

對於hotfix/ 或者 feature/

  • git push事件 觸發gitlab鉤子,然後gitlabCI腳本通過條件檢測,執行開發分支的shell腳本
  • shell腳本拉去對應的功能分支或者熱修復分支,然後拷貝一份nginx模板配置文件,修改對應的域名和目錄。
  • 重載nginx配置文件並將訪問連接推送至釘釘羣。

gitlab服務器配置

  • 配置服務器祕鑰
    初創團隊持續集成的落地與實現(gitlab+python)

  • 添加系統鉤子,並選擇在什麼時候觸發鉤子

初創團隊持續集成的落地與實現(gitlab+python)


webserver配置

配置gitlab處理腳本

這裏只貼出部分代碼只供參考,因爲具體需求可能不同,這裏就拋磚引玉。

  • gitlabCI.py 用於處理gitlab的事件請求
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2018-12-18 17:41
# @Author  : opsonly
# @Site    :
# @File    : gitlabCi.py
# @Software: PyCharm

from flask import Flask,request,render_template,make_response,Response
import json,os,re,requests
import subprocess
import re

app = Flask(__name__)
null = ""
cmd = "/var/www/html/"
@app.route('/test',methods=['POST'])
def hello():
    json_dict = json.loads(request.data)

    name = json_dict['event_name']

    #字符串截取分支名
    ref = json_dict['ref'][11:]

    ssl = json_dict['project']['url']

    #gitlab項目名
    project = json_dict['project']['name']

    #gitlab分組名
    namespace = json_dict['project']['namespace']

    hostfix = re.compile(r'hostfix/*')
    feature = re.compile(r'feature/*')

    if name == 'push':
        if namespace == 'it':

            #預上線分支
            if ref == 'master':
                cmd = './itmaster.sh ' + project + ref + ' ' + namespace
                s = subprocess.getoutput(cmd)
                return Response(s)

            # 測試分支
            elif ref == 'develop':
                cmd = './itdevelop.sh ' + project + ref + ' ' + namespace
                s = subprocess.getoutput(cmd)
                return Response(s)

            #開發分支
            elif hostfix.match(ref) and feature.match(ref):
                cmd = './itOwn.sh' + project + ref + ' ' + namespace + '' + ssl
                s = subprocess.getoutput(cmd)
                return Response(s)

        elif namespace == 'web':
            if ref == 'master':
                cmd = './webMaster.sh ' + project + ref + ' ' + namespace
                s = subprocess.getoutput(cmd)
                return Response(s)
            elif ref == 'develop':
                cmd = './webDevelop.sh ' + project + ref + ' ' + namespace
                s = subprocess.getoutput(cmd)
                return Response(s)
                # 開發分支
            elif hostfix.match(ref) and feature.match(ref):
                cmd = './webOwn.sh' + project + ref + ' ' + namespace
                s = subprocess.getoutput(cmd)
                return Response(s)

    elif name =='merge_request':

        #可以定義一個釘釘推送,每次直接點開鏈接就能直達gitlab合併界面
        pass
    else:
        return Response('未觸發事件')

if __name__ == '__main__':
    app.run()

將不同的請求分發至不同shell腳本來處理

it組shell腳本

  • 測試服務器腳本
#!/bin/bash
Dir="/var/www/html"
function ERROR_NOTICE() {
    url="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxx"
    header="'Content-Type: application/json'"
    msg="'{\"msgtype\": \"text\",\"text\": {\"content\":\"$1 $2 $3\"}}'"
    a='curl '$url' -H '$header' -d '$msg
    eval $a

}

function IF_TRUE() {
    if [ $? -ne 0 ];then
        ERROR_NOTICE $1 $2 $3
    fi
}

function master() {
    if [ -d $Dir/$1 ];then
        cd $Dir/$1
        #startTime=$(ls -l composer.lock|awk '{print $6,$7,$8}')
        git fetch
        git checkout $2
        git pull origin $2
        cp .env.develop .env

        composer install
        IF_TRUE $1 $2 $3
      #fi
    /usr/local/php7/bin/php artisan queue:restart

        IF_TRUE $1 $2 $3

        echo $1 " Success"
    else
        cd $Dir
        git clone [email protected]:${3}/${1}.git
        cd ${1}
        git checkout $2
        cp .env.develop .env
        composer install
        IF_TRUE $1 $2 $3

        /usr/local/php7/bin/php artisan queue:restart
        IF_TRUE $1 $2 $3
    fi
}
master $1 $2 $3

web組shell腳本

  • 測試環境shell更新腳本
#!/bin/bash

#定義文件目錄
Dir="/var/www/html"
EnvirmentJs="/var/www/html/ucarCarWeb/src/js/environment.js.develop"
DirEnvirJs="/var/www/html/ucarCarWeb/src/js/environment.js"
EnjoyJsDe="/var/www/html/EnjoyCarWeb/src/config/environment.js.develop"
EnjoyJs="/var/www/html/EnjoyCarWeb/src/config/environment.js"
function pull_say() {
        PullDir=$1
        if [ $? -ne 0 ];then
                echo "$PullDir Git Pull Error"
        fi
}
echo 'start'
if [ $1 == "EnjoyCarWeb" ];then
        cd $Dir/EnjoyCarWeb
        startTime=$(ls -l package.json|awk '{print $6,$7,$8}')
        JstartTime=$(ls -l $EnjoyJsDe|awk '{print $6,$7,$8}')

        #拉取項目代碼
        git pull origin develop/v1.3.4
        pull_say
        stopTime=$(ls -l package.json|awk '{print $6,$7,$8}')
        JstopTime=$(ls -l $EnjoyJsDe|awk '{print $6,$7,$8}')

        if [ "$JstartTime" != "$JstopTime" ];then
                cp $EnjoyJsDe $EnjoyJs
        fi

        #編譯代碼
        if [ "$startTime" != "$stopTime" ];then
                rm -f package-lock.json
                /usr/bin/npm install
                /usr/bin/node build/build.js
        else
                /usr/bin/node build/build.js
        fi
        echo $1 "Success"

elif [ $1 == "ucarCarWeb" ];then
    cd $Dir/ucarCarWeb
        startTime=$(ls -l package.json|awk '{print $6,$7,$8}')
    JstartTime=$(ls -l $EnvirmentJs|awk '{print $6,$7,$8}')
    git pull origin develop
    pull_say
        stopTime=$(ls -l package.json|awk '{print $6,$7,$8}')
    JstopTime=$(ls -l $EnvirmentJs|awk '{print $6,$7,$8}')
        if [ "$startTime" != "$stopTime" ];then
        rm -f package-lock.json
        /usr/bin/npm install
        /usr/bin/node build/build.js
    else
        /usr/bin/node build/build.js
    fi
    if [ "$JstartTime" != "$JstopTime" ];then
        cp $EnvirmentJs $DirEnvirJs
    fi
    echo $1 "Success"

fi
echo "Complate.."

開發分支和預算線分支與上面大致相同,這裏就不貼出來了

ELK服務器配置

請點擊ELK實時分析之php的laravel項目日誌


效果展示

  • gitlab合併請求推送至釘釘
    初創團隊持續集成的落地與實現(gitlab+python)

  • nginx訪問url釘釘推送
    初創團隊持續集成的落地與實現(gitlab+python)

  • ELK展示php錯誤日誌
    初創團隊持續集成的落地與實現(gitlab+python)

總結

  • 並非所有項目都需要自動部署,我司線上環境是通過git tag,然後使用灰度發佈腳本手動發佈。常用腳本在我github上:github地址
  • 我司另一個項目因爲用到了java和客戶端app,現在測試使用Jenkins的另一套自動化流程方案,屆時我再總結出來。

喜歡我寫的東西的朋友可以關注一下我的公衆號,上面有我的學習資源以及一些其他福利。:Devops部落
初創團隊持續集成的落地與實現(gitlab+python)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章