高效前端项目自动化构建部署实践——使用webhook钩子运维

1,835 阅读5分钟

本文首发于我的个人博客:高效前端项目自动化构建部署实践——使用webhook钩子运维,欢迎大家来访问哦!

优化原由

最近访问我的博客网站的时候,我发现经常会出现打不开的情况,我的博客是搭载在coding的coding pages服务上的,仔细检查了coding服务上的代码,发现没什么毛病,应该就是coding pages的服务器不稳定造成的,由于最近在投递简历,这个情况也给我造成了一定的困扰,考虑到要给招聘者良好的浏览体验,我决定要优化一下自己的博客网站运行环境了。

优化思路——将网站文件放到我自己的服务器上

执行方法——使用ftp部署

我的优化思路就是将网站文件放到我自己的服务器上。起初我打算使用Hexo的ftp部署相关的插件来完成,当我运行hexo d的时候,文件会自动上传到服务器端,我这样试着弄了之后发现由于ftp协议的特性,在传大量的文件的时候,无法充分利用网络带宽,因此造成了速度非常慢的情况。这种情况只能将网站文件打包压缩再上传,然后在服务器端解压才能解决。想了想,我觉得这样太复杂又累赘,实在是不能体现我代码洁癖的风格,拒绝拒绝!

执行方法——使用webhook自动运维部署

我在之前实习公司里,有见同事用过webhook这玩意儿,了解过其运作方法和作用。又查了一下相关资料(下为Github解释):

Webhooks allow you to build or set up integrations, such as GitHub Apps or OAuth Apps, which subscribe to certain events on GitHub.com. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL. Webhooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. You're only limited by your imagination.

简单来说就是WebHooks可用于更新外部问题跟踪器、触发CI构建、更新备份镜像,甚至部署到生产服务器。这对于我目前的需求完全匹配呀,于是新的思路来了——在服务器端克隆下自己的博客项目并安装依赖,在coding服务上设置相应的webhook钩子,在服务器端编写接受webhook请求的回调方法,在回调方法里执行构建相关的命令。这样就不用把文件传来传去了,简洁明了,开始搞!

执行步骤

  1. 在服务器上合适的位置克隆下自己的项目文件,并安装好依赖,我这里是hexo博客项目,所以还得全局安装hexo依赖。

  2. 编写接收webhook回调的服务,我这里是用node.js写的,你也可以使用其他后端语言,只要能执行shell命令就行。 在服务端合适的位置编写webhook.js如下:

const http = require('http')
const { exec } = require('child_process')

const PORT = 9999 // 服务端口
const path = '/www/wwwroot/projects/xxxxxxx/' // 这里是服务器端,此项目的文件地址
const commands = [
  'cd ' + path,
  'git pull',
  'yarn dep', // 这个命令是我hexo项目编译的命令
  'rm -rf ../../hexo-blog/', // 删除原来的文件
  'mv ' + path + 'public/ ../../hexo-blog/' // 将编译的文件放入网站项目文件夹
].join(' && ') // 这里写依次要在服务器上执行的命令

var deployServer = http.createServer(function(req, res) {
  if (req.url.search(/deploy\/?$/i) > 0) { // 这里做了个deploy的标记,以防用户访问此网址无意出发命令
    let work = exec(commands, {
      maxBuffer: 5000 * 1024, // 默认 200 * 1024
    }, function(err, out) {
      if (!err) {
        res.writeHead(200)
        res.end('Deploy Done.')
      }
    })
    work.stdout.on('data', function(data) {
      console.log('stdout: ' + data)
    })
    work.stderr.on('data', function(data) {
      res.writeHead(500)
      res.end('Server Internal Error.')
      console.log('stderr: ' + data)
    })
  } else {
    res.writeHead(404)
    res.end('Not Found.')
  }
})

deployServer.listen(PORT)

代码很短很简单,重点就是要调用node的子进程来执行命令。有个坑要注意,使用子进程时,在回调函数里的打印是不会输出到控制台的,需要将当前执行过程设为一个变量,通过其stdout和stderr的data监听来获取打印输出。另外,exec在执行时会在内存中建一个buffer来缓冲组合所有的输出数据,而maxBuffer则是指定该buffer大小的属性。如果输出超过指定的大小则会报 maxBuffer exceeded 的错误,这也是很常见的错误,所以我们需要加大其maxBuffer。

写好服务端代码后,我们ssh连接服务器,执行node webhook.js将其启动并不要关闭,以备后面调试,并且要注意你设置的端口在服务器安全组里已经打开过了,或者你也可以使用Nginx反向代理到80端口开启另一个服务,这里就不细说了,目的就是要让外网能够访问到此服务。

  1. 在Git项目管理的设置里开启并设置webhook服务

    webhook-setting
    URL是必填项,填写你格式如:http://你的服务器地址:刚才设置的端口/deploy,令牌则是发送webhook时,客服端做的记号,也是起到防止恶意或无意请求到webhook服务的作用,如果设置了令牌,根据Git服务端的不同,会发送不同的令牌格式,一般是放在请求头的自定义参数里,我们需要在服务端接收请求的方法里进行处理,这里不再做演示。

  2. 如果设置完成,系统会自动发送检测命令来出发webhook回调。

    webhook-setting-success
    如果设置正常,查看刚刚开启的ssh界面,会发现有大段输出
    child-process-out

  3. 这时我们查看网站对应的文件,会发现已经更新了!这时设置好对应的网站服务,就可以正常访问我们的网站了。

  4. 要注意的是,临时开启的node进程会经常自己断掉,我们最好需要用pm2来管理node服务,PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务。 全局安装pm2:

npm install -g pm2

开启进程服务

pm2 start webhook.js

像这样status为online就是在正常运行了

webhook-pm2

webhook会监听我们的代码推送,所以现在只用Git推送就可以轻松地部署我们的项目啦,大工告成!