用 Hexo + Butterfly + Decap CMS + Artalk 搭建博客
用 Hexo + Butterfly + Decap CMS + Artalk 搭建博客
butterfly~=5.0
的部分配置和butterfly~=4.0
不一样,建议参考官网进行对比。
之前博客搭建在 VPS,将 Hexo,Qexo (一个 Hexo CMS) 和 Twikoo (评论后台) 集成在一个容器中,构建容器的代码很复杂,可维护性也不高,包括写作过程比上传文件到服务器的方式也简化不了多少。因此重新调整了博客的方案,主要是把 🐛 满满的 Qexo 换成了 Decap CMS,配合 GitLab 的自动化部署方便了不少,也不用担心 Qexo 把好不容易写好的博客弄坏;然后就是把 Twikoo 换成功能更多的 Artalk。
已经很久没有写博客了,这篇文章就把这次的过程都记录下来,凑点年度字数。
搭建本地环境
在本地生成足够的模板文件能大幅度降低自动化部署的难度。先安装 Hexo 和 Butterfly 主题,这一步骤按照官网来。
1 | # install Hexo |
然后按照官网教程把主题改成 Butterfly,安装依赖。
虽然 Butterfly 已经很久没有添加大的新特性了,但我们仍然可以把 themes/butterfly
加进 git submodule
,让主题能自动更新。
1 | git submodule add themes/butterfly https://github.com/jerryc127/hexo-theme-butterfly.git |
添加 Decap CMS
Decap CMS 是一个基于 Git 的内容管理系统,原理就是通过前端直接调用诸如 GItHub 这些源代码托管平台的 API,而不需要自己手动上传。Decap CMS 和其它可选的对比可以参考这篇文章 《Hexo 博客集成内容管理系统 Decap》。
Decap CMS 没有后端,取而代之的是需要 GitHub 或者其它平台的安全密钥才能正常使用,因此配置相对较为复杂。
先将 Decap CMS 添加进 Hexo 目录,创建 CMS 的入口文件 source/admin/index.html
。
1 |
|
然后创建配置文件 source/admin/config.yml
1 | # source/admin/config.yml |
backend
部分是博客托管平台的配置,在我的示例中用的是 GitLab;repo
是仓库名称,可以使私有仓库;media_folder
和public_folder
是图片资源的存放位置和链接前缀;site_url
和logo_url
是网站地址和 LOGO 的地址;locale
是内容管理系统的显示语言;common_col_conf
是自定义的一个块,里面是文章 front-matter 元数据的描述,比如标题、发表日期等;collections
是集合,如果你的博客文章都放到一个目录里,这里就只需要配置一个,如果是像我一样分开放的,那就一个一个都配置上;
Decap CMS 支持 GitHub,GitLab,Gitea 和 Bitbucket 等源代码管理平台,详细的设置方法可以参考官网文档,以 GitLab 为例,将创建的 Application ID 复制到配置文件 source/admin/config.yml
对应位置中,并且设置 auth_type: pkce
。
另外,还需要修改 Hexo 的配置文件 _config.yml
,让它原样输出 source/admin
目录下的文件。
1 | skip_render: admin/**/* |
如果一切顺利的话,访问 https://博客地址/admin
用 GitLab 登录,就可以开始写文章啦。
自动化部署
准备密钥对
要将仓库的文章部署到 VPS 上,需要免密登录 VPS,因此需要将 VPS 生成的私钥放到仓库的自动化管道中,VPS SSH 开启公钥登录。为了安全起见,先创建一个不允许密码登录的新用户:
1 | sudo adduser --disabled-password blog_deployer |
创建一个 SSH 密钥对用于部署 ,不要输入密码!
1 | ssh-keygen -t ed25519 -f ~/blog_deployer |
我们使用 GitLab 偏好的 ed25519
加密算法,它基于椭圆曲线,相比 RSA
性能更好,具有更好的密码学属性。然后将公钥添加进授权密钥列表文件 ~/.ssh/authorized_keys
中。
1 | sudo -u blog_deployer mkdir ~/.ssh |
保存密钥 $private_key
到一个安全的地方,密文内容如下:
1 | -----BEGIN OPENSSH PRIVATE KEY----- |
需要注意的是,SSH 对于密钥文件的权限极其敏感,如果发现 CI/CD 登录失败,很有可能就是这里出了问题。可以用
/usr/sbin/sshd -d -p ${port}
开启一个新的 SSH 守护进程进行调试。
设置 GitLab CI/CD
GitLab 的 CI/CD 由 gitlab-ci.yml
设置,我个人感觉比 GitHub 的 GitHub Action
方便很多。首先先向 GitLab 提供我们的 SSH 私钥 ($private_key
),以便我们的 CD 进程可以将构建发布到 VPS。
一种比较方便的方式是给向 CI/CD 配置中添加两个环境变量 (settings > CI/CD > Variables
)。
变量名称 | 变量内容 |
---|---|
SSH_PRIVATE_KEY | 生成的 SSH 私钥 $private_key |
SSH_KNOWN_HOSTS | VPS 的指纹,也就是 ssh-keyscan 博客地址.blog 的输出结果 |
最终应该看起来像这样:
其中,SSH_PRIVATE_KEY
可以设置为 protected
,但是需要注意要同时设置 protected branches
,否则会报错。
1 | $ echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null |
接下来就可以愉快地编写.gitlab-ci.yml
了。我的脚本由两个阶段组成:build
和 deploy
。在阶段 build
,我们使用最新的 Hexo,Butterfly 生成我们的博客内容,并且存储 public/
下的内容 (只保留 30 分钟,避免耗尽资源)。在阶段 deploy
,我们配置 SSH,然后将上一步生成的输出同步到 VPS。
1 | stages: # List of stages for jobs, and their order of execution |
每当有新的内容提交到仓库的 main
分支,GitLab CI/CD 就会将生成的内容发送到 VPS。这里我们使用的是 hexo d
命令,使用 rsync
进行实际的推送,因此 VPS 上需要安装并启动 rsync
服务。_config.yml
上相应的配置修改为:
1 | deploy: |
另一个值得注意的是,Windows 下的 cwrsync 需要 cygwin openssh,如果使用 Windows 11 内置的 openssh 会报错。
反向代理
下面是 Caddy 设置静态文件服务器的例子,还是比较方便的。
1 | { |
需要注意的是 Caddy 的用户是 caddy:caddy
,可能会有文件权限问题,导致 403
。一种方法是将 caddy
加入 blog_deployer
用户组中 (usermod -aG blog_deployer caddy
),然后重启 (因为只有当 caddy
登录时才会实际执行该操作)。另一种方法就是将 Caddy 设置为 root
用户启动 (即修改 /etc/systemd/system/multi-user.target.wants/caddy.service
)。
Update: 上面的方法来自网络,最近发现这样做并不能解决问题。实际上,较新版本的 Caddy 不会在
/home
目录工作。根据官网描述,当通过 systemd 启动 Caddy 时,caddy 用户由于没有/home
的用于遍历的执行权限,因此导致状态码403
。虽然官方建议将文件放到/var/www/html
中,但是在其中加入文件需要 root 权限,而出于安全考虑,我们不能允许 root 在无密码情况下远程登录。一种临时的解决办法是在/var/www/html
建立软链接至用户目录,然后将 caddy 加入用户组中,给予遍历权限。通过下面的命令可以检查用户 caddy 在哪些用户组:
1
2 id caddy
# uid=998(caddy) gid=998(caddy) groups=998(caddy),0(root),33(www-data),1000(blog_deployer)
到这一步访问 https://博客地址
就能看到博客页面,访问 https://博客地址
并登录 GitLab 就能看到 Decap CMS。
Artalk 评论系统
Butterfly 支持很多评论后台,其中 Artalk 算是我个人比较喜欢的一个 (Twikoo 也不错),功能也更多,能完美满足我的需求。
Update: 注意,Artalk 通过浏览器路径来确定站点评论,也就是说,
example.com/index.html
,example.com/index.htm
和example.com
在 Artalk 看来是三个站点,导致评论不会在一个位置。因此最好将永久链接修改为以{title}.html
结尾的格式以确保不会缺失评论。
我用 Podman 构建 Artalk 容器。在 Podman 高版本中,可以编写下面的 artalk.container
并把它放入 /etc/containers/systemd
中,用 systemctl
对其进行管理。这样做的好处是该容器在上游更新后,本地镜像也会同步更新。
1 | [Unit] |
systemctl daemon-reload
应用 Systemd 文件更新,然后启动 Artalk:systemctl start artalk.service
。通过 podman ps
可以看到 Artalk 已经启动。
1 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
执行命令创建管理员账户:
1 | podman exec -it artalk artalk admin |
通过 Caddy 暴露在公网中:
1 | artalk.jiuh.top { |
修改 _config.butterfly.yml
,应用 Artalk:
1 | comments: |
当使用 Artalk时,Butterfly 使用 Artalk 而不是 busuanji 记录文章访问人数。但是 Butterfly 的默认配置不知道为何无法生效,因此需要添加 pvEl: "#ArtalkPV"
临时解决这个 🐛。但是由于缺失 data-page-key
,导致所有页面共享一个计数,只能等待上游修复这个 🐛。
上游已修复
结语
写累了,就这样吧,希望明天也开开心心的~