背景
之前这个博客的自动化部署一直是用自建 Drone CI 来跑的,相关文章可以翻翻:Blog All Process 和 Drone自动化部署Java应用。
用了两三年,功能上没问题,push 代码后自动构建部署一条龙。但最近越来越觉得维护这套东西的成本太高了:
- 需要一台服务器专门跑 Drone——drone-server + drone-runner-docker 两个容器,还得配 NGINX 反代、HTTPS 证书
- GitHub OAuth 应用要维护——Drone 登录依赖 GitHub OAuth,得在 GitHub 上建 OAuth App,配回调地址
- 服务器迁移就全得重来——之前从宝塔换到 1Panel,Drone 的 docker socket 映射、目录映射全部得重新搞
- 资源占用不划算——就为了跑个博客构建,长期跑两个容器,内存白白占着
说白了,就我一个用户,建一个博客项目,为了这点需求长期维护一套 CI 系统,性价比太低了。
于是就想,能不能把这套东西干掉,找个现成的、不用自己维护的方案。
方案选择
目前主流的 CI 服务就这几个:
- GitHub Actions——GitHub 自带的,免费额度够用,仓库就在 GitHub 上,天然集成
- GitLab CI——如果项目放 GitLab 上可以用,但我项目已经在 GitHub 了
- Jenkins——更重了,跟 Drone 一个性质,需要自己维护服务器
- Drone Cloud——官方托管版,但免费额度有限
没怎么犹豫就选了 GitHub Actions,原因很简单:
- 仓库本身就在 GitHub 上,push 就触发,零延迟
- 免费额度:每月 2000 分钟,博客构建一次不到 2 分钟,根本用不完
- 不用维护任何服务器,GitHub 帮你跑
- 配置就是一个
.yml文件,放项目里就行
之前的部署流程
先回顾一下之前 Drone 的流程长什么样:
GitHub push → Drone webhook → drone-runner 拉代码 →
hugo 构建 → scp 静态文件到博客服务器 → 完成
看着挺顺的,但实际上里面有几个坑:
- 换行符问题——Windows 下 git 默认 CRLF,Linux 下 LF,PaperMod 主题的 CSS integrity 校验会失败,导致样式全丢。之前在 Drone 服务器上还得专门配
git config --global core.autocrlf input来解决 - submodule 问题——PaperMod 主题用
git submodule方式引入的,Drone 拉代码时候不会自动拉 submodule,导致 hugo 构建出来内容不全 - scp 全量传输——每次部署都是全量上传,博客大了之后越来越慢
这些坑其实都能解,但每次服务器迁移或者换环境,都得重新踩一遍。
GitHub Actions 实现
最终跑通的配置如下,直接看文件 .github/workflows/deploy.yml:
name: Deploy Hugo Blog by SCP
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v3
- name: Install custom hugo withdeploy
run: |
wget https://github.com/gohugoio/hugo/releases/download/v0.145.0/hugo_extended_withdeploy_0.145.0_Linux-64bit.tar.gz -O hugo.tar.gz
tar -zxf hugo.tar.gz
sudo mv hugo /usr/local/bin/hugo
hugo version
- name: 构建网站
run: hugo --minify
- name: 上传到blog.chunxiao.site
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
port: 1313
password: ${{ secrets.SERVER_PASSWORD }}
source: "public/."
target: "/opt/1panel/apps/openresty/openresty/www/sites/blog.chunxiao.site/index"
下面一步一步解释为什么这么写。
代码检出
- name: 检出代码
uses: actions/checkout@v3
这一步 GitHub 会拉取仓库代码到 runner 上。默认就会拉取所有 submodule,所以之前 Drone 遇到的主题缺失问题在 Actions 上天然不存在。
Hugo 构建
这里有个坑,重点说。
最开始我试了 GitHub 官方的 peaceiris/actions-hugo@v3 这个 action,看着挺方便的:
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: "0.145.0"
extended: true
但实际跑下来发现问题了——普通的 hugo extended 版本缺少 withdeploy 功能,PaperMod 主题有些特性依赖这个。之前自建 Drone 时候用的也是自定义的 hugo_extended_withdeploy 版本。
所以这里改成了手动下载 Hugo 官方提供的 extended_withdeploy 版本:
- name: Install custom hugo withdeploy
run: |
wget https://github.com/gohugoio/hugo/releases/download/v0.145.0/hugo_extended_withdeploy_0.145.0_Linux-64bit.tar.gz -O hugo.tar.gz
tar -zxf hugo.tar.gz
sudo mv hugo /usr/local/bin/hugo
hugo version
这个版本在 Hugo 的 release 页面有提供,专门带了 deploy 扩展功能。版本号锁定 0.145.0,避免 Hugo 升级后 PaperMod 出现兼容问题——这个教训之前踩过,Hugo 大版本升级后主题的 partial 路径可能变,直接导致构建出来的页面结构不对。
构建命令很简单:
- name: 构建网站
run: hugo --minify
--minify 会对 HTML/CSS/JS 进行压缩,减少传输体积。
部署到服务器
这一步是核心,也是我之前踩了大坑的地方。
最开始的方案是用 rsync:
- name: Deploy via rsync
uses: burnett01/rsync-deployments@7.0.2
with:
switches: -avz --delete
path: public/
remote_path: /opt/1panel/...
...
结果跑完之后网站直接 404 了。
排查发现,rsync 在 GitHub Actions 环境下,--delete 参数会把远程目录里不属于本次同步的文件全部删掉。1Panel 管理网站的时候会在网站目录下放一些配置文件(比如 .user.ini),rsync 一跑全给清了,OpenResty 找不到正确的 index 文件,直接 404。
后来试了自己写原生 rsync 脚本,把 .user.ini 排除掉:
rsync -avz --delete --exclude '.user.ini' ...
结果还是 404。进一步排查发现,rsync 的 public/ 路径行为跟 scp 不一样——rsync 会把 public 这个目录本身也同步过去,而不是只同步目录里的内容。
所以最终回到了之前 Drone 用的方案:appleboy/scp-action。
- name: 上传到blog.chunxiao.site
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
port: 1313
password: ${{ secrets.SERVER_PASSWORD }}
source: "public/."
target: "/opt/1panel/apps/openresty/openresty/www/sites/blog.chunxiao.site/index"
关键点:
source: "public/."——末尾的.很重要,意思是同步public目录里的内容,而不是把public这个目录本身也带过去。如果写成public/,传到服务器上会变成index/public/xxx.html,路径就错了- password 认证——不用 SSH key,直接用密码。GitHub Secrets 里存的是密码,安全级别对于博客部署足够了
- port: 1313——我的服务器 SSH 端口不是默认 22,改成了 1313
Secrets 配置
需要在 GitHub 仓库的 Settings → Secrets and variables → Actions 中添加:
| Secret 名称 | 值 |
|---|---|
SERVER_HOST | 博客服务器 IP(填入你云服务商提供的公网 IP) |
SERVER_USER | SSH 用户名 |
SERVER_PASSWORD | SSH 密码 |
注意:SERVER_PORT 我直接写死在 yml 里了,因为端口不会变,没必要多一个 secret。
实际运行效果
push 代码后,大约 1 分 30 秒完成部署:
- 检出代码:~5 秒
- 下载 Hugo:~10 秒
- 构建网站:~30 秒
- SCP 上传:~20 秒
每次部署完可以在 Actions 页面看到完整的运行日志,哪一步出了问题一目了然。
踩坑记录
坑1:rsync 导致 404
这个坑在上面已经详细说了,核心就是 rsync 的路径行为和 --delete 参数跟 1Panel 的网站目录管理冲突。最终用 scp-action 解决。
教训:能跑的配置别随便改。 之前 Drone 的 scp 方案跑了两年没出过问题,换成 rsync 反而把网站搞挂了。
坑2:Hugo 版本选错
peaceiris/actions-hugo 装的是普通 extended 版,缺少 withdeploy 功能,导致部分 PaperMod 特性不正常。必须用 hugo_extended_withdeploy 版本。
坑3:server 地址搞混
配置 secret 时候把服务器地址写错了,部署半天连不上。这个坑提醒大家:多个服务器的话一定要核对清楚 IP,别像我一样张冠李戴。
跟 Drone 的对比
| 维度 | Drone CI | GitHub Actions |
|---|---|---|
| 需要自建服务器 | 是 | 否 |
| 免费额度 | 自建无限制 | 2000 分钟/月 |
| 维护成本 | 高(容器+NGINX+OAuth) | 零 |
| 配置位置 | 项目根目录 .drone.yml | .github/workflows/deploy.yml |
| 日志查看 | Drone 后台页面 | GitHub Actions 页面 |
| 构建速度 | ~2 分钟 | ~1.5 分钟 |
| 换行符问题 | 需要额外配置 | 无需处理 |
| submodule | 需要手动处理 | 自动处理 |
总结
从自建 Drone 切换到 GitHub Actions,最大的感受就是:省事了。
不用再维护 Drone 服务器、不用管 OAuth 应用、不用担心服务器迁移后 CI 全挂。push 代码后 GitHub 自动跑,跑完自动部署,日志也在 GitHub 上能看到。
对于个人博客这种低频、轻量的场景,GitHub Actions 完全够用。如果你的项目本身就在 GitHub 上,基本没有理由再自建一套 CI。
当然,如果你的团队有合规要求、代码不能放 GitHub,那另当别论。但对于个人开发者来说,能用现成的就别自己搭。
最后,如果你对 WSL2、Docker、本地大模型部署这些感兴趣,可以关注我的 B 站(UID: 483468180 - 晓若寒轻),后面还会出更多实用教程。
有问题的话,可以在文章下面留言,或者 B 站私信我。