博客项目概述

一、项目简介

这是一个基于 Hugo 静态网站生成器 + PaperMod 主题的个人博客系统。代码托管在 Gitee 私有仓库,通过 Caddy 作为 Web 服务器自动提供 HTTPS 服务,并利用服务器定时任务(crontab)实现自动拉取更新、构建和部署。整个系统部署在腾讯云服务器(Ubuntu 24.04)上,同时服务器还运行着 Nextcloud 私有云盘和 Jupyter 科学计算环境(均通过 Docker 容器管理)。

二、技术栈

  • 静态生成器:Hugo(v 0.146.0+extended)
  • 主题:PaperMod(手动下载,非 Git 子模块)
  • 版本控制:Git + Gitee(国内镜像,私有仓库)
  • Web 服务器:Caddy(Docker 容器),自动申请/续期 SSL 证书
  • 部署方式:本地推送 → Gitee → 服务器定时拉取 → Hugo 构建 → Caddy 服务
  • 服务器 OS:Ubuntu 24.04 LTS
  • 容器管理:Docker + Docker Compose(管理 Caddy、Nextcloud、Jupyter 等)
  • 自动部署:crontab 每分钟执行自定义脚本 /home/your-username/auto-deploy.sh

三、系统架构

本地电脑
    │ 写文章 (Markdown)
    │ hugo new posts/xxx.md
    │ 编辑后 git add/commit/push
Gitee 仓库 (私有)
腾讯云服务器
    ├─ crontab (每分钟)
    │   └─ 运行 /home/your-username/auto-deploy.sh
    │        ├─ git pull 拉取最新源码
    │        ├─ hugo --minify 生成静态文件到临时目录
    │        ├─ 替换 /home/your-username/blog/public
    │        ├─ 修复权限为 www-data
    │        └─ docker restart caddy
    └─ Caddy 容器 (监听 80/443)
         └─ 托管 /home/your-username/blog/public

四、关键配置与路径

4.1 本地博客源码

  • 路径本地博客路径
  • 配置文件hugo.toml(或 config.toml),内容示例:
    baseURL = 'https://lingzuian.top/'
    languageCode = 'zh-cn'
    title = '静影沉璧到此一游'
    theme = 'PaperMod'
    copyright = '© 2026 静影沉璧到此一游 | 备案号:<a href="https://beian.miit.gov.cn/" target="_blank">湘ICP备2026005470号</a>'
    
    [params]
      math = true  # 开启 KaTeX 数学公式
    [menu]
      [[menu.main]]
        name = "文章"
        url = "/posts/"
        weight = 10
      [[menu.main]]
        name = "关于"
        url = "/about/"
        weight = 20
    
  • 文章存放content/posts/
  • 页面存放content/,如 about.md
  • 自定义模板layouts/partials/,如 math.html(加载 KaTeX)

4.2 服务器关键目录

  • 博客源码/home/your-username/blog-source(从 Gitee 克隆)
  • 静态文件目录/home/your-username/blog/public(Caddy 挂载的根目录)
  • 自动部署脚本/home/your-username/auto-deploy.sh
  • 部署日志/home/your-username/auto-deploy.log
  • Caddy 配置/home/your-username/caddy/Caddyfile
  • Caddy Compose 文件/home/your-username/caddy/docker-compose.yml

4.3 自动部署脚本(当前稳定版)

#!/bin/bash
SOURCE_DIR="/home/your-username/blog-source"
PUBLIC_DIR="/home/your-username/blog/public"
TEMP_DIR="/home/your-username/blog/temp"

cd "$SOURCE_DIR" || exit 1

git fetch origin
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main)

if [ "$LOCAL" = "$REMOTE" ]; then
    echo "No updates, exiting."
    exit 0
fi

echo "Updates found, pulling changes..."
if ! git pull; then
    echo "Git pull failed. Aborting deployment."
    exit 1
fi

mkdir -p "$TEMP_DIR"
sudo chown your-username:your-username "$TEMP_DIR"

if ! hugo --minify --destination "$TEMP_DIR"; then
    echo "Hugo build failed, aborting deployment. Keeping old site."
    rm -rf "$TEMP_DIR"
    exit 1
fi

sudo rm -rf "$PUBLIC_DIR"
sudo mv "$TEMP_DIR" "$PUBLIC_DIR"
sudo chown -R 33:33 "$PUBLIC_DIR"
sudo chmod -R 755 "$PUBLIC_DIR"
docker restart caddy
echo "Deployment complete."

4.4 Caddy 配置 (Caddyfile)

lingzuian.top {
    root * /var/www/blog
    file_server
}

cloud.internal.example.com {
    reverse_proxy nextcloud_app:80
}

jupyter.internal.example.com {
    reverse_proxy math_jupyter:8888
}

4.5 Caddy Docker Compose (docker-compose.yml)

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - /home/your-username/blog/public:/var/www/blog
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - web

volumes:
  caddy_data:
  caddy_config:

networks:
  web:
    external: true

五、自动部署流程详解

  1. crontab 每分钟执行 /home/your-username/auto-deploy.sh
  2. 脚本先 git fetch origin 比较本地与远程 commit hash:
    • 若相同 → 退出(无更新)。
    • 若不同 → 执行 git pull 拉取最新代码。
  3. 拉取成功后,创建临时目录 /home/your-username/blog/temp,确保所有者是 your-username
  4. 运行 hugo --minify --destination 生成静态文件到临时目录。
  5. 构建成功:删除旧 public 目录,将 temp 原子移动为 public,然后修复所有者为 www-data(UID 33)并设置权限 755,最后重启 Caddy。
  6. 构建失败:保留旧 public 目录,删除临时目录,退出(网站不变)。

