本文可公开复用。域名、服务器路径、用户名、仓库、指纹和凭据均为示例;真实 Token、私钥、证书私钥与公网地址不得写入仓库、日志或文章。
一、最终架构
| 服务 | 示例地址 | 端口 | 职责 |
|---|---|---|---|
| Caddy 静态站点 | blog.example.com | TCP 80、443 | HTTPS、压缩与静态文件 |
| DERP | derp.example.com | TCP 4443 | Tailscale 中继 |
| STUN | derp.example.com | UDP 3478 | NAT 穿透测试 |
| SSH | 服务器地址 | TCP 22 | 运维与 Actions 部署 |
Caddy 与 DERP 不争用 443:Caddy 接管标准 HTTPS,DERP 使用 4443。Hugo 只在 CI 构建,服务器只托管产物,运行面更小。
二、Compose 管理 Caddy
| |
| |
Caddy 证书位于 Docker 命名卷,不在站点目录。日常检查:
| |
三、DERP 与 Tailscale
DERP 使用域名和自签名证书时,证书 SAN 必须包含该域名;Tailscale 策略中的节点应使用域名、DERPPort: 4443、STUNPort: 3478,并同步证书的 CertName 指纹。
| |
保留官方区域(OmitDefaultRegions: false)作为故障回退。浏览器访问 http://derp.example.com:4443/ 出现“HTTP request to an HTTPS server”是正常现象;请用 TLS 工具或 tailscale netcheck 验证。
四、GitHub Actions:可验证构建与原子发布
工作流固定 Hugo extended 版本,直接从 Hugo 官方 Release 下载二进制和 checksums 文件,再用 sha256sum --check --strict 校验。这样不依赖维护较慢的第三方 Hugo 安装 Action。
发布流程:
| |
失败时旧站点保留;部署脚本会在 Caddy 重建失败时尝试回滚。工作流以并发组串行化生产部署,避免多个发布互相覆盖。
Secrets 仅保存服务器地址、端口、部署用户、部署目录、Compose 目录和部署私钥。主题子模块优先直连 GitHub,失败时才使用可选 GH_PROXY 变量。
五、部署账户与更新任务
Actions 使用专用部署用户和专用 SSH 密钥:服务器仅保存公钥;该用户禁用密码与交互式认证。Token、私钥与证书私钥权限应为最小化,例如 root:root、600,并置于仓库外。
Hugo 更新检查可由 cron 每日执行:无稳定版更新只写日志;有更新才触发 workflow dispatch。只有构建和部署成功后才发送通知,避免误报。
六、主题、可访问性与浏览体验
主题已迁移 Hugo 弃用 API:locale、.Site.Language.Locale 与 hugo.Data,生产构建不再出现弃用警告。文章链接使用稳定 slug。
当前体验优化:
- 本地 Lunr 搜索生成
/search.json;中文使用字符级分词,不依赖外部搜索服务; Alt+S聚焦搜索框,不自动搜索;Alt+X浏览文章/标题,Alt+Z浏览导航;- 首页返回时恢复上次文章焦点;
- 代码块、引用块和文章链接提供复制功能;
- 文章入口按配置在新标签页/窗口打开,并使用
rel="noopener"; - 评论按需加载,减少初始第三方请求;关闭 instant.page;图片缩放固定版本而非
@latest; - 装饰元素不制造屏幕阅读器噪音,功能控件使用原生语义和明确名称。
七、维护清单
| |
| |
- 修改主题后先检查 Hugo 弃用警告和生产构建;
- 主题通用功能放主题仓库,站点专属设置留在站点配置;
- 不要保留与主题完全相同的本地模板覆盖;
- 更新镜像、Hugo 或脚本后先验证,再发布;
- 定期轮换 Token 和部署密钥,并检查安全组只开放必要端口。