gitlab
gitlab
配置web hooks
起因和环境
遇到一个问题,帮同事在51的服务器上部署好项目后,他要经常更新代码,项目还需要一直跑起来给别人演示,他每次git push完代码后我都会跑到服务器上把新代码down下来,然后重启项目。一天更新一次也就算了,一天更新个两三回我受不了。
机器配置:
centos7.7
python3.6
nginx
git客户端(已在gitlab配置好当前账户的公钥)
仓库管理:
gitlab
普通用户:
centos
用户组:
wonders: 具有操作python、uwsgi、nginx等文件权限:chown -R root:wonders,chmod -R ?7?
centos属于用户组wonders
配置webhooks
最初的看网友的操作是选中gitlab具体项目-settings-Web Hooks创建一个hooks,这个hooks简单点说就是一个回调事件,其中Trigger复选框就是触发这个回调事件的条件,也就是说在哪几种条件下触发这个回调。在来说说这个回调的URL,假设你勾选了Trigger为第一个Push事件,当该项目的任何成员push该项目代码后都会触发该回调。有一点要说明的是:这个回调是一个POST请求,它会请求一个http服务的地址。也就是说你要启动一个能被访问的http地址供它请求。
最初我是想直接想网友一样使用nginx启动一个php的服务来接受这个回调请求,然后接受到请求后执行一些git命令去同步代码的,但是nginx启动毕竟麻烦还要搞配置文件,最重要的是我不会PHP,我本地恰好装了python,用python直接启动一个服务岂不更方便?不管你是用什么启动一个服务去接受回调请求都可以。
既然如此:那我们在服务器端新建一个py文件:hooks.py
#--*--coding:utf-8--*-- from wsgiref.simple_server import make_server from subprocess import call import os def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) print('Hello!') return [b'hello',] httpd = make_server('', 8001, application) # 监听8001端口,linux 1024以下端口需要root才能启动 print('Serving HTTP on port 8001...') httpd.serve_forever()
然后执行这个py文件
python3 hooks.py
如果没有报错那就会输出启动信息,如果报错的化,可能就是以下几种问题导致:
1、没有访问python3的权限
2、新建的hooks.py没有执行权限
既然我们已经把gitlab该项目的回调服务已经写好,那么就可以直接配置请求该回调服务的地址即可,在settings-Web Hooks-hooks的URL地址栏里填写:
http://你刚才启动回调服务的ip地址:8001/
保存,然后点击Test Hook测试一下该回调是否通。点击后看你回调服务后台的启动信息,如果在你点击Test hooks后gitlab页面没有报错,并提示你一条绿杠里面写一些提示成功的信息,那就说明你的回调已经配置好了,接下来该项目的任何用户push代码后,你启动回调服务的后台都会输出一些Hello!
如果以上问题都没错,下面就可以在回调服务里执行git请求了:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) print('git pull start') os.system('cd /home/centos/mycode/py/test ; /usr/bin/git pull origin master') # 切换到项目的目录,并且执行pull操作 print('git pull finish') return [b'hello',]
将该代码替换原来的 application然后重新运行,你再次点击gitlab上回调钩子的Test hooks,看后台是否正确的打印了你想要的信息,还是输出一大段错误信息,亦或是一个让你输入git pull密码的信息,当遇到这些问题时不用担心,这都是小问题了。
如果是让你输入git密码,那你要看看你当前启动该hooks.py的用户是否是你配置的git用户?
如果是一大段错误信息,可能又是一些访问权限的问题,你要执行chown或chmod等,给当前用户一些权限才行。
获取解析hooks URL中的参数
在上面我们已经配置好了有关git pull自动同步代码的逻辑,但接下来我要做的操作来源于我考虑到一个问题:目前我启动的这个hooks.py独自占用了一个端口8001,而且里面代码是写死的,如果我有多个项目都需有这么一个需求:每个项目都要实现自动化同步。那么显然以上面的那种方式我要启动多个server开辟多个端口,这点让我很gye硬,如果能以传参的方式,我将一些变量传递过来,那么启动一个服务,只要传递的参数不同我就可以切换到不同的项目去执行git pull,而不需要启动多个像hooks.py这样的server。
我最早的在gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://你刚才启动回调服务的ip地址:8001/?id=1&name=apple
然后使用print函数打印出application里的environ参数,成功找到我传递的参数在environ['QUERY_STRING']里,但是需要经过进一步解析成对象格式。
import urllib.parse def application(environ, start_response): print(environ) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) print(query_string) start_response('200 OK', [('Content-Type', 'text/html')]) return [b'hello',]
既然可以传参那么就没啥问题,我们将上面完成的git pull的application代码改一下:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) os.system('cd '+ query_string['GitHome'] +' ; /usr/bin/git pull origin ' + query_string['branch']) # 切换到项目的目录,并且执行pull操作 print('git pull finish') return [b'hello',]
gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://yours ip:8001/?GitHome=/home/centos/code/test&branch=master
如果上一节以不传参的方式能执行,那么现在这种传参的方式也能执行了。
既然要实现自动化部署,那么更新完代码后不重启uwsgi和nginx,也称不上自动化部署。接下来就是重中之重了,上面都只是自己写个test测试git pull,下面就是拿真实项目来搞了!按照这个思路走下去,我下面将我核心代码贴出来:
# -*- coding: utf-8 -*- from wsgiref.simple_server import make_server import urllib.parse import os # GitHome : G # branch : B # virtualenv : V # UwsgiPid : U # NginxPid : N Home = '/home/**/mycode/py/' UBase = '/run/project/' def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) cmd = 'cd ' + Home + query_string['G'] + ' ; ' + '/usr/bin/git pull origin ' + query_string['B'] print(cmd) res = os.popen(cmd) # 切换到项目的目录,并且执行pull操作 cmd = 'workon ' + query_string['V'] + ' ; uwsgi --reload ' + UBase + query_string['U'] + ' ; nginx -c ' + Home + query_string['N'] + ' -s reload ; deactivate' print(cmd) os.system(cmd) return [b'', ] httpd = make_server('', 8001, application) # 监听8001端口 print('Serving HTTP on port 8001...') httpd.serve_forever()
gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://ip:8001/?G=Intelligent-Question&B=znwz_manual&V=explorer_master_env&U=explorer_master/uwsgi/uwsgi.pid&N=Intelligent-Question/Explorer-master/nginx_51.conf
如果你之前的git pull都没问题,到此我只是在git pull代码之后重启了一下uwsgi和nginx,下面贴上我们在项目中重启uwsgi和nginx的方式,我上面的代码里可以看到,只是在拼接下面重启uwsgi和nginx的命令而已。
# 重启uwsgi uwsgi --reload /run/project/***/uwsgi/uwsgi.pid # 重启nginx nginx -c /home/***/nginx_51.conf -s reload
如果你注意到了,我在代码里重启uwsgi时之前加了workon ***,如果你知道python的虚拟环境管理virtualenvwrapper这点一看便知,我只是切换到对应的虚拟环境下,因为我的uwsgi不是装在全局环境里,而是装在一个虚拟环境里【个人洁癖】,workon表示激活切换到该虚拟环境,deactivate表示退出该虚拟环境。
解释性的东西就说这么多,接下来说一下我遇到的问题,点击Test Hook,hooks.py在命令终端输出了如下错误信息:
sh: workon: command not found sh: uwsgi: command not found sh: deactivate: command not found
出现上面的错误,我立马想到是不是当前执行程序的用户不能访问虚拟环境的配置(我的是配置在我用户的.bashrc里了,配置信息如下:)?
# python3 virtualenvwrapper export WORKON_HOME=~/mycode/py/envs #设置virtualenv的统一管理目录 export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/python36/bin/virtualenv export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-packages' #添加virtualenvwrapper的参数,生成干净隔绝的环境 export VIRTUALENVWRAPPER_PYTHON=/usr/local/python36/bin/python3 #指定python解释器 source /usr/local/python36/bin/virtualenvwrapper.sh # 设置每次启动终端自动激活 virtualenvwrapper
既然我不能执行workon说明当前执行用户的人不是我也不属于wonders组?还是说当前执行的用户无权访问/usr/local/python36/bin/virtualenvwrapper.sh,这一条可排除,因为我的python36目录及其下所有文件属于:root:wonders且771。那么就是当前执行的用户没有权限访问virtualenvwrapper.sh了,在网上一番百度,我发现一个重大的消息:
python里的os.system是无法获取系统的环境变量的!而linux任何一个启动的终端都会先去加入环境变量后在启动,这是个重要信息。
接下来我要看看执行当前hooks.py的用户是谁?能否访问到虚拟环境?
import getpass def application(environ, start_response): print(environ) #print(os.system('env')) print(getpass.getuser()) ...
执行以后发现执行当前程序的用户就是我没错,我是可以直接执行workon命令的,而且os虽然能获取环境变量,上面代码并不能说明os.system执行的命令前会加载这些环境变量,那简单了,测测呗:
$ python3 >>> import os >>> os.system('lsvirtualenv') sh: lsvirtualenv: command not found 32512 >>>os.system('source /usr/local/python36/bin/virtualenvwrapper.sh ; lsvirtualenv') test ==== 0
测试可以结果似乎已经说明了,好像是的,os.system执行的命令不会加载环境变量。那我们在执行workon命令之前将一些需要的环境变量加入不就行了?既然我在执行lsvirtualenv之前加入的激活virtualenwrapper.sh的命令,lsvirtualenv能获取结果,那显然workon也能获取结果了。
# -*- coding: utf-8 -*- from wsgiref.simple_server import make_server import urllib.parse import os import logging import traceback #import getpass logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) handler = logging.FileHandler("/home/liuwei/mycode/py/webhooks/log.txt",encoding='utf-8') handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) # GitHome : G # branch : B # virtualenv : V # UwsgiPid : U # NginxPid : N Home = '/home/liuwei/mycode/py/' UBase = '/run/liuwei_project/' activate = 'source /usr/local/python36/bin/virtualenvwrapper.sh ; ' def application(environ, start_response): #print(environ) #print(getpass.getuser()) #print(os.system('env')) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) start_response('200 OK', [('Content-Type', 'text/html')]) try: cmd = 'cd ' + Home + query_string['G'] + ' ; ' + '/usr/bin/git pull origin ' + query_string['B'] logger.info("[Start] git pull cmd: " + cmd) res = os.popen(cmd) # 切换到项目的目录,并且执行pull操作 except Exception: logger.error("pull error!") logger.error(traceback.format_exc()) else: logger.info(res.read()) logger.info("pull finish!") finally: cmd = activate + ' workon ' + query_string['V'] + ' ; uwsgi --reload ' + UBase + query_string['U'] + ' ; nginx -c ' + Home + query_string['N'] + ' -s reload ; deactivate' code = os.system(cmd) logger.info("reload cmd: " + cmd) if code is 0: logger.info("[End] reload uwsgi and nginx Success!") else: logger.info("[End] reload uwsgi and nginx Fail!") return [b'', ] httpd = make_server('', 8001, application) # 监听8001端口 print('Serving HTTP on port 8001...') httpd.serve_forever()
# 以后台方式启动 nohup python3 hooks.py &
gitlab实现自动化部署:https://www.cnblogs.com/rexyan/p/7326581.html
linux命令连接区别:https://www.cnblogs.com/linwenbin/p/10943737.html
python中os.system药要注意的问题:https://www.cnblogs.com/xiu123/p/10355825.html
配置web hooks
遇到一个问题,帮同事在51的服务器上部署好项目后,他要经常更新代码,项目还需要一直跑起来给别人演示,他每次git push完代码后我都会跑到服务器上把新代码down下来,然后重启项目。一天更新一次也就算了,一天更新个两三回我受不了。
wonders: 具有操作python、uwsgi、nginx等文件权限:chown -R root:wonders,chmod -R ?7?
最初的看网友的操作是选中gitlab具体项目-settings-Web Hooks创建一个hooks,这个hooks简单点说就是一个回调事件,其中Trigger复选框就是触发这个回调事件的条件,也就是说在哪几种条件下触发这个回调。在来说说这个回调的URL,假设你勾选了Trigger为第一个Push事件,当该项目的任何成员push该项目代码后都会触发该回调。有一点要说明的是:这个回调是一个POST请求,它会请求一个http服务的地址。也就是说你要启动一个能被访问的http地址供它请求。
最初我是想直接想网友一样使用nginx启动一个php的服务来接受这个回调请求,然后接受到请求后执行一些git命令去同步代码的,但是nginx启动毕竟麻烦还要搞配置文件,最重要的是我不会PHP,我本地恰好装了python,用python直接启动一个服务岂不更方便?不管你是用什么启动一个服务去接受回调请求都可以。
既然如此:那我们在服务器端新建一个py文件:hooks.py
#--*--coding:utf-8--*-- from wsgiref.simple_server import make_server from subprocess import call import os def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) print('Hello!') return [b'hello',] httpd = make_server('', 8001, application) # 监听8001端口,linux 1024以下端口需要root才能启动 print('Serving HTTP on port 8001...') httpd.serve_forever()
如果没有报错那就会输出启动信息,如果报错的化,可能就是以下几种问题导致:
既然我们已经把gitlab该项目的回调服务已经写好,那么就可以直接配置请求该回调服务的地址即可,在settings-Web Hooks-hooks的URL地址栏里填写:
保存,然后点击Test Hook测试一下该回调是否通。点击后看你回调服务后台的启动信息,如果在你点击Test hooks后gitlab页面没有报错,并提示你一条绿杠里面写一些提示成功的信息,那就说明你的回调已经配置好了,接下来该项目的任何用户push代码后,你启动回调服务的后台都会输出一些Hello!
如果以上问题都没错,下面就可以在回调服务里执行git请求了:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) print('git pull start') os.system('cd /home/centos/mycode/py/test ; /usr/bin/git pull origin master') # 切换到项目的目录,并且执行pull操作 print('git pull finish') return [b'hello',]
将该代码替换原来的 application然后重新运行,你再次点击gitlab上回调钩子的Test hooks,看后台是否正确的打印了你想要的信息,还是输出一大段错误信息,亦或是一个让你输入git pull密码的信息,当遇到这些问题时不用担心,这都是小问题了。
如果是让你输入git密码,那你要看看你当前启动该hooks.py的用户是否是你配置的git用户?
如果是一大段错误信息,可能又是一些访问权限的问题,你要执行chown或chmod等,给当前用户一些权限才行。
在上面我们已经配置好了有关git pull自动同步代码的逻辑,但接下来我要做的操作来源于我考虑到一个问题:目前我启动的这个hooks.py独自占用了一个端口8001,而且里面代码是写死的,如果我有多个项目都需有这么一个需求:每个项目都要实现自动化同步。那么显然以上面的那种方式我要启动多个server开辟多个端口,这点让我很gye硬,如果能以传参的方式,我将一些变量传递过来,那么启动一个服务,只要传递的参数不同我就可以切换到不同的项目去执行git pull,而不需要启动多个像hooks.py这样的server。
我最早的在gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://你刚才启动回调服务的ip地址:8001/?id=1&name=apple
然后使用print函数打印出application里的environ参数,成功找到我传递的参数在environ['QUERY_STRING']里,但是需要经过进一步解析成对象格式。
import urllib.parse def application(environ, start_response): print(environ) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) print(query_string) start_response('200 OK', [('Content-Type', 'text/html')]) return [b'hello',]
既然可以传参那么就没啥问题,我们将上面完成的git pull的application代码改一下:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) os.system('cd '+ query_string['GitHome'] +' ; /usr/bin/git pull origin ' + query_string['branch']) # 切换到项目的目录,并且执行pull操作 print('git pull finish') return [b'hello',]
gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://yours ip:8001/?GitHome=/home/centos/code/test&branch=master
如果上一节以不传参的方式能执行,那么现在这种传参的方式也能执行了。
既然要实现自动化部署,那么更新完代码后不重启uwsgi和nginx,也称不上自动化部署。接下来就是重中之重了,上面都只是自己写个test测试git pull,下面就是拿真实项目来搞了!按照这个思路走下去,我下面将我核心代码贴出来:
# -*- coding: utf-8 -*- from wsgiref.simple_server import make_server import urllib.parse import os # GitHome : G # branch : B # virtualenv : V # UwsgiPid : U # NginxPid : N Home = '/home/**/mycode/py/' UBase = '/run/project/' def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) cmd = 'cd ' + Home + query_string['G'] + ' ; ' + '/usr/bin/git pull origin ' + query_string['B'] print(cmd) res = os.popen(cmd) # 切换到项目的目录,并且执行pull操作 cmd = 'workon ' + query_string['V'] + ' ; uwsgi --reload ' + UBase + query_string['U'] + ' ; nginx -c ' + Home + query_string['N'] + ' -s reload ; deactivate' print(cmd) os.system(cmd) return [b'', ] httpd = make_server('', 8001, application) # 监听8001端口 print('Serving HTTP on port 8001...') httpd.serve_forever()
gitlab该项目中settings-Web Hooks-hooks里将URL配置为如下:
http://ip:8001/?G=Intelligent-Question&B=znwz_manual&V=explorer_master_env&U=explorer_master/uwsgi/uwsgi.pid&N=Intelligent-Question/Explorer-master/nginx_51.conf
如果你之前的git pull都没问题,到此我只是在git pull代码之后重启了一下uwsgi和nginx,下面贴上我们在项目中重启uwsgi和nginx的方式,我上面的代码里可以看到,只是在拼接下面重启uwsgi和nginx的命令而已。
# 重启uwsgi uwsgi --reload /run/project/***/uwsgi/uwsgi.pid # 重启nginx nginx -c /home/***/nginx_51.conf -s reload
如果你注意到了,我在代码里重启uwsgi时之前加了workon ***,如果你知道python的虚拟环境管理virtualenvwrapper这点一看便知,我只是切换到对应的虚拟环境下,因为我的uwsgi不是装在全局环境里,而是装在一个虚拟环境里【个人洁癖】,workon表示激活切换到该虚拟环境,deactivate表示退出该虚拟环境。
解释性的东西就说这么多,接下来说一下我遇到的问题,点击Test Hook,hooks.py在命令终端输出了如下错误信息:
sh: workon: command not found sh: uwsgi: command not found sh: deactivate: command not found
出现上面的错误,我立马想到是不是当前执行程序的用户不能访问虚拟环境的配置(我的是配置在我用户的.bashrc里了,配置信息如下:)?
# python3 virtualenvwrapper export WORKON_HOME=~/mycode/py/envs #设置virtualenv的统一管理目录 export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/python36/bin/virtualenv export VIRTUALENVWRAPPER_VIRTUALENV_ARGS='--no-site-packages' #添加virtualenvwrapper的参数,生成干净隔绝的环境 export VIRTUALENVWRAPPER_PYTHON=/usr/local/python36/bin/python3 #指定python解释器 source /usr/local/python36/bin/virtualenvwrapper.sh # 设置每次启动终端自动激活 virtualenvwrapper
既然我不能执行workon说明当前执行用户的人不是我也不属于wonders组?还是说当前执行的用户无权访问/usr/local/python36/bin/virtualenvwrapper.sh,这一条可排除,因为我的python36目录及其下所有文件属于:root:wonders且771。那么就是当前执行的用户没有权限访问virtualenvwrapper.sh了,在网上一番百度,我发现一个重大的消息:
python里的os.system是无法获取系统的环境变量的!而linux任何一个启动的终端都会先去加入环境变量后在启动,这是个重要信息。
接下来我要看看执行当前hooks.py的用户是谁?能否访问到虚拟环境?
import getpass def application(environ, start_response): print(environ) #print(os.system('env')) print(getpass.getuser()) ...
执行以后发现执行当前程序的用户就是我没错,我是可以直接执行workon命令的,而且os虽然能获取环境变量,上面代码并不能说明os.system执行的命令前会加载这些环境变量,那简单了,测测呗:
$ python3 >>> import os >>> os.system('lsvirtualenv') sh: lsvirtualenv: command not found 32512 >>>os.system('source /usr/local/python36/bin/virtualenvwrapper.sh ; lsvirtualenv') test ==== 0
测试可以结果似乎已经说明了,好像是的,os.system执行的命令不会加载环境变量。那我们在执行workon命令之前将一些需要的环境变量加入不就行了?既然我在执行lsvirtualenv之前加入的激活virtualenwrapper.sh的命令,lsvirtualenv能获取结果,那显然workon也能获取结果了。
# -*- coding: utf-8 -*- from wsgiref.simple_server import make_server import urllib.parse import os import logging import traceback #import getpass logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) handler = logging.FileHandler("/home/liuwei/mycode/py/webhooks/log.txt",encoding='utf-8') handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) # GitHome : G # branch : B # virtualenv : V # UwsgiPid : U # NginxPid : N Home = '/home/liuwei/mycode/py/' UBase = '/run/liuwei_project/' activate = 'source /usr/local/python36/bin/virtualenvwrapper.sh ; ' def application(environ, start_response): #print(environ) #print(getpass.getuser()) #print(os.system('env')) query_string = dict(urllib.parse.parse_qsl(environ['QUERY_STRING'])) start_response('200 OK', [('Content-Type', 'text/html')]) try: cmd = 'cd ' + Home + query_string['G'] + ' ; ' + '/usr/bin/git pull origin ' + query_string['B'] logger.info("[Start] git pull cmd: " + cmd) res = os.popen(cmd) # 切换到项目的目录,并且执行pull操作 except Exception: logger.error("pull error!") logger.error(traceback.format_exc()) else: logger.info(res.read()) logger.info("pull finish!") finally: cmd = activate + ' workon ' + query_string['V'] + ' ; uwsgi --reload ' + UBase + query_string['U'] + ' ; nginx -c ' + Home + query_string['N'] + ' -s reload ; deactivate' code = os.system(cmd) logger.info("reload cmd: " + cmd) if code is 0: logger.info("[End] reload uwsgi and nginx Success!") else: logger.info("[End] reload uwsgi and nginx Fail!") return [b'', ] httpd = make_server('', 8001, application) # 监听8001端口 print('Serving HTTP on port 8001...') httpd.serve_forever()
# 以后台方式启动 nohup python3 hooks.py &
gitlab实现自动化部署:https://www.cnblogs.com/rexyan/p/7326581.html
linux命令连接区别:https://www.cnblogs.com/linwenbin/p/10943737.html
python中os.system药要注意的问题:https://www.cnblogs.com/xiu123/p/10355825.html