【28】博客自动化部署

前情提要

【21】博客搭建笔记中,我记录了搭建现在这个博客的过程。为了便于管理,我现在把网站的全部文件用 git 做管理,并上传到 Github 上的一个 private repo 里。这样一方面便于版本管理,另一方面也可以省的每次有改动都要开 FileZilla 传一遍。每次有改动或者发布新文章,只需要推上 Github,然后 SSH 进服务器拉下来重新 hugo 一下就可以。

还是麻烦!

虽然说我写了个小脚本可以一键完成 git pullhugo,但是每次都还是要 SSH 上去,外网的 ec2 连接又总是延迟高还不稳定。既然这么麻烦,干脆搞个自动化部署吧。

思路

在每次 push 事件发生后,让 Github 向一个特定的接口发送一个通知;服务器则配置这么一个接口用于监听事件,一旦收到 push 的通知就运行脚本自动部署。

工具

webhook:一个用 Golang 编写的轻量级 webhook 工具。可以在服务器端特定端口监听,接收到符合要求的请求后执行指定的命令。

webhooks:一项 Github 自带的功能,在 repo 的 Settings -> Webhooks 选项中可以找到相关的设置项。可以在每次 push 事件发生后,让 Github 向一个特定的接口发送一个 HTTP POST 请求。

要注意此 webhook 非彼 webhooks,前者是一个正好叫 webhook 的 webhook 工具,后者则是一项 Github 的 webhook 功能。

配置

以下配置参考了这两篇博客:

Deploy using GitHub webhooks by @awea

Setting up Automatic Deployment and Builds Using Webhooks by Will Browning

安装

直接使用 APT 安装:sudo apt install webhook

更多安装方式参考 webhookREADME

Service

使用 APT 安装,会自动把 webhook 配置成一个 systemd 服务。我在这里做过一些相关解释。要更改 webhook 的运行指令,需要更改 /lib/systemd/system/webhook.service 这个脚本中的内容。因为只需要更改启动指令,因此只要更改 [Service] 项下的 ExecStart 即可。

1
2
[Service]
ExecStart=/usr/bin/webhook -nopanic -hotreload -hooks /etc/hooks.json

相较于默认配置,这里我添加了热加载 -hotreload 选项,并把配置文件位置改成了 /etc/hooks.json

更多命令行参数的配置,可以参考文档

hooks.json

直接编辑 /etc/hooks.json 或者在其他地方编辑后软链接到对应位置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[
  {
    "id": "my-blog-deployment-webhook",
    "execute-command": "/path/to/your/script/update.sh",
    "command-working-directory": "/path/to/your/script/",
    "response-message": "Executing deploy script...",
    "trigger-rule":
    {
      "and":
      [
        {
          "match":
          {
            "type": "payload-hash-sha1",
            "secret": "<RANDOM-SECRET-STRING>",
            "parameter":
            {
              "source": "header",
              "name": "X-Hub-Signature"
            }
          }
        },
        {
          "match":
          {
            "type": "value",
            "value": "refs/heads/master",
            "parameter":
            {
              "source": "payload",
              "name": "ref"
            }
          }
        }
      ]
    }
  }
]

其中,id 是这个接口的名字,excute-command 是运行部署脚本的指令,command-working-directory 是执行脚本的位置,response-message 是回复 HTTP POST 的消息。最后还需要更改下面的 secret 项,里面要填写一个 足够长足够随机 的字符串作为密钥。

更多参数配置参考文档示例

NGINX

根据文档webhook 会在 http://0.0.0.0:9000/hooks/{id} 这个接口上监听(按照上面的示例就是 http://0.0.0.0:9000/hooks/my-blog-deployment-webhook)。虽说官方提供了 HTTPS 支持,但是需要另外指定证书,而且还要到控制台安全组放开 9000 端口,就很麻烦,还让人觉着不安全。

既然我们前面已经配好了 NGINX 和 HTTPS,不如就继续用 NGINX 转发请求好了。在 server 块中添加这个 location:

1
2
3
location /hooks {
	proxy_pass http://127.0.0.1:9000;
}

于是就非常轻松的把 /hooks 为开头的 uri 请求转发到了 webhook 的接口上。这样一来,外网访问就可以直接使用 443 端口的 https://your-domain-name/hooks/{id} 作为 webhook,HTTPS 用的就是之前为域名配置的证书啥也不用改,而服务器内部 NGINX 会把请求再转发到在 9000 端口监听的 webhook。(反正是内部转发所以用 HTTP 也无所谓)

这里 NGINX 的配置参考了这篇教程:nginx 关于 uri 的截取

部署脚本

部署脚本本应该很简单,不就是 git pull 一下,然后 hugo 一下完事。不过用了以后发现不是这么回事,webhook 在调用脚本的时候使用了 root 用户,这使得一切都不一样了。由于使用了 snap 来安装 Hugo,在 root 身份下无法执行 hugo 命令;输出 log 文件的时候,log 文件会是以 root 身份创建的,当回到原本的 ubuntu 身份手动执行脚本的时候就无法写入这个 log 文件了。(手动改文件权限当然可以,但是非常不鲁棒)

另外,在 hook.json 中的 excute-command 里直接重定向输出行不通,需要在脚本里直接搞定。(套两个脚本当然也是可以,但是看着令人不爽)

查阅资料以后我写了如下脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# Open a shell with ubuntu as user
sudo -i -u ubuntu bash << EOF

# Redirect output
exec >> /path/to/log/deployment.log
exec 2>&1

echo "=============== Blog deployment ==============="
echo $(date "+%Y-%m-%d %H:%M:%S") UTC
echo

# Start doing some real stuff
cd /path/to/hugo/blog/
git pull origin master
rm -r public
rm -r resources

echo
hugo
echo

echo "==============================================="
echo

EOF

这里使用了 sudo -uHere Document 创建了一个身份为 ubuntu 的 shell,然后在里面执行命令。还使用了 exec >> fileexec 2>&1 在文件内实现输出重定向。

参考了以下资料:

How do I redirect the output of an entire shell script within the script itself?

How do I use su to execute the rest of the bash script as that user?

shell脚本中使用其他用户执行脚本

Github webhooks

最后就是在 Github repo 里设置对应的 webhook 事件啦。到 Settings -> Webhooks 中新建一个 webhook,Payload URL 就是你定义的接口;Content type 选择 application/jsonSecret 填入之前在 hooks.json 中填的那个 secretSSL verification 自然是选择启用;事件选择仅 push 事件。

设置完成以后,博客的自动化部署就最终配置完成了!

后续

webhook 是用 Golang 编写的,而 Hugo 也是 Golang 编写的,用过的人都知道速度那是真的快。Hugo 加上一样超快的 webhook,整个自动化部署的效率真的是巨高。完成全部配置以后,git push 完几秒钟就可以看到服务器端已经完成了建站操作,非常牛逼了。

Nobody:“自动化是解放生产力的必经之路。”


本文阅读量
本站访客量