持續整合概念
持續整合是一種軟體開發實踐,即團隊開發成員經常整合他們的工作,通過每個成員每天至少整合一次,也就意味著每天可能會發生多次整合。每次整合都通過自動化的構建(包括編譯,釋出,自動化測試)來驗證,從而儘早地發現整合錯誤。 --馬丁福勒
git工作分支
持續整合的前提必須要有一個健壯且分明的版本工具,毫無疑問我們這裡使用git
作為版本工具
這裡只簡單說一下各個分支的作用,想了解更多關於git工作流知識,請點選深入理解學習Git工作流
- feature/* 功能分支,用於一個新的功能的開發
- hotfix/* 熱修復分支,用於對線上環境的bug熱修復
- develop/* 測試分支,測試環境對應的分支
- master分支,預上線環境分支
對於hotfix和feature分支允許開發者push,對於develop和master分支只允許開發者merge。
本文原理分析圖示
- 首先開發者完成程式碼後git push到gitlab伺服器,通過gitlab上事先設定好的系統鉤子來觸發一個post請求到後端的webserver伺服器。
- 後端webserver伺服器收到請求後通過gitlabCI.py分析來源分支與專案組,然後交給不同的shell指令碼處理。
- 通過shell指令碼來更新不同環境的專案程式碼,如果是開發分支的話還需要配置nginx並推送訪問url至釘釘。
- ELK伺服器監控php的專案報錯日誌,開發者通過檢視然後及時進行debug。
webserver對git請求的具體處理圖示
開發環境(dev_branch)處理流程
對於hotfix/* 或者 feature/*
- git push事件 觸發gitlab鉤子,然後gitlabCI指令碼通過條件檢測,執行開發分支的shell指令碼
- shell指令碼拉去對應的功能分支或者熱修復分支,然後拷貝一份nginx模板配置檔案,修改對應的域名和目錄。
- 過載nginx配置檔案並將訪問連線推送至釘釘群。
gitlab伺服器配置
-
配置伺服器祕鑰
-
新增系統鉤子,並選擇在什麼時候觸發鉤子
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 git@example.com:${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伺服器配置
效果展示
-
gitlab合併請求推送至釘釘
-
nginx訪問url釘釘推送
-
ELK展示php錯誤日誌
總結
- 並非所有專案都需要自動部署,我司線上環境是通過git tag,然後使用灰度釋出指令碼手動釋出。常用指令碼在我github上:github地址
- 我司另一個專案因為用到了java和客戶端app,現在測試使用Jenkins的另一套自動化流程方案,屆時我再總結出來。
喜歡我寫的東西的朋友可以關注一下我的公眾號,上面有我的學習資源以及一些其他福利。:Devops部落