Hexo博客部署架构
Hexo 博客部署架构

说是自动化博客部署,其实是记录了下 枋柚梓的猫会发光 的部署过程,基本概括来说就是通过 Github Action 处理代码的提交,部署博客到 Github、Gitea、COS 中,此外还包含一些 GPG 的签名提交、自托管主机遇到的一些坑和一些其它功能的使用过程。

如你所见,这个部署流程是繁杂的,如果只是将 Hexo 博客部署到 Github、COS、CVM 等,其实都有对应现成的轮子可以用。不过出于个人定制的缘故,我将源码发送到了多个位置:整站内容部署到服务器、Github 仓库;静态文件(js,css,fonts)单独同步到另一个 Github 仓库,并且使用 Jsdelivr 加速访问静态库;而图片类文件则同步到腾讯云对象存储,以方便使用它的图片处理功能(自适应 Webp 自适应、Guetzli 自适应等)。

同时,强迫症心理作祟 ,各个环节都用上了 GPG 签名;再同时还是强迫症的缘故,网站的域名解析几乎不对境外开放(只有 inkss 的域名做了全球解析),所以又用上了 Github Actions 的自托管主机;再然后还自建了 Gitea 的代管托管站,又将整站同步一份到 Gitea 里。


本文包括但不限于以下内容:

  • SSH 私钥提交
  • GPG 部署与代码签名
  • Github Action 基本语法规则
  • Github Action 自托管主机使用
  • Gitea 的 Webhook 及本地代码同步
  • 利用 Gulp 批量替换静态资源访问地址
  • 腾讯云 COS 部署及 CDN 缓存刷新

一、SSH & GPG

使用 SSH 连接到 Github

这部分是基础的基础,不作详细解释,大致的流程就是:生成密钥,然后将公钥上传到 Github,完了齐活儿~ 可以参考此链接操作:使用 SSH 连接到 Github

使用 GPG 对提交进行签名

简要介绍一下 GPG :你以为我会大段的复制粘贴?并不那是无意义的。B 站有一个视频对 RSA 加密讲述的很好,排除中间大段的算法部分它很形象的概括了现有的加密算法:【软件科普】详解RSA加密算法 。此外对于 GPG 对 Git 提交进行签名的原理可以在此链接处查看:PGP 工作原理简述

至于为什么要使用 GPG 可以这样理解,由于 Git 的身份认证只需要配置用户名和邮箱,且这俩可以任意修改,甚至 Git 的 commit 都可以批量修改,那么有人伪造提交记录,Github 也是无法分别的。所以需要一个我证明是我自己的过程,也就是利用 GPG 对一次提交记录进行签名,Github 利用你上传的公钥对签名解密,验证通过后就可以相信对应的提交记录是你真实发出的。

总结就是 SSH 用来验证身份,GPG 用来验证 “著作权” 。

GPG 的安装

对于类 Uinux 用户,系统已经默认安装了 GPG ,直接使用即可;Windows 用户可以下载 GPG 的 命令行工具 ,当然最简单的还是利用 Scoop 安装:

Windows 安装 scoop
其实 sudo、which 等基础命令也能利用 Scoop 安装
scoop install gpg

GPG 的使用

安装完成之后就是生成 GPG 密钥对,生成过程参考此链接操作:生成新 GPG 密钥

GPG 的导入导出

如果已有 GPG 密钥是可以直接导入,这点比 SSH 密钥的导入导出方便很多,下面是查看系统中已有的密钥和对其导入导出的命令:

列出您拥有其公钥和私钥
列出您拥有的公钥和私钥,因为可以导入公钥验证身份,公钥可能会有多个
➜  ~ gpg --list-secret-keys --keyid-format=long 
/root/.gnupg/pubring.kbx
------------------------
sec rsa4096/705CF548160B570E 2021-07-25 [SC]
3B21B65778AD1331E052020D705CF548160B570E
uid [ultimate] inkss (inkss) <me@szyink.com>
ssb rsa4096/0C85BC5AF8B7CF09 2021-07-25 [E]
➜ ~ gpg --list-keys --keyid-format=long
/root/.gnupg/pubring.kbx
------------------------
pub rsa4096/705CF548160B570E 2021-07-25 [SC]
3B21B65778AD1331E052020D705CF548160B570E
uid [ultimate] inkss (inkss) <me@szyink.com>
sub rsa4096/0C85BC5AF8B7CF09 2021-07-25 [E]

其中,ses 和 pub 后跟的字符串为对应 ID,例如本例中 GPG 密钥 ID 为 705CF548160B570E

导出私钥和公钥
导出私钥时需要验证私钥密码
➜  ~ gpg -a -o gpg-public.key --export 705CF548160B570E
➜ ~ gpg -a -o gpg-private.key --export-secret-keys 705CF548160B570E

相应的导入密钥命令如下,同样导入私钥时需要输入密码。

