这里主要介绍了博客建站的过程。

最初的想法

最初建立博客的时候也是基于Hexo发布,网上关于Hexo的教程大多数都是基于github pages,也有一些是部署到个人VPS上。

对于个人而言github pages无法使用自己的域名和服务器实在浪费,同时由于本日常人使用多台电脑,如果换台机器写博客则需要重新搭建hexo环境并从部署hexo的vps拉取代码,每次都需要花很多时间在网络上,也是得不偿失。因此想到通过github存储博客代码,同时每次push的时候代码可以自动化部署到服务器上面。

方案1:WebHook

第一种解决方案是基于WebHook。所谓WebHook,就概念而言可以延伸出很多东西,这里就具体例子而言。如果传统方法搭建网站,那么博客会在每天定时监测github代码是否已经更新,然后定期拉取github的代码;如果采用web hook的方法,当更新博客push到github的时候,github会通知VPS,VPS收到通知后才会去拉取代码。

Github实际上已经实现了WebHook的功能,比较麻烦的是你需要自己在VPS上写一个接口处理Github的通知,这种方法网上教程比较少但也不是没有,有些拿Python写一个单独的后端API接口,有些则由nginx监听推送,然后执行一个自己写的自动化脚本。

方案2: GitHub Actions

GitHub Actions是Github在2018年10月推出的持续集成服务,这里不需要深究持续集成的概念,只需要知道Github Actions运行在Github服务器的虚拟机上,可以通过运行脚本实现登录VPS、执行脚本、自动化部署等操作,而Github中已经有许多前人开发的脚本。

相较之WebHook,Github和VPS不再需要搭建Web服务以及进行各种网络调试,显然更加省心。

具体实施

Hexo的本地建立

建立hexo本地服务器需要node.js和git,自行参考安装教程解决,以下为安装hexo博客的脚本。

1
2
3
4
5
6
# copy from hexo.io
npm install hexo-cli -g
hexo init blog
cd blog
npm install
hexo server

hexo服务器运行正常之后,去 http://localhost:4000 测试是否可以正常访问。

Hexo部署到Github

这一部分可以参考大多数Hexo建立Github Pages的教程,需要注意Github Pages目前只支持公有仓库,私有仓库要加钱。

第一步,建立Github仓库,如果需要建立GitHub Pages,仓库名必须为 <用户名>.github.io 的固定格式,同时建议设置公有仓库。

第二步,设置Git的账号密码。由于Github在去年ban了账号密码登录,传统的 git config user.name xxx && git config user.email xxx && git config.user.password xxx 已经狗带了,因此这里比较推荐ssh免密登录。

1
2
3
4
git config --global user.name "xxx"
git config --global user.email "xxx"
ssh-keygen -t rsa -C "你的注册邮箱(github账号)"
cat /你的.ssh路径/你的ssh密钥名称.pub

然后根据官方指南 Adding a new SSH key to your GitHub account 将你的ssh公钥(后缀为.pub)加入到github的ssh keys中。

此外还有一种方法是通过Github Token登录,git config --global user.password "你的密码" 更改为 git config --global user.password "你申请到的token",申请Token可以参照官方教程 Creating a personal access token。注意Token是有时效和访问限制,如果设置错误或者时效过期只能重新申请。

Hexo设置Git部署

在hexo根目录_config.yml可以找到

1
2
3
4
5
6
deploy:
type: 'git'
repo: '你的github repo地址.git'
token: '如果有可以加'
branch: 'main'
message: 'git commit提交的信息'

注意现在由于黑命贵的影响,现在创建的Github仓库默认改成了main而不是master,很多旧的教程还在用master,以及我的SourceTree也是默认推送到master分支,目前hexo生成的_config.yml默认为main不需要更改,所以使用 hexo deploy 则不需要修改分支。

设置完成之后可以 hexo deploy 测试一下是否成功。

