都9012年了,你还在手动部署代码吗

8,248 阅读6分钟

背景

公司从当初的SVN代码版本控制,FTP手动上传项目代码zip压缩包,到如今的git代码版本控制,jenkins一键打包部署环境,已经初步完成了手动到自动的大跃进.回过头来看看自己的项目,还处在本地仓库修改代码 -> 提交远程github仓库 -> 自己上服务器手动pull最新分支代码的原始阶段.不能忍

OK,接下来让我们开始我们的进化偷懒之旅,大家一起跟随我的心路历程一起进化.

目标

当我们本地仓库修改完成push远程仓库之后, 服务器能够自动拉取最新分支代码,自动完成项目部署.

前置条件(废话)
  1. 有个本地仓库能够连接到远程仓库,能够push代码
  2. 服务器仓库能够从远程仓库pull代码
  3. 远程仓库有webhooks功能

行动

工欲善其事必先利其器,开始行动前有必要理解一波webhooks钩子自动部署原理;

webhooks自动部署原理
本地仓库 -> (push提交代码) -> 远程仓库(webhooks钩子) -> (发送带有key的post请求) -> 测试/生产服务器(执行部署脚本) 

知悉了原理之后我们来看看我们需要准备些什么:

  • 带有webhooks的远程仓库(gitlab,github,gitee等等)
  • 能够接收post请求的服务和测试/生产服务器
  • 部署脚本(.sh)
远程仓库webhooks设置

远程仓库的webhooks设置你只需要找到具体位置点进去:

  1. URL: 设置post请求的地址(即服务器服务地址)

  2. key: 这个key可以设置也可以不设置,建议设置,防止他人随意请求服务器接口然后自动疯狂拉代码部署,相当于一个验签

部署脚本auto_build.sh

部署脚本(.sh)就自由发挥,自己平时怎么手动部署的就咋写就完事了,创建文件auto_build.sh.Linux下创建目录使用mkdir 目录名,创建文件使用touch 文件名.

PROJECTNAME_PATH = '/usr/local/src/项目目录';

echo "Starting deployment"
cd $PROJECTNAME_PATH
git checkout .
git pull
npm i
gulp release
pm2 start ecosystem.config.js
echo "Finished"

执行脚本的时候注意一下用户权限的问题以及基本命令的全局安装.

编写服务deploy.js

接下来的重头戏就是构建起一个能够接收远程仓库post请求的服务,这同样也很简单.你可以借助插件github-webhook-handler 的帮助,快速建立起这样一个服务,创建文件deploy.js.

ps: 这里的secret就是上面webhooks设置中的key

var http = require('http')
var createHandler = require('github-webhook-handler')
var handler = createHandler({ path: '/webhook', secret: 'myhashsecret' })

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

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

handler.on('push', function (event) {
  console.log('Received a push event for %s to %s',
    event.payload.repository.name,
    event.payload.ref)
})

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)
})

ps: 还可以设置当有人给自己仓库提issues时发邮件提醒自己23333333

如果服务无法启动,报错类似Error: Cannot find module 'github-webhook-handler',可是依赖包明明已经全局安装过了.确认方法:

npm root -g							// 查看npm全局安装路径
=> /root/.nvm/versions/node/v9.10.1/lib/node_modules

cd /root/.nvm/versions/node/v9.10.1/lib/node_modules
ll 									// 查看目录文件确认依赖是否安装

确认安装后可以通过以下步骤解决:

  1. 进入deploy.js所在目录
  2. 执行以下命令
npm link github-webhook-handler

现在,你需要做的是将auto_build.shdeploy.js结合起来.

阅读上面代码,你会发现handler监听到push事件调用对应的函数,所以你要做的就是在函数中执行auto_build.sh命令,你需要在deploy.js添加以及更改如下代码

// 新增runCmd函数
funciton runCmd(cmd, args, callback) {
    var spawn = require('child_process').spawn;				// node子进程
    var child = spawn(cmd, args);
    var response = '';
    
    child.stdout.on('data', buffer => response += buffer.toStirng());
    child.stdout.on('end', () => callback(response));
}

// 修改push监听事件 我这里auto_build.sh和deploy.js位于同一目录文件中
handler.on('push', function(event) {
   runCmd('sh', ['./auto_build.sh'], function(text) { console.log(text) }); 
});

ps: 可以通过console.log()在相应的步骤输出相应提示,方便查错

运行服务deploy.js

我们希望deploy服务能够一直运行在服务器上,当远程仓库发送post请求提示我们有新代码push的时候能够正常执行部署脚本.这时我们需要以守护进程的方式来启动deploy.js服务,当服务意外崩溃时能够重启服务,彻底解放我们的双手.

这里提供两种方法供大家选择,都可以通过npm安装:

  1. forever就是保证进程退出时,应用会自动重启。
forever start deploy.js		   				  // 启动服务进程
forever list								  // 列出所有进程
forever logs id								  // 查看进程输出日志
forever stop id								  // 停止服务进程
forever restart id							  // 重启服务进程
forever -m 5 deploy.js						  // 最多重启次数为5
  1. pm2功能强大,除了重启进程以外,还能实时收集日志和监控。
pm2 start deploy.js		   				  // 启动服务进程
pm2 list								  // 列出所有进程
pm2 logs id								  // 查看进程输出日志
pm2 stop id								  // 停止服务进程
pm2 restart id							  // 重启服务进程
pm2 delete id							  // 删除服务进程
Nginx反向代理

因为我的服务器在腾讯云上面,7777端口并未放开,所以通过一个Nginx反向代理到服务器安全组开放端口.

在远程仓库发送post测试请求前一定要确认

自己服务器安全组端口已放开!!!

自己服务器安全组端口已放开!!!

自己服务器安全组端口已放开!!!

重要的事情说三遍! 下面是我nginx配置

server {
	listen 8080;
    server_name	localhost;
    
    location /webhook {
        proxy_pass http://127.0.0.1:7777;
    }
}

ps: Linux下重启nginx,进入nginx的sbin目录运行命令./nginx -s reload

Bug

Nginx启动 ! deploy服务启动 ! 远程仓库webhook设置完毕 ! 点击测试按钮 !

然后......

报错!!! (强颜欢笑and笑容逐渐消失.jpg

{
  "error": "No X-Hub-Signature found on request"
}

看了半天......才想起来我的项目代码远程仓库是码云gitee.com,因为github私人private仓库2019年2月之前都是需要付费的,所以涉及私人的项目代码我都选择了码云作为远程仓库,然而我的插件是github-webhook-handler!!!!能通才有鬼 TAT

没关系,小问题.都到这一步了,男人怎么能说不行!

先去github上搜下有没有对应gitee的webhook插件,要是没有就forkgithub-webhook-handler下来自己改下改成适配gitee码云的.果然让我搜到了gitee-webhook-middleware,然后deploy.js改一改

var createHandler = require('gitee-webhook-middleware');
var handler = createHandler({ path: '/webhook', token: '你的key' });

重启服务,点击测试 !

{
  "ok": true
}

完美 !

上服务器一看项目代码还是旧的,妈耶还有坑...后来发现gitee的post请求事件是Push Hook

// handler.on('push', function() {});
handler.on('Push Hook', function() {});

至此,重新测试,项目代码更新成功 !

结果

偷懒成功! 偷懒果然是工程师第一生产力

现在每当我本地仓库push代码到远程仓库,服务器就会拉取最新版本代码自动部署.

这只是我一次简单尝试,我们完全可以扩展自动化测试,自动化部署于一体,完成多人协作开发时的CI/CD,大大减少人力成本,减少人为错误的发生,提高大家的工作效率.