导入公钥和私钥
导入私钥/公钥到系统中
➜  ~ gpg --import gpg-public.key 
gpg: key 705CF548160B570E: public key "inkss (inkss) <me@szyink.com>" imported
gpg: Total number processed: 1
gpg: imported: 1
➜ ~ gpg --import gpg-private.key
gpg: key 705CF548160B570E: "inkss (inkss) <me@szyink.com>" not changed
gpg: key 705CF548160B570E: secret key imported
gpg: Total number processed: 1
gpg: unchanged: 1
gpg: secret keys read: 1
gpg: secret keys imported: 1

GPG 的 Git 调用

当密钥导入到系统后,需要配置 Git 命令调用 GPG 签名:

配置密钥并开启全局签名验证
配置 GPG 密钥 ID 并开启全局 GPG 签名验证
➜  ~ git config --global user.signingkey 705CF548160B570E
➜ ~ git config --global commit.gpgsign true

添加 GPG 公钥至 Github

我们需要将公钥上传到 Github,以方便其对签名信息解密,可以参考此链接操作:新增 GPG 密钥到 GitHub 帐户

二、Github Action

流程

如上图所示我将部署流程拆分成了三部分:分发、同步和备份,分发和同步通过 Ubuntu-last 执行,备份通过自托管主机执行,下面根据各个要点逐一展开。

部署博客 - CVM

部署博客源码到服务器

这一流程的主要目的是将生成的网站源码部署到服务器的网站目录中,这里呢其实最简单的就是直接利用 SSH 将文件发送到服务器,涉及的插件是:ssh-deploy,基本的 Action 命令可以这样写:

ssh-deploy 的使用
完全版本:https://github.com/easingthemes/ssh-deploy#example-usage-in-workflow
- name: Deploy to Server
uses: easingthemes/ssh-deploy@v2
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: public/
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_PORT: ${{ secrets.REMOTE_PORT }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.TARGET }}

复杂点就是先将网站源码部署到 Github 仓库,然后利用 WebHook 触发服务器,服务器再执行克隆命令,从代码仓库中拉取最新内容,服务器端执行的克隆命令大致如下:

宝塔 Webhook 命令
修改 gitHttp 为自己的仓库地址
#!/bin/bash

if [ ! -n "$1" ];
then
echo "param 参数错误"
exit
fi

gitPath="/www/wwwroot/$1"
gitHttp="https://gitea.szyink.com/szyink/blog.git"

if [ -d "$gitPath" ]; then
cd $gitPath
git fetch --all
git reset --hard origin/main
chown -R www:www $gitPath
exit
else
echo "该项目路径不存在"
exit
fi

部署博客源码到托管仓库

由于我自建了 Gitea 代码托管,秉承着不能浪费的原则:不如将一份博客源码同步到 Gitea 仓库 ,然后顺带还可以同步进 Github 仓库,这里用到的工具是 hexo-deployer-git,基本的 Action 命令可以这样写:

部署博客到 Git 仓库
https 是走 token 验证,git 是走 ssh 验证
# Deployment 部署
deploy:
type: git
repo:
gitee:
url: https://gitea.szyink.com/szyink/blog.git
token: '************************************'
branch: main
github:
url: git@github.com:inkss/inkss.github.io.git
branch: main

Github Action 自托管主机

但是,由于心理洁癖的缘故,除了 inkss.cn 的域名,其它的几个域名没有对境外解析,换句话就是执行 Github Action 的服务器联系不到我的 Gitea 网站,我又修改不了它的 hosts 文件,所以一不做二不休,整自托管主机。关于将自托管主机的部署可以查看这篇文档:添加自托管的运行器,如此在配置文件中,运行环境就需要更改成自己的主机:runs-on: self-hosted

接下来就是踩坑的血泪史了,自托管主机不像 Github 主机那样,每次运行都是新环境,需要特别留意环境的问题。假设你按照安装流程并且没有修改路径的话,那么它的运行路径为:

基础运行地址
Hexo-Blog 是我的仓库名称
~/actions-runner/_work/Hexo-Blog/Hexo-Blog/

每一次执行 Action 时,这个目录中除了 .github\workflows 外其余内容都会被清空;然后是环境变量,在安装 Github Action 的时候是不允许 root 或者 sudo 下执行命令的,所以原则上你 Action 的环境变量应该是当前用户的环境变量,可是离谱的事情发生了:在终端打印 $HOME 时,读出的内容是 /root/,而 $PATH 只有系统的环境变量。

这简直是坑惨了人,明明 whoamipwd 得到的都是普通用户下的路径和名称,但是这些变量就是不对,最明显的便是 Node 环境找不到,一个劲儿的去往 root 的目录读, 然后没有权限不停的报错。 这一点我是花了很长时间才发现,尤其是 HOME 指向 root 这一点,离谱!

解决方案就是重写环境变量:

