背景
公司从当初的SVN代码版本控制,FTP手动上传项目代码zip压缩包,到如今的git代码版本控制,jenkins一键打包部署环境,已经初步完成了手动到自动的大跃进.回过头来看看自己的项目,还处在本地仓库修改代码 -> 提交远程github仓库 -> 自己上服务器手动pull最新分支代码的原始阶段.不能忍
OK,接下来让我们开始我们的进化偷懒之旅,大家一起跟随我的心路历程一起进化.
目标
当我们本地仓库修改完成push远程仓库之后, 服务器能够自动拉取最新分支代码,自动完成项目部署.
前置条件(废话)
- 有个本地仓库能够连接到远程仓库,能够push代码
- 服务器仓库能够从远程仓库pull代码
- 远程仓库有webhooks功能
行动
工欲善其事必先利其器,开始行动前有必要理解一波webhooks钩子自动部署原理;
webhooks自动部署原理
本地仓库 -> (push提交代码) -> 远程仓库(webhooks钩子) -> (发送带有key的post请求) -> 测试/生产服务器(执行部署脚本)
知悉了原理之后我们来看看我们需要准备些什么:
- 带有webhooks的远程仓库(gitlab,github,gitee等等)
- 能够接收post请求的服务和测试/生产服务器
- 部署脚本(.sh)
远程仓库webhooks设置
远程仓库的webhooks设置你只需要找到具体位置点进去:
-
URL: 设置post请求的地址(即服务器服务地址)
-
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 // 查看目录文件确认依赖是否安装
确认安装后可以通过以下步骤解决:
- 进入
deploy.js
所在目录 - 执行以下命令
npm link github-webhook-handler
现在,你需要做的是将auto_build.sh
和deploy.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
安装:
forever
就是保证进程退出时,应用会自动重启。
forever start deploy.js // 启动服务进程
forever list // 列出所有进程
forever logs id // 查看进程输出日志
forever stop id // 停止服务进程
forever restart id // 重启服务进程
forever -m 5 deploy.js // 最多重启次数为5
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,大大减少人力成本,减少人为错误的发生,提高大家的工作效率.