使用Github Webhooks自动部署代码

编程/技术 更新时间: 2019-10-24 @ 14:18:31 创建时间: 2019-10-24 @ 14:14:34 浏览数: 133 净访问: 88 By: skyrover

本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议


自己的博客代码是托管在Github的,每次修改之后都要提交代码,然后去服务器手动暂停应用,然后拉代码,再重启,非常麻烦。后来看到Github的项目支持一个叫WebHooks的东东,刚好可以用来做自动部署。

首先应用部署得集中管理,之前都是在tmux里手动执行inv命令,因为tmux会保持,所以应用也就能保持启动,但是这样会很不安全,也不稳定,而且如果要做自动部署,这样也不方便,所以就改成使用supervisor来管理。

使用supervisor管理进程

网站有两个启动任务,一个是web服务器,一个是celery worker,之前是在tmux里面手动启动的,现在改成了使用supervisor进行管理。supervisor是client/server结构,分为supervisord和supervisorctl

安装:pip install supervisor

下面是配置:

[unix_http_server]
file=/home/www/supervisor/supervisor.sock   ; the path to the socket file

[supervisord]
logfile=/home/www/supervisor/supervisord.log ; main log file; default $CWD/supervisord.log
logfile_maxbytes=50MB        ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10           ; # of main logfile backups; 0 means none, default 10
loglevel=info                ; log level; default info; others: debug,warn,trace
pidfile=/home/www/supervisor/supervisord.pid ; supervisord pidfile; default supervisord.pid
nodaemon=false               ; start in foreground if true; default false
minfds=1024                  ; min. avail startup file descriptors; default 1024
minprocs=200                 ; min. avail process descriptors;default 200

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///home/www/supervisor/supervisor.sock ; use a unix:// URL  for a unix socket

[program:skyrover]
directory=/home/www/astronomy
command=/home/www/skyrover_env/bin/inv web.gunicorn
environment=HOME="/home/www",USER="www",FLASK_ENV="prod"
stderr_logfile=/home/www/supervisor/skyrover_prod-stderr.log
stdout_logfile=/home/www/supervisor/skyrover_prod-stdout.log
user=www
stopasgroup=true
autostart=true
autorestart=true
startretries=3
startsecs=1
loglevel=debug

[program:skyrover-worker]
directory=/home/www/astronomy
command=/home/www/skyrover_env/bin/inv celery.all-in-one
environment=HOME="/home/www",USER="www",FLASK_ENV="prod"
stderr_logfile=/home/www/supervisor/skyrover_worker_prod-stderr.log
stdout_logfile=/home/www/supervisor/skyrover_worker_prod-stdout.log
user=www
stopasgroup=true
autostart=true
autorestart=true
startretries=3
startsecs=1
loglevel=debug

启动supervisord:supervisord -c supervisor.conf

supervisorctl的控制命令:

supervisorctl stop program_name 停止某个进程
supervisorctl start program_name    启动某个进程
supervisorctl restart program_name  重启某个进程
supervisorctl stop all  停止全部进程
supervisorctl reload    载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程
supervisorctl update    根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启

github自动部署

首先将项目的git远程仓库地址改为ssh模式。首先将机器的ssh公钥放到github的设置里面,然后复制ssh模式的仓库地址。

到服务器上的仓库所在地,然后:

git remote -v
git remote rm origin
git remote add origin git@github.com:Microndgt/astronomy.git

然后在github仓库的setting里配置webhooks,如图

然后这个是我们的自动部署脚本:

#! /bin/bash

SITE_PATH='/home/www/astronomy'
USER='www'
USERGROUP='www'

cd $SITE_PATH
git reset --hard origin/master
git clean -f
git pull
git checkout master
chown -R $USER:$USERGROUP $SITE_PATH
FLASK_ENV=prod flask db upgrade
supervisorctl -c /home/www/supervisor/supervisord.conf restart skyrover-worker skyrover

接下来是部署一个监听服务器,不能用当前的应用服务器来搞,因为需要重启,如果用当前服务器重启自己就会有问题,比如停止了,启动不起来,而且停止后github就收不到响应了,而且还得自己写签名算法比较麻烦。

github-webhook-handler作为自动部署服务器

可以使用github-webhook-handler来部署,

首先在在shell脚本的同级目录下面执行下面命令初始化一个package.json,并且安装上面说的库:

