使用 1Panel 与 Git 的Hugo博客自动化部署#
一、 目标#
本指南旨在解决在 Windows 本地使用 Hugo 撰写博客,并将其部署到装有 1Panel 面板的云服务器时,手动上传 public
文件夹所带来的繁琐与不便。
我们将通过配置 Git、SSH 密钥、1Panel 以及 Webhook,搭建一套稳定、高效的自动化部署流水线(CI/CD)。最终实现的效果是:您只需在本地电脑上完成文章写作并执行 git push
命令,您的云服务器便会自动拉取最新内容、完成构建并发布上线,整个过程无需您登录服务器。
二、 核心流程概览#
- 本地: 撰写/修改 Markdown 文章 (
.md
) -> 本地预览 ->git push
推送整个项目到 GitHub。 - GitHub: 收到推送后,通过 Webhook 功能,向您的 1Panel 服务器发送一个“更新通知”。
- 1Panel 服务器: 接收到通知后,自动执行预设好的部署脚本。
- 部署脚本: 自动从 GitHub 拉取最新的项目文件 -> 将您在本地构建好的
public
文件夹同步到网站根目录。 - 完成: 网站内容更新,部署成功。
三、 本地环境与 Git 仓库准备#
这一阶段在您的 Windows 电脑上完成,目的是为您的 Hugo 项目建立一个规范的 Git 版本控制环境。
步骤 1.1:初始化本地 Git 仓库#
在您的 Hugo 项目根目录(例如
E:\MyProgrammes\Hugo\Antwen-Space
)下,右键打开终端(PowerShell, Git Bash 或 Windows Terminal)。执行以下命令,初始化 Git 仓库:
Bash
git init git add . git commit -m "Initial commit: 项目初始化"
步骤 1.2:创建并配置 .gitignore
文件(关键步骤)#
为了避免将不必要的文件(如 Hugo 生成的临时文件和资源)上传到 GitHub,我们需要创建一个 .gitignore
文件。
在项目根目录,创建一个名为
.gitignore
的文件。打开该文件,粘贴以下内容:
# Hugo /resources/ hugo_stats.json # OS generated files .DS_Store Thumbs.db
注意:根据我们最终确定的工作流,您需要在本地构建好
public
目录并将其推送到 GitHub。因此,请确保.gitignore
文件中没有/public/
这一行。如果存在,请删除它。
步骤 1.3:在 GitHub 上创建远程仓库#
- 登录 GitHub,点击右上角的
+
号,选择 “New repository”。 - 为您的仓库命名(例如
my-hugo-blog
)。 - 强烈建议选择 “Private” (私有),以保护您的文章草稿和配置信息。
- 点击 “Create repository”。
步骤 1.4:关联本地仓库与远程仓库#
根据 GitHub 创建仓库后给出的提示,执行命令将本地仓库与远程关联。
Bash
# 将 URL 替换为您自己的仓库地址 git remote add origin https://github.com/your-username/your-repo.git git branch -M main git push -u origin main
步骤 1.5:解决 Windows 下的 “dubious ownership” 问题#
如果在执行 Git 命令时遇到 fatal: detected dubious ownership in repository
的错误,这是 Windows 上常见的权限问题。只需按照 Git 的提示,执行以下命令即可将其标记为安全目录:
Bash
# 将路径替换为您自己的项目路径
git config --global --add safe.directory 'E:/MyProgrammes/Hugo/Antwen-Space'
四、云服务器环境配置#
这一阶段在您的云服务器上完成,目的是让服务器具备安全、自动地从 GitHub 拉取代码的能力。
步骤 2.1:通过 SSH 登录服务器#
使用您的 SSH 工具(如 Xshell, Termius, หรือ Windows Terminal)登录到您的云服务器。
步骤 2.2:安装 Git#
如果您的服务器是全新的,可能没有安装 Git。
Bash
# 以 Debian/Ubuntu 为例
sudo apt update
sudo apt install -y git
注意:由于我们最终采用的工作流是直接同步您本地构建的
public
文件夹,服务器上并非必须安装 Hugo。这大大简化了服务器的环境配置。
步骤 2.3:为服务器生成专用的 SSH 密钥#
为了让服务器能免密从您的私有 GitHub 仓库拉取代码,我们需要为其生成一把专用的 SSH 密钥。
在服务器终端执行命令,
-t
指定加密算法,-C
添加注释(建议用邮箱或项目名)。Bash
ssh-keygen -t ed25519 -C "my_hugo_blog_deploy_key"
接下来会有一系列提问,请连续按三次 Enter 键,接受默认路径并且不设置密码。自动化脚本无法处理需要输入的密码。
步骤 2.4:将服务器公钥添加到 GitHub Deploy Keys#
在服务器上,执行命令查看并复制公钥的全部内容。
Bash
cat ~/.ssh/id_ed25519.pub
复制从
ssh-ed25519
开头到末尾注释的全部文本。回到 GitHub,进入您的项目仓库页面,导航至
Settings
>Deploy Keys
。点击
Add deploy key
。- Title: 任意填写,方便您识别,如
My 1Panel Server
。 - Key: 粘贴您刚刚从服务器复制的公钥。
- Allow write access: 不要勾选。服务器只需要读取(拉取)代码,为了安全,不授予写入权限。
- Title: 任意填写,方便您识别,如
点击
Add key
保存。
步骤 2.5:测试 SSH 连接#
在服务器上执行以下命令,验证与 GitHub 的连接是否成功。
Bash
ssh -T git@github.com
首次连接会提示 Are you sure you want to continue connecting (yes/no)?
,输入 yes
并回车。如果看到 Hi your-username/your-repo! You've successfully authenticated...
的欢迎信息,则证明连接成功。
步骤 2.6:克隆项目源代码到服务器#
将您的项目克隆到服务器的一个指定目录,例如 /opt/hugo_source
。
在 GitHub 仓库页面,点击绿色
< > Code
按钮,选择 SSH 标签页,复制 SSH 格式的 URL (应为git@github.com:...
格式)。在服务器上执行
clone
命令。Bash
# 创建父目录 sudo mkdir -p /opt/hugo_source # 将所有权赋予当前用户,避免后续的 sudo 权限问题 sudo chown -R $(whoami) /opt/hugo_source # 使用 SSH URL 克隆 git clone git@github.com:dinghyv/myhugo.git /opt/hugo_source/my-blog
五、1Panel 自动化流程配置#
这是连接所有环节,实现自动化的核心步骤。
步骤 3.1:在 1Panel 中创建静态网站#
- 登录 1Panel,进入 “网站” -> “创建网站”。
- 选择 “静态网站”。
- 主域名: 填写您的主域名,例如
www.antwen.com
。 - 网站目录: 1Panel 会自动为您生成一个不可更改的目录,例如
/opt/1panel/apps/openresty/openresty/www/sites/www.antwen.com/index
。请记下这个路径,我们稍后会用到。 - 完成其他配置并创建网站。
步骤 3.2:编写并上传部署脚本 (deploy.sh
)#
这个脚本是自动化部署的执行者。
在服务器上,进入我们之前克隆的项目目录,并创建脚本文件。
Bash
cd /opt/hugo_source/my-blog nano deploy.sh
将以下脚本内容完整地粘贴到
deploy.sh
文件中。请务必将DEST_DIR
的值修改为您在上一步中记下的真实网站目录!Bash
#!/bin/bash # Hugo 博客源代码的存放路径 (其中包含了您已经构建好的 public 目录) SOURCE_DIR="/opt/hugo_source/my-blog" # 1Panel 指定的网站根目录 (请务必替换成您自己的真实路径!) DEST_DIR="/opt/1panel/apps/openresty/openresty/www/sites/www.antwen.com/index" echo "============ 开始同步网站文件 ============" echo "同步时间: $(date '+%Y-%m-%d %H:%M:%S')" cd $SOURCE_DIR || exit echo "正在从 Git 拉取最新代码 (包括 pre-built public 目录)..." git reset --hard git pull origin main echo "正在从 $SOURCE_DIR/public/ 同步到 $DEST_DIR/ ..." # 核心命令:使用 rsync 高效地将本地构建好的 public 目录同步到网站根目录 # --delete 参数会删除目标目录中多余的文件,保持与源目录完全一致 rsync -av --delete "$SOURCE_DIR/public/" "$DEST_DIR/" echo "============ 同步完成!网站已更新。 ============"
保存并退出 (
Ctrl+X
,Y
,Enter
)。赋予脚本执行权限:
Bash
chmod +x deploy.sh
步骤 3.3:利用 1Panel 计划任务创建 Webhook 触发器#
进入 1Panel 的 “计划任务” -> “创建计划任务”。
任务名称: 自定义,如
Webhook-Deploy-Hugo-Blog
。执行周期: 选择 “手动执行”,或设置一个极少执行的频率(如每年一次),因为我们不依赖它的定时功能。
脚本内容: 填写部署脚本的绝对路径。
Bash
/bin/bash /opt/hugo_source/my-blog/deploy.sh
保存任务。
在计划任务列表中,找到刚创建的任务,点击右侧的 “…” 更多操作,选择 “Webhook”,复制弹出的专属 URL。
步骤 3.4:在 GitHub 中设置 Webhook#
- 回到 GitHub 项目仓库,进入
Settings
>Webhooks
。 - 点击
Add webhook
。 - Payload URL: 粘贴您从 1Panel 复制的 Webhook URL。
- Content type: 选择
application/json
。 - 保存 Webhook。您可以立即看到 GitHub 发送了一个测试请求(ping),如果一切正常,它会有一个绿色的对勾。
六、 域名与最终配置#
确保用户无论如何访问,都能得到一致且正确的体验。
步骤 4.1:选择首选域名并设置 301 重定向#
为了用户体验和 SEO,我们需要在 www.antwen.com
和 antwen.com
中选择一个作为唯一“官方”地址,并将另一个地址永久重定向过去。
推荐选择 www.antwen.com
作为首选域名。
确认 Hugo
baseURL
: 检查您本地项目的hugo.toml
文件,确保baseURL
设置为带www
的版本。Ini, TOML
baseURL = "https://www.antwen.com/"
配置 1Panel 重定向:
- 进入 1Panel 中
www.antwen.com
网站的设置页面。 - 点击 “域名绑定”,确保
www.antwen.com
和antwen.com
都已添加。 - 点击 “重定向”,创建一条新规则:
- 源域名:
antwen.com
- 重定向类型:
301
- 目标 URL:
https://www.antwen.com$request_uri
- 源域名:
- 保存并启用规则。
$request_uri
变量会确保所有内页都能被正确跳转。
- 进入 1Panel 中
步骤 4.2:验证#
设置完成后,清空浏览器缓存或使用无痕模式访问 http://antwen.com
,观察它是否自动、正确地跳转到了 https://www.antwen.com
。
七、 您的全新日常发布工作流#
恭喜您!所有复杂的配置都已完成。从现在开始,您发布新文章的流程将极其简单:
本地写作: 在
content/posts/
目录下创建或修改.md
文件。本地构建: 在本地项目根目录运行
hugo
命令,生成最新的public
文件夹。推送更新: 执行 Git三连,将所有改动(包括
public
目录)推送到 GitHub。Bash
git add . git commit -m "发表新文章:xxx" git push origin main
完成! 几秒到一分钟后,刷新您的网站,即可看到最新的内容。
八、 附录:常见问题(FAQ)#
- Q: 执行
git push
后网站没变化? A: 1. 去 GitHub 检查最新 commit 是否已成功推送。 2. 去 1Panel 的“计划任务”里查看部署日志,看git pull
和rsync
命令是否报错。 - Q: 网站显示异常,样式丢失? A: 1. 检查您本地
public
目录里的文件是否完整。 2. 检查hugo.toml
中的baseURL
是否与您设置的首选域名一致。 3. 检查 1Panel 的重定向规则是否正确,是否造成了循环。 - Q: 如何查看部署日志? A: 在 1Panel 的“计划任务”页面,找到对应的任务,点击右侧的“日志”按钮即可查看每次 Webhook 触发后的脚本执行输出。