当前位置 : 首页 » 文章分类 :  开发  »  Git-WebHooks钩子

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


上一篇 Apache-Commons-Pool 使用笔记

下一篇 Flask

阅读
评论
2.7k
阅读预计12分钟
创建日期 2019-08-28
修改日期 2020-02-16
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论