sudo apt-get install npm
npm config set registry https://registry.npm.taobao.org
npm init -f
npm i -S github-webhook-handler

安装必要工具sudo npm install pm2 -g --no-optional

参考上面的项目,这个是我们使用的node.js服务器,下面这块代码需要将secret填进去,这个secret就是在Github WebHooks里面配置的那个。另外注意服务器的path是/auto-build,监听接口是6666,可以自己根据情况修改

var http = require('http');
var spawn = require('child_process').spawn;
var createHandler = require('github-webhook-handler');

// 下面填写的myscrect跟github webhooks配置一样,下一步会说;path是我们访问的路径
var handler = createHandler({ path: '/auto-build', secret: '' });

http.createServer(function (req, res) {
  handler(req, res, function (err) {
    res.statusCode = 404;
    res.end('no such location');
  })
}).listen(6666);

handler.on('error', function (err) {
  console.error('Error:', err.message)
});

// 监听到push事件的时候执行我们的自动化脚本
handler.on('push', function (event) {
  console.log('Received a push event for %s to %s',
    event.payload.repository.name,
    event.payload.ref);

  runCommand('sh', ['./auto_build.sh'], function( txt ){
    console.log(txt);
  });
});

function runCommand( cmd, args, callback ){
    var child = spawn( cmd, args );
    var response = '';
    child.stdout.on('data', function( buffer ){ resp += buffer.toString(); });
    child.stdout.on('end', function(){ callback( resp ) });
}

// 由于我们不需要监听issues,所以下面代码注释掉
//  handler.on('issues', function (event) {
//    console.log('Received an issue event for %s action=%s: #%d %s',
//      event.payload.repository.name,
//      event.payload.action,
//      event.payload.issue.number,
//      event.payload.issue.title)
// });

保存为index.js后运行pm2 start index.js,会遇到下面的问题/usr/bin/env: ‘node’: No such file or directory,执行sudo ln -s /usr/bin/nodejs /usr/bin/node后重试

重试后遇到这个错误:

~ » pm2 start index.js
/usr/local/lib/node_modules/pm2/node_modules/debug/src/node.js:132
        let val = process.env[key];
        ^^^

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:374:25)
    at Object.Module._extensions..js (module.js:417:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Module.require (module.js:354:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (/usr/local/lib/node_modules/pm2/node_modules/debug/src/index.js:9:19)
    at Module._compile (module.js:410:26)
    at Object.Module._extensions..js (module.js:417:10)

查了下说是node的版本太低,所以执行下面的升级:

sudo npm install -g n
sudo n stable

启动后,在nginx上加上反向代理配置后重新加载nginx配置sudo nginx -s reload

location /auto-build {
    proxy_pass http://127.0.0.1:6666;
}

我在使用pm2启动应用的时候遇到了一些错,这些命令是一些常用的:

pm2 list
pm2 restart <app_id>
pm2 logs

最后设置好这些之后再提交代码,就可以成功自动部署了!

使用Python作为自动部署服务器

这个就是需要自己单独再弄一个web服务器,可以使用各种框架,比如Tornado, Flask, Bottle, Webpy等。下面以Flask为例子,主要代码如下:

import hashlib
import hmac
import subprocess


@blueprint.route('/auto-build', methods=['POST'])
@csrf_protect.exempt
def auto_build():
    body = request.get_data()
    github_secret_key = current_app.config.get('GITHUB_SECRET_KEY', '')
    x_hub_sign = request.headers.environ.get('HTTP_X_HUB_SIGNATURE')
    hashed = hmac.new(github_secret_key.encode('utf-8'), body, hashlib.sha1)
    if x_hub_sign != 'sha1=' + hashed.hexdigest():
        return error()
    command = 'sh /home/www/auto_build.sh'
    try:
        subprocess.run(command, shell=True, check=True)
    except Exception as e:
        logging.exception(e)
        return error()
    return success({})


点赞走一波😏


评论

提交评论
Surmon
2019-10-31 @ 00:25:51 中国 上海 上海

现在可以用 actions 了

Mac OS X:10.15.1-Other Chrome:78.0.3904
skyrover
2019-10-31 @ 10:30:51 中国 陕西 西安

@Surmon ok 我去看看

Linux:-Other Chrome:73.0.3683