为 Job 配置公共环境变量
相当离谱,试错了太多次了就是因为这个
jobs:
buildToGit:
runs-on: self-hosted
name: '部署博客 - CVM'
env:
PATH: /home/szyink/.nvm/versions/node/v14.17.3/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/
HOME: /home/szyink
GPG_TTY: $(tty)

部署博客 - COS

部署到对象存储就和我的域名没有关系了,此部分可以使用 Github 的主机,由于将部署到 CVM 和 COS 拆分成两个流程,那么配置文件自然要独立成两个,我在 Hexo 文档中看到关于多个配置文件整个使用的:

Hexo 多配置文件
使用 --options 指定了两个自定义配置文件
hexo generate --config custom.yml,custom2.json

但是实际体验中发现似乎这部署时并没有将 _config.yml_custom.yml 合并成一个,维护两个配置文件自然不显示,最终采用了将主配置文件中不保存 deploy 段,将其只放进子配置文件中,部署时在 Action 里用 >> 整合下使用。

部署博客 - 同步文件

其实这部分没什么好说的,就是克隆仓库,然后将最新的内容复制到仓库中,最后执行提交。

Workflows 文件

总的 Workflows 文件可以在这个链接处查看:Hexo-Blog/.github/workflows/main.yml

三、附录内容

GPG 的免密校验

在使用 GPG 对 Git 提交签名的过程中,每次都需要输入主密码对私钥进行认证,这类交互式程序个人使用还好,但是在 Action 这种无人值守的自动程序中就面临凉凉的问题了,我检索了一部分资料实在没找到如何在 Git 提交的过程中自动完成密钥验证。不过倒是找到了免密解密文件,由于密码有一定的缓存时间,那么在正式的 Git 提交前我只需要进行一次解密操作,后续一段时间内都无需输入密码,四舍五入下也算完成了免密提交。

大致流程是这样:首先就是利用公钥加密一份文件,然后在执行类似 Git 提交的操作前对加密后的文件进行一次解密,刷新密码的缓存时间。

利用 GPG 私钥解密公钥解密的文件
PASSWD 为写有密码的文件,szyink.gpg 为待解密文件
gpg -d --pinentry-mode=loopback --passphrase-file PASSWD ~/gpg/szyink.gpg

更新:可以通过 crazy-max/ghaction-import-gpg@v3 完成 GPG 密钥的部署及密码缓存。

是否要将 GPG 公钥提交

在一些博客教程中,都提到过将 GPG 公钥上传到公钥服务器中,但是我在读到 2021年,用更现代的方法使用PGP(下) 中的 安全风险和争议, 被玩坏的 KeyServer 后,忽然就意识到我完全没有将公钥发布出去的需求,它提供的加密功能又不会在生活中用到,最多最多只是用在 Github 之类的验证上,但是上传到公钥服务器就意味着永远无法删除,这不太好

Github Action 的运行目录

对于 Github 的主机来说,项目的运行目录位于(起码 Ubuntu 主机是这样),而自托管主机就需要看个人配置了,直接登陆到服务器查看它不更香嘛。

Git 主机的基础仓库路径
project-name: 仓库名称
~/work/project-name/project-name/

Gulp 替换静态资源链接

我的对象存储的访问域名是 static.inkss.cn ,在博文源码中关于图片的引用是利用 Typecho 按照相对路径导入的,形如:../../imgs/article/自动化博客部署/Hexo博客自动化部署.sv,所以如果需要将访问地址修改为静态站的,那就得批量修改类似的链接,这里采用了 Gulp 操作。

Gulp 除了能压缩文件外,还可以替换文本
利用 replace 替换文本内容
gulp.src(['./public/**/*.html','!./public/{lib,lib/**}'])
.pipe(replace('../../imgs/article', 'https://static.inkss.cn/imgs/article'))
)

统一 Commit message

在 Github Action 中,可以利用 ${{ github.event.head_commit.message }} 获取到触发本此流程的 message 信息,所以在提交时使用这个变量就达到替换的目的了,在类似于命令行中可以直接使用该变量:

获取原始提交信息
命令行中使用 Github 环境变量
git commit -m '自动化部署:${{ github.event.head_commit.message }}'

但是对于 hexo-deployer-git 来说,它的提交信息是配置在 yml 文件中的,可以借助 microsoft/variable-substitution 完成变量的替换,同样的操作还可以替换配置文件中的隐私变量:

利用 secrets 存储重要信息
替换配置文件中的指定内容
- name: '填充密钥'
uses: microsoft/variable-substitution@v1
with:
files: '_github.yml'
env:
deploy.repo.gitee.token: '${{secrets.GITEA_TOKEN}}'
deploy.message: '自动化部署:${{ github.event.head_commit.message }}'

Github Action 的守护进程

利用宝塔面板的 Supervisord 直接创建即可,也就是:

- command=/home/szyink/actions-runner/run.sh
- directory=/home/szyink/actions-runner/
- user=szyink

评论