设置GitHub Actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# main.yml
name: deploy to aliyun
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install SSH private key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
known_hosts: placeholder

- name: Adding Known Hosts
run: ssh-keyscan -p ${{ secrets.SSH_PORT}} -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts

- name: Deploy with rsync
run: rsync --omit-dir-times -rltvz -e "ssh -p ${{ secrets.SSH_PORT }}" ./ ${{ secrets.SSH_ACCOUNT }}@${{ secrets.SSH_HOST }}:${{ secrets.WEB_PATH }}

这一部分脚本是根据 Zellwk的代码 修改的,很大程度上参考了 他的博客。secrets则是在 仓库>>Setting>>Secrets>>Actions 中建立,本质就是字符串代换,建立的secrets只能替代或删除,但无法查看。由于我之前建立的Github仓库是公有的,因此Github Actions的 main.yml 文件全部都是可见的,因此必须通过secrets隐藏ssh密钥和IP、端口、文件目录等敏感信息,保障了命令的安全性。

GitHub Actions都是运行在GitHub Actions Runner,Runner 是一个开源的虚拟机环境,因此cmd和其它虚拟机没有什么区别,惟一的好处是运行在Github自己的服务器上,有一系列大佬写好的一键部署脚本可供使用。on之后是触发的条件,这里的触发条件是有push到main分支。Jobs则是所需要完成的任务,Jobs之间可以用needs指定前要条件,而单一Jobs则由Steps组成,Steps的先后次序则是固定的。

第一步,uses: actions/checkout@v2,则是执行了git checkout的自动化脚本,将我们博客的代码拉到工作区,这些代码后面都要发送到VPS。

第二步,模拟本地免密登录VPS。回想一下我们本地免密登录Github都是将公钥分发给VPS,实际上每次登录都是本地使用私钥和VPS持有的公钥配对,在这个过程中只有公钥需要分发,私钥一般都是不会暴露在网络上,只能够用私钥打开公钥而不能反着来,因此这种加密被称作非对称加密。设想如果其它主机通过某种途径获取到这个私钥,它同样也可以通过私钥进行登录。第二步实际上也是在做这样的事,由于Runner是Github自动分配的,所以我们每次登录的IP地址都是不同的,所以我们通过Github的Secrets分发私钥。

如果我们是在本机第一次ssh免密登录,则会出现 The authenticity of host 'xxx' can't be established. 这样的警告,然后抛出 Are you sure you want to continue connecting (yes/no/[fingerprint])? Warning: Permanently added 'xxx' to the list of known hosts,脚本一般不知道怎么处理这件事,所以最后的任务就是将xxx添加到list of known hosts中。查看本机(MBP16)的 ~/.ssh/known_hosts 我们可以发现这些该文件格式为 [IP地址:端口号 SSH-2.0-OpenSSH_8.0 一大坨hash值],利用 ssh-keyscan 生成相应格式代码并添加,即可完成该任务。

第三步,则是通过rsync同步文件。rsync 是Linux内置的文件同步工具,主要用法为 rsync local-file user@remote-host:remote-file,作用是同步两端相似的目录结构和内容,相比起其他复制方法,rsync不会复制已有的内容,通过差分对比发现远端缺失的内容,将源服务器内容复制到目标主机中。rsync可以实现代码的增量备份,将github中增加的代码拷贝到VPS上,而诸如评论等VPS上自己生成的内容不受影响。

Zellwk的代码为 rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" ./dist/ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/path/to/your/directory/,在这里主要遇到了两个错误,一个是 failed to set permissions,另外一个是 failed to set times on "/foo/bar": 前者是由于我禁止了root用户登录,所以登录的用户没有权限操作,所以登录用户没有执行足够权限执行 -avz,所以需要改为 -rltvz;后者可能同样也是由于账号引起的问题,我的文件挂载在Linux的User Na mespace中,没有内核级别的时间设定权限,因此需要添加 --omit-dir-times 规避这一问题。