持续集成概念
持续集成是一种软件开发实践, 即团队开发成员经常集成他们的工作, 通过每个成员每天至少集成一次, 也就意味着每天可能会发生多次集成. 每次集成都通过自动化的构建 (包括编译, 发布, 自动化测试) 来验证, 从而尽早地发现集成错误. -- 马丁福勒
Git 工作分支
持续集成的前提必须要有一个健壮且分明的版本工具, 毫无疑问我们这里使用 Git 作为版本工具
这里只简单说一下各个分支的作用, 想了解更多关于 Git 工作流知识, 请点击深入理解学习 Git 工作流 https://www.cnblogs.com/xirongliu/p/4584653.html
feature/* 功能分支, 用于一个新的功能的开发
hotfix/* 热修复分支, 用于对线上环境的 bug 热修复
develop/* 测试分支, 测试环境对应的分支
master 分支, 预上线环境分支
对于 hotfix 和 feature 分支允许开发者 push, 对于 develop 和 master 分支只允许开发者 merge.
本文原理分析图示
首先开发者完成代码后 Git push 到 GitLab 服务器, 通过 GitLab 上事先设定好的系统钩子来触发一个 post 请求到后端的 webserver 服务器.
后端 webserver 服务器收到请求后通过 GitLab CI.py 分析来源分支与项目组, 然后交给不同的 shell 脚本处理.
通过 shell 脚本来更新不同环境的项目代码, 如果是开发分支的话还需要配置 nginx 并推送访问 url 至钉钉.
ELK 服务器监控 PHP 的项目报错日志, 开发者通过查看然后及时进行 debug.
webserver 对 Git 请求的具体处理图示
开发环境 (dev_branch) 处理流程
对于 hotfix/ 或者 feature/
Git push 事件 触发 GitLab 钩子, 然后 GitLab CI 脚本通过条件检测, 执行开发分支的 shell 脚本
shell 脚本拉去对应的功能分支或者热修复分支, 然后拷贝一份 nginx 模板配置文件, 修改对应的域名和目录.
重载 nginx 配置文件并将访问连接推送至钉钉群.
GitLab 服务器配置
配置服务器秘钥
添加系统钩子, 并选择在什么时候触发钩子
webserver 配置
配置 GitLab 处理脚本
这里只贴出部分代码只供参考, 因为具体需求可能不同, 这里就抛砖引玉.
GitLab CI.py 用于处理 GitLab 的事件请求
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- # @Time : 2018-12-18 17:41
- # @Author : opsonly
- # @Site :
- # @File : GitLab CI.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 服务器配置
请点击 ELK 实时分析之 PHP 的 Laravel 项目日志
https://www.jianshu.com/p/4b3bd9e8d00d
效果展示
GitLab 合并请求推送至钉钉
nginx 访问 url 钉钉推送
ELK 展示 PHP 错误日志
总结
并非所有项目都需要自动部署, 我司线上环境是通过 Git tag, 然后使用灰度发布脚本手动发布. 常用脚本在我 GitHub 上: GitHub 地址 https://github.com/opsonly
我司另一个项目因为用到了 java 和客户端 App, 现在测试使用 Jenkins 的另一套自动化流程方案, 届时我再总结出来.
喜欢我写的东西的朋友可以关注一下我的公众号, 上面有我的学习资源以及一些其他福利.:Devops 部落
来源: http://blog.51cto.com/dashui/2340204