六、常见问题与解决方案(历史坑点)

6.1 权限问题(Permission denied)

  • 现象:Hugo 构建时无法写入 public 目录,或 Caddy 无法读取文件。
  • 原因public 目录所有者不是运行 Hugo 的用户(your-username)或不是容器内的用户(www-data)。
  • 解决方案
    • 在脚本中,生成前确保 temp 目录归 your-username,生成后 publicwww-data
    • 手动修复:sudo chown -R 33:33 /home/your-username/blog/public
  • 当前状态:脚本已正确处理,若手动操作后需检查权限。

6.2 Git 冲突或无法推送

  • 现象git push 被拒,提示 non-fast-forwarddivergent branches
  • 原因:在服务器上直接提交了代码,导致本地与远程分叉。
  • 解决方案
    • 永远不要在服务器上执行 git commit。所有修改必须在本地进行。
    • 若已发生,需在服务器上 git pull 合并(可能冲突),再 git push 解决。
  • 预防:遵守“只从本地推送”原则。

6.3 Hugo 构建失败(Front Matter 格式错误)

  • 现象:构建时报错 unmarshal failed: toml: expected character =,导致网站无法更新。
  • 原因:新建或修改的 Markdown 文件开头元数据格式有误(如误用 --- 代替 +++,或缺少等号)。
  • 解决方案
    • 确保文件开头是 +++,每行格式如 key = "value"
    • 推送前用 hugo server -D 本地预览,可提前发现错误。
  • 脚本保护:脚本在构建失败时会保留旧网站,避免 404。

6.4 Caddy 挂载失效(目录 inode 变化)

  • 现象:网站 404,但服务器上 public 目录有文件,容器内 /var/www/blog 为空。
  • 原因:脚本用 sudo rm -rf 删除旧目录再重建,导致目录 inode 变化,Docker 挂载指向了已删除的 inode。
  • 解决方案
    • 改用 sudo rm -rf "$PUBLIC_DIR"/* 清空内容而不删除目录本身(保留 inode)。
    • 或采用临时目录原子替换(当前脚本已使用 mv 替换整个目录,但 mv 后 inode 改变,仍需重启 Caddy)。因此脚本最后重启 Caddy 确保重新挂载。
  • 当前状态:脚本通过重启 Caddy 解决此问题,且只在有更新时重启,无更新时不重启。

6.5 自动部署脚本误判更新

  • 现象:即使无更新,脚本也执行构建和重启。
  • 原因:早期脚本通过 git pull 的输出判断更新,不够准确。
  • 解决方案:改用 git fetch 比较 commit hash(当前脚本已修复)。

6.6 KaTeX 公式不渲染

  • 现象:文章中的 $...$$$...$$ 未渲染为数学公式。
  • 原因:KaTeX 脚本未正确加载或定界符未配置。
  • 解决方案
    • layouts/partials/math.html 中添加 KaTeX 脚本和 renderMathInElement 调用(已配置)。
    • 确保文章或全局开启了 math = true

七、注意事项与最佳实践

  1. 绝对不要在服务器上直接修改文件(包括 nano 编辑),所有更改都应在本地完成并推送。
  2. 绝对不要在 Gitee 网页上直接编辑文件,以免造成版本混乱。
  3. 推送前务必本地预览hugo server -D 检查格式和渲染。
  4. 新文章必须 git add:新建文件后,先用 git status 确认已加入版本控制。
  5. 定期查看服务器日志tail -f /home/your-username/auto-deploy.log 确保脚本正常运行。
  6. 如果需紧急修改服务器文件(不推荐),改完后必须立即 git commitgit push,然后在本地 git pull 同步。

八、日常维护与更新指南

8.1 写一篇新文章

# 本地
cd 本地博客路径
hugo new posts/文章名.md
# 编辑文章(修改 draft = false)
hugo server -D   # 预览
# 满意后
git add .
git commit -m "新增文章:文章名"
git push

8.2 修改已有文章

# 编辑文件后
git commit -am "更新文章:xxx"   # 针对已追踪文件
git push

8.3 修改主题或配置

  • 所有修改在本地进行,然后 git add/commit/push
  • 主题文件位于 themes/PaperMod/,若有自定义覆盖,放在 layouts/assets/css/extended/

8.4 升级 Hugo 版本

  • 服务器上需手动下载新版 hugo 二进制并替换 /usr/local/bin/hugo
  • 本地也需要同步升级,避免版本差异导致的构建问题。

8.5 处理服务器故障

  • Caddy 挂了docker restart caddycd ~/caddy && docker-compose down && docker-compose up -d
  • 磁盘空间满:检查日志大小,清理旧日志或无用的 Docker 镜像。
  • 域名证书问题:Caddy 会自动续期,无需干预。

九、联系与备份

  • 博客域名https://lingzuian.top(备案号:湘 ICP 备 2026005470 号)
  • Gitee 仓库:私有仓库,地址不公开
  • 服务器:仅管理员 SSH 登录
  • 数据备份:建议定期将 /home/your-username/blog/public/home/your-username/blog-source 备份到 COS 或 Nextcloud。

本概述涵盖了项目架构、配置、历史问题和维护要点。希望未来无论由谁(或哪个 AI)接手,都能快速理解并有效协助。

截止至 2026.02.14