再记:现在的HUGO建站与部署

Carinnan

三年前,我在前文中记录了本站从动态博客迁移到 Hugo 的过程。

现在回头看,最初的思路并没有变化:

  • 本地使用 Hugo 生成静态文件。
  • 服务端只运行 Nginx,不安装 Hugo。
  • 使用 rsync over SSH 将本地的public/同步到服务器。

变化主要在于:站点、配置和部署脚本都已纳入 Git;主题使用 Git 子模块固定版本;部署之前还要完成一次严格构建和本地链接审计。

这样做稍显繁琐,但换一台电脑后,可以从一个空目录重新得到相同的网站。


0、准备环境 #

本地需要:

  • Git
  • Hugo Extended
  • Python 3
  • rsync
  • SSH 客户端

服务端只需要 Nginx、SSH 和 rsync。

Hugo 的安装方式随发行版而异,请参阅官方安装文档。 以 Debian/Ubuntu 和 Fedora 为例:

1
2
3
4
5
# Debian / Ubuntu
sudo apt install git hugo python3 rsync openssh-client

# Fedora
sudo dnf install git hugo python3 rsync openssh-clients

安装后先记录版本:

1
2
3
hugo version
git --version
rsync --version

hugo version应包含extended。也不必盲目追逐最新版本;站点能够通过完整审计的版本, 才是当前可用的版本。

1、克隆站点和主题 #

本站的主题不是普通目录,而是 Git 子模块。普通的git clone只会得到空的主题目录, 所以应当递归克隆:

1
2
3
4
5
6
7
8
9
mkdir -p ~/src
cd ~/src

# ACCOUNT和SITE_REPOSITORY均为脱敏占位值,使用时请替换。
git clone --recurse-submodules \
  https://github.com/ACCOUNT/SITE_REPOSITORY.git \
  hugo-site

cd hugo-site

若仓库已经克隆,则补充执行:

1
2
git submodule sync --recursive
git submodule update --init --recursive

随后检查:

1
git submodule status

每行开头不应出现-+-表示主题尚未初始化;+表示当前主题版本与主仓库记录不符。 Git 对这些状态的定义见git-submodule文档

为了使一次构建可以追溯,我通常同时保留以下输出:

1
2
3
git rev-parse HEAD
git submodule status
hugo version

如此便可以知道:哪一版站点、哪一版主题、由哪一版 Hugo 生成。

2、站点目录 #

当前仓库大致如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
hugo-site/
├── content/              # 文章
├── config/themes/        # 各主题的独立配置
├── themes/               # 官方主题子模块和本地覆盖层
├── layouts/              # 站点公共模板覆盖
├── assets/               # CSS、JavaScript等资源
├── static/               # favicon等原样复制的文件
├── scripts/audit_site.py # 生成结果审计
├── site.sh               # 预览、构建、审计和部署入口
└── rsync.sh              # SSH部署

public/是构建结果,不纳入 Git,也不视作源文件。真正需要保存的是content/、配置、 模板、资源和脚本。Hugo 各目录的作用可查阅目录结构文档

主题子模块本身保持不变。需要修改模板时,在项目的layouts/或本站自己的主题覆盖层中 放置同路径文件。Hugo 会优先使用项目层文件;这一行为见Theme components。 这样更新官方主题时,不必处理自己留下的修改。

3、本地预览 #

先列出可用主题:

1
./site.sh list

默认使用 Narrow:

1
./site.sh serve narrow

然后访问:

1
http://localhost:1313/

也可以将narrow换成typopapermodblowfish。预览只用于查看页面; 最终上线内容取决于部署命令指定的主题。

4、构建和审计 #

普通的hugo命令只负责生成站点。本站统一使用脚本,以免每次手写参数:

1
2
./site.sh build narrow
./site.sh audit narrow

audit会启用严格警告和路径检查,再检查生成的 HTML、CSS、空字段与失效本地链接。 它通过之后,public/才可用于部署。

主题或 Hugo 升级后,应检查全部主题:

1
2
3
4
./site.sh audit narrow
./site.sh audit typo
./site.sh audit papermod
./site.sh audit blowfish

Hugo 的构建参数可在hugo build文档中核对。

5、准备静态服务器 #

以下配置中的域名、用户和目录均为脱敏占位值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
server {
    listen 80;
    listen [::]:80;

    server_name blog.invalid;
    root /srv/www/hugo-site;
    index index.html;

    location / {
        try_files $uri $uri/index.html =404;
    }
}

创建发布目录,并使专用部署用户能够写入:

1
sudo install -d -o deploy -g deploy -m 0755 /srv/www/hugo-site

检查并重新加载 Nginx:

1
2
sudo nginx -t
sudo systemctl reload nginx

DNS、HTTPS证书和防火墙需按实际环境另行配置。这里的 Nginx 只负责读取静态文件; roottry_files的行为可查阅Nginx官方文档

6、部署 #

部署密钥不进入仓库。以下地址和路径也都是脱敏占位值,执行前必须替换:

1
2
export HUGO_DEPLOY_KEY='/secure/path/hugo-deploy-key'
export HUGO_DEPLOY_REMOTE='[email protected]:/srv/www/hugo-site/'

先 dry-run:

1
./site.sh deploy narrow --dry-run

确认来源目录、目标地址和文件变更均正确,再正式同步:

1
./site.sh deploy narrow

完成后清理当前 shell 中的变量:

1
unset HUGO_DEPLOY_KEY HUGO_DEPLOY_REMOTE

部署脚本使用 SSH 传输,并为 rsync 启用权限收敛、部分传输、延迟更新和延迟删除。 --dry-run不会修改服务器,但仍会建立 SSH 连接。rsync 参数的确切含义以 官方手册为准。

新的环境无法仅凭 Git 仓库直接部署,是有意如此:仓库保存站点,密钥负责证明身份, 二者不应放在一起。

7、以后如何更新 #

日常拉取使用:

1
2
3
4
git pull --ff-only
git submodule sync --recursive
git submodule update --init --recursive
./site.sh audit narrow

git submodule update会恢复主仓库已经记录的主题提交,并不会自动升级主题。 若确需升级,应一次只升级一个主题,审计通过后再提交新的子模块版本。不要不加检查地 对全部主题执行git submodule update --remote


现在的思路大致就是如此:

Git保存全部输入和版本,Hugo负责确定性构建,审计阻止明显错误,rsync只发布生成结果, Nginx只提供静态文件。

与早先相比,并没有增加新的服务器组件。只是把“我记得该怎么做”,改成了“仓库本身说明 该怎么做”。