Git-WebHooks钩子
git webhooks实现自动部署
8.3 自定义 Git - Git 钩子
https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90
需求
我这里需求场景是这样的:
1、自己的 hexo 博客源码托管在 github 上,博客部署在自己的 vps 服务器上,我想要的是: 每次写完博客 git push
推送到 github 后,都能触发自动在 vps 服务器上把最新的博客源码 git pull
拉取下来并自动 hexo g
生成博客内容,实现自动部署博客。
之前都是每次推到github后,再登陆到vps再手动 git pull && hexo g
,太麻烦了。
2、目前给博客配了两个后台服务,一个 disqus 评论服务, 一个 statistic 访问量统计服务, 都是 spring boot 项目, docker 部署,想要每次推送代码到 github 后,在 vps 服务器实现自动部署,类似一个 Jenkins。
要实现这个需求就需要用到 git 的 webhooks 了。
webhooks 需要有个响应 http 请求的 web 服务器,实现方式有很多种,比如简单的用 python 或 nodejs,可以很快部署一个 web 服务响应请求并执行 shell 脚本,复杂的用 java springboot 也可以搞一套,写起来也很快但太重了没必要。
python 和 nodejs 我都试了试,写起来都很方便,node版的很快调试好了,python的就比较蛋疼,一堆问题。最终选择用node作webhooks。
node.js实现webhooks
nodejs 安装参考笔记 Node.js
WebHooks实现hexo博客自动部署
新建一个 webhooks.js 文件,内容如下:
// nodejs启动一个http服务监听1121端口响应webhook
var http = require('http');
var exec = require('child_process').exec;
http.createServer(function (request, response) {
console.log("收到 http 请求, 方法:", request.method, "path:", request.url);
// 解决中文乱码问题
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
response.write(request.url + " ");
// hexo 项目
if(request.url === '/pull/hexo') {
// 异步执行shell命令,拉取最新代码,生成hexo博客内容,输出重定向到文件
exec("cd /home/centos/git/hexo && git pull && hexo g 1>/home/centos/hexo-deploy.log 2>&1", function(err,stdout,stderr) {
if(err) {
console.log("git pull && hexo g 执行失败: ", err, stderr);
// response.write('Git pull error: ' + stderr);
} else {
// 这个stdout的内容就是shell结果
console.log("git pull && hexo g 执行成功: ", stdout);
// 由于 shell 执行较慢会造成http超时,不再向response中写入shell执行结果
// response.write('Git pull done. ' + stdout);
}
// 如果想返回shell执行结果,response.end() 必须放在回调函数内,否则回调时主进程已返回,再向response写入会报错
// response.end();
});
console.log("git pull && hexo g 开始执行,主进程返回");
response.write("git pull && hexo g 开始执行,主进程返回");
response.end();
}
}).listen(1121);
console.log("Node.js http webhooks 服务已启动.");
代码很简单,稍微解释下:
- 对外暴露了一个 http 接口,监听 1121 端口,根据 url 中的 path 进行匹配,执行命令上线拉取代码并生成博客内容。
exec
方法是异步执行的,cd
进入目录和git pull
必须在同一个exec
中,不能分两步开两个子进程操作。- 还是因为
exec
方法是异步执行的,如果想在 response 中看到命令执行结果的话,response.end()
必须放在exec
的回调函数内,不能放在主线程中,否则会出现向已end的response中写入错误。 - 一开始在response中返回了命令执行结果,后来因为
hexo g
命令执行时间较长,会造成http响应超时,主进程直接返回了,不再等待命令执行完。
后台运行nohup node /home/centos/git/hexo/nodejs/webhooks-centos.js &
在vps本地测试
curl 127.0.0.1:1121/pull/hexo
或
curl localhost:1121/pull/hexo
$ curl localhost:1121/pull/hexo
Git pull done. 已经是最新的。
webhooks+springboot+docker实现SpringBoot自动容器部署
为了实现在服务器上 pull 代码,打jar包,需要安装 git, java 和 maven, 参考笔记 AWS Lightsail CentOS7 使用记录
安装 docker 环境,以及 spring boot 项目的 docker 容器化参考笔记 Docker
doker_deploy.sh 脚本
#!/bin/bash
set -ex
cd /home/centos/git/masikkk
# 拉取最新代码
echo "`date "+%Y-%m-%d %H:%M:%S"` 拉取最新代码 >>>>>>>>>>>>>>>>>>>>>>>>>>"
git pull
# maven 打包所有子模块
echo "`date "+%Y-%m-%d %H:%M:%S"` 开始 maven 打包 >>>>>>>>>>>>>>>>>>>>>>>>>>"
mvn package
# 构建 statistic 项目 docker 镜像
echo "`date "+%Y-%m-%d %H:%M:%S"` 开始构建和部署 statistic 服务 >>>>>>>>>>>>>>>>>>>>>>>>>>"
cd statistic
docker build -t statistic .
# 停止当前 statistic 容器,用新打的镜像启动容器
docker ps
docker stop statistic
docker run -d --rm \
--network host \
--name statistic \
-v /data/log/spring:/data/log/spring \
-e "SPRING_PROFILES_ACTIVE=prod" \
statistic
# 构建 disqus 项目 docker 镜像
echo "`date "+%Y-%m-%d %H:%M:%S"` 开始构建和部署 disqus 服务 >>>>>>>>>>>>>>>>>>>>>>>>>>"
cd ../disqus
docker build -t disqus .
# 停止当前 disqus 容器,用新打的镜像启动容器
docker ps
docker stop disqus
docker run -d --rm \
--network host \
--name disqus \
-v /data/log/spring:/data/log/spring \
-e "SPRING_PROFILES_ACTIVE=prod" \
disqus
echo "`date "+%Y-%m-%d %H:%M:%S"` 全部任务完成 >>>>>>>>>>>>>>>>>>>>>>>>>>"
此脚本实现 拉取最新代码, maven 打jar包, 构建 statistic 和 disqus 项目的最新 docker 镜像, 关闭旧容器并启动新容器。
注意: 需要给启动 node 进程的用户添加脚本的可执行权限,否则在node中执行脚本会报无权限错误。
如果node用户是文件的拥有者,给文件拥有者添加脚本的可执行权限chmod u+x doker_deploy.sh
如果node用户非文件拥有者,给所有用户添加可执行权限chmod a+x doker_deploy.sh
// nodejs启动一个http服务监听1121端口响应webhook
var http = require('http');
var exec = require('child_process').exec;
http.createServer(function (request, response) {
console.log("收到 http 请求, 方法:", request.method, "path:", request.url);
// 解决中文乱码问题
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
response.write(request.url + " ");
// masikkk 项目
if(request.url === '/pull/masikkk') {
// 异步执行shell脚本, 如果需要输出重定向,必须使用 exec, execFile 不支持
exec("sh /home/centos/git/hexo/nodejs/doker_deploy.sh 1>docker-delploy.log 2>&1", function(err,stdout,stderr) {
if(err) {
console.log("doker_deploy.sh 脚本执行失败", err, stderr);
// response.write('Git pull error: ' + stderr);
} else {
console.log("doker_deploy.sh 脚本执行成功")
// 这个stdout的内容就是shell结果
// console.log("doker_deploy.sh 脚本执行成功" + stdout);
// 由于 shell 执行较慢会造成http超时,不再向response中写入shell执行结果
// response.write('Git pull done. ' + stdout);
}
// 如果想返回shell执行结果,response.end() 必须放在回调函数内,否则回调时主进程已返回,再向response写入会报错
// response.end();
});
console.log("doker_deploy.sh 开始执行,主进程返回");
response.write("doker_deploy.sh 开始执行,主进程返回");
response.end();
}
}).listen(1121);
console.log("Node.js http webhooks 服务已启动.");
解释下
- 使用
exec
异步执行shell脚本,由于打包部署时间较长,主进程直接返回,不等待执行结果输出。 - doker_deploy.sh 脚本的执行重定向到 java-delploy.log 文件,由于需要用到输出重定向,必须使用
exec
,不能用execFile
- 启动 node 进程的用户必须有脚本的可执行权限,否则会报错 Error: spawn doker_deploy.sh EACCES
配置nginx转发
如果服务器不想对外暴露监听的接口,可以统一通过 nginx 网关转发请求
nginx 增加一段配置,监听 webhooks.devgou.com 三级域名,转发到本地的1121端口上。
# webhooks.开头的三级域名访问, git的webhooks, 转发到后台node.js web服务
server {
listen 80;
server_name webhooks.devgou.com;
location / {
proxy_pass http://localhost:1121;
}
}
然后还要在自己的域名解析处配置主机记录webhooks,解析到服务器ip,用于git钩子
此后就可以通过下面的链接触发了
http://webhooks.masikkk.com/pull/image
遇到的问题
sudo启动web服务导致git Permission denied
如果启动时加sudo,也就是以 root 账号启动sudo nohup node webhooks.js &
shell 在执行 git pull
时会报 Permission denied 错误
Permission denied (publickey).
fatal: 无法读取远程仓库。
请确认您有正确的访问权限并且仓库存在。
这是因为 ssh 私钥文件的拥有者是当前的非root账户(在我的aws上是ec2-user账号),如果以 root 账户执行 git pull
无法读取到私钥文件,所以导致 Permission denied
解决方法就是以 非root账户启动 nodejs web 服务。
中文乱码问题
如果直接在浏览器中访问 刚才的 git webhooks 链接,可能出现中文乱码问题,这是因为 node 里没给 response 设置编码格式
加上下面这句即可
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
Node.js中文乱码解决
https://www.jianshu.com/p/b9ae0c5e642f
无/var/lib/nginx权限导致请求体较大时500
conding 中后每次push代码总是回调 webhooks 接口失败,但我自己通过 curl 或浏览器访问就能成功, 看了看 nginx error 日志
2019/08/29 16:38:52 [crit] 9611#0: *6500 open() "/var/lib/nginx/tmp/client_body/0000000012" failed (13: Permission denied), client: 118.25.132.68, server: webhooks.devgou.com, request: "POST /pull/madaimeng HTTP/1.1", host: "webhooks.devgou.com"
网上搜了搜,原因大概是,我自己访问时没有请求体,coding上回调时请求体很大,貌似把整个diff内容都发过去了,估计 nginx 会用 /var/lib/nginx
这个目录存储较大的请求体,然后没权限就报错了。
解决方法sudo chmod -R 775 /var/lib/nginx
python实现webhooks
Python 编写 Github Webhook (原生http server)
https://juejin.im/entry/57e14806816dfa006709862c
使用开源webhook
adnanh / webhook
https://github.com/adnanh/webhook
使用 webhook 自动更新博客
https://blog.cugxuan.cn/2019/03/23/Git/Use-Webhook-To-Update-Blog/
rvagg / github-webhook-handler
https://github.com/rvagg/github-webhook-handler
https://blog.winsky.wang/Hexo博客/自动将更新部署到VPS/
git中配置webhooks
github中配置webhooks
给github项目配置webhooks
GitHub配置webhooks
Payload URL 填 webhooks地址
Content type 不用管,因为任何type都会处理
监听事件选择 push
coding中配置webhooks
给coding项目配置webhooks
Coding配置webhooks
URL 填 webhooks地址
内容类型 不用管,因为任何type都会处理
监听事件选择默认的 push 和 mr
下一篇 Flask
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: