diff --git a/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/image.png b/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/image.png deleted file mode 100644 index 5231cafb..00000000 Binary files a/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/image.png and /dev/null differ diff --git a/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/index.md b/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/index.md deleted file mode 100644 index 67ac68f7..00000000 --- a/content/posts/2026/2026-06-25-我的Hugo博客部署优化从7分钟到3分钟的秘密/index.md +++ /dev/null @@ -1,262 +0,0 @@ ---- -title: "聊聊我博客部署的优化:从 her-cat/upyun-deployer 换到官方 upx" -date: 2026-06-25T19:54:00+08:00 -tags: ["Hugo", "GitHub Actions", "又拍云", "部署"] -categories: ["折腾"] -draft: false ---- - -端午假期,我把用了挺久的 `her-cat/upyun-deployer` 又拍云自动部署脚本换成了官方出品的 `upx`。 - -https://help.upyun.com/knowledge-base/developer_tools/#upx-efbc88e591bde4bba4e8a18ce5b7a5e585b7efbc89 - -![upx](/image.png) - -这个工具是我在逛官方文档时偶然发现的。之前一直用 GitHub 开源作者 hercat 的 `her-cat/upyun-deployer`,稳定性确实不错,跑了一年多都没出什么问题。不过现在 Hugo 博客的文章越来越多,部署时间也越来越长。 - -GitHub Actions 对免费用户有每月 2000 分钟的运行时长限制,有时候遇到网络波动,部署就会卡很久,甚至直接超时失败。 - -这篇文章简单记录一下迁移过程,顺便对比分析两款工具的实现差异,希望能给同样在用 Hugo + 又拍云的朋友一些参考。 - -## 先聊聊 her-cat/upyun-deployer 的实现 - -在说 upx 之前,我让ai 查了查 `her-cat/upyun-deployer` 的源码,看看它是怎么工作的。作者是 her-cat,这个项目帮了很多人,包括我之前也一直在用,非常感谢作者的付出。 - -它的核心逻辑在 `main.go` 里,大概是这样的: - -```go -func (d *UpYunDeployer) UploadFiles() { - // 第一步:获取远程所有文件列表 - files, dirs := d.GetAllRemoteFilesByPath(publishDir) - - // 第二步:遍历本地文件 - err := filepath.Walk(d.localDir, func(filename string, file os.FileInfo, err error) error { - // ... - // 第三步:对每个文件,先获取远程文件信息 - remoteFileInfo, err := d.getFileInfo(relativeFilename) - - // 第四步:判断是否需要上传 - if upyun.IsNotExist(err) { - d.uploadFile(putObjectConfig, false) - return - } - - // 第五步:MD5 对比,如果相同就跳过 - if remoteFileInfo.MD5 == fmt.Sprintf("%x", md5.Sum(data)) { - fmt.Printf("[%s] cached!\n", relativeFilename) - return - } - - // 第六步:不同的话,先删除再上传 - err = d.deleteFile(relativeFilename, true) - d.uploadFile(putObjectConfig, true) - return nil - }) - - // 第七步:全量 CDN 刷新 - d.up.Purge(urls) - - // 第八步:删除远程多余的文件和目录 - d.deleteFiles(files) - d.deleteDirs(dirs) -} -``` - -整体思路其实很清晰: - -1. 先把远程所有文件列表拉下来 -2. 遍历本地每个文件,和远程对比 MD5 -3. 不一样就先删再上传 -4. 最后全量刷新 CDN,再删掉远程多余的文件 - -这个思路很不错,就是在实际使用中,有几个地方会比较耗时: - -**第一个是每个文件都要单独调 API。** - -上传一个文件之前,先要调 `GetInfo` 检查远程是否存在;如果存在且 MD5 不同,还要先调 `Delete` 删除,再调 `Put` 上传。一个文件搞不好就要发 2-3 次请求,文件多了自然就慢了。 - -**第二个是获取远程文件列表的过程。** - -`GetAllRemoteFiles` 是递归遍历所有目录的,虽然用了 goroutine 并发请求,但文件越多,请求次数就越多,这块也会花不少时间。 - -**第三个是全量 CDN 刷新。** - -每次部署完都会把所有上传过的文件都 Purge 一遍,虽然又拍云的 Purge 是异步的,但全量刷新既浪费配额,也可能导致用户访问的时候缓存命中率下降。 - -## upx 是什么 - -upx 是又拍云官方出的命令行工具,全称是 UpYun Uploader(我猜的)。 - -> UPX 是专为开发者设计的,基于命令行的云存储管理工具。它可以实现文件上下传、增量文件同步、目录创建删除、文件删除等。 - -项目地址:[https://github.com/upyun/upx](https://github.com/upyun/upx) - -我最看重的就是它的 `sync` 命令,类似于Linux的 rsync,支持增量同步。而且因为是官方维护的,对 API 的利用应该会更高效一些。 - -## 我的部署方案 - -下面说说我现在的部署配置,大家可以参考一下。 - -### GitHub Actions 配置 - -我把构建和部署拆成了两个 job,这样构建产物可以复用。 - -```yaml -name: Deploy to Production - -on: - push: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Build Hugo site - run: hugo --gc --minify - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: public - path: ./public - - deploy-upyun: - runs-on: ubuntu-latest - needs: build - steps: - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: public - path: ./public - - - name: Deploy to UpYun - env: - UPYUN_SERVICE: ${{ secrets.UPYUN_BUCKET }} - UPYUN_OPERATOR: ${{ secrets.UPYUN_OPERATOR }} - UPYUN_PASSWORD: ${{ secrets.UPYUN_PASSWORD }} - run: | - chmod +x scripts/deploy_upyun.sh - ./scripts/deploy_upyun.sh ./public / 10 -``` - -### 部署脚本 - -然后是具体的部署脚本,放在 `scripts/deploy_upyun.sh`: - -```bash -#!/bin/bash -# UpYun 部署脚本 - 使用 upx 官方工具 - -set -e - -LOCAL_DIR="${1:-./public}" -REMOTE_DIR="${2:-/}" -THREADS="${3:-10}" - -# 又拍云 upx sync 的线程数限制在 1-10 之间 -if [ "$THREADS" -lt 1 ]; then - THREADS=1 -elif [ "$THREADS" -gt 10 ]; then - THREADS=10 -fi - -SERVICE_NAME="${UPYUN_SERVICE:-}" -OPERATOR="${UPYUN_OPERATOR:-}" -PASSWORD="${UPYUN_PASSWORD:-}" - -if [ -z "$SERVICE_NAME" ] || [ -z "$OPERATOR" ] || [ -z "$PASSWORD" ]; then - echo "❌ 错误:缺少必要的环境变量" - exit 1 -fi - -echo "🚀 UpYun 部署开始" - -# 下载又拍云 upx -UPX_VERSION="0.4.9" -UPX_URL="https://collection.b0.upaiyun.com/softwares/upx/upx_${UPX_VERSION}_linux_amd64.tar.gz" -UPX_BIN="/tmp/upyun-upx" - -if [ ! -x "$UPX_BIN" ]; then - echo "📥 下载又拍云 upx v${UPX_VERSION}..." - curl -sL "$UPX_URL" -o /tmp/upx.tar.gz - tar -xzf /tmp/upx.tar.gz -C /tmp - mv /tmp/upx "$UPX_BIN" - chmod +x "$UPX_BIN" - echo "✅ upx 安装完成" -fi - -# 登录并同步 -"$UPX_BIN" login "$SERVICE_NAME" "$OPERATOR" "$PASSWORD" -"$UPX_BIN" sync "$LOCAL_DIR" "$REMOTE_DIR" \ - -w "$THREADS" \ - --delete \ - --strong - -echo "✅ UpYun 部署完成!" -``` - -脚本很简单,就是下载 upx,登录,然后 sync 一下。 - -## upx 为什么更快? - -我自己用下来,upx 确实比 `her-cat/upyun-deployer` 快不少,我个人认为主要有这几个原因: - -**第一是官方实现,对 API 的利用更高效。** - -毕竟是官方自己的工具,很多操作应该是批量处理的,而不是每个文件单独调一次 API。这一点从速度上就能明显感觉出来。 - -**第二是 sync 命令做了专门的优化。** - -`upx sync` 作为核心功能,内部的文件对比、差异计算应该都是经过优化的。不像社区项目,作者可能是业余时间维护,精力有限。 - -**第三是线程控制更稳定。** - -upx 的 `-w` 参数可以控制并发线程数,而且内部应该有完整的错误重试和限流机制,不容易触发又拍云的 API 频率限制。 - -不过有一点要注意,又拍云的 upx sync 线程数上限是 10,别开太大了,开了也没用。 - -## 一些小技巧 - -### build hash 跳过重复部署 - -有时候我们可能只是改了一下 README,或者 CI 配置变了,但 Hugo 构建的产物其实是一样的。这时候可以用 build hash 来判断是否需要重新部署。 - -```yaml -- name: Generate build hash - id: hash - run: echo "build_hash=$(find ./public -type f -exec md5sum {} \; | md5sum | cut -d' ' -f1)" >> $GITHUB_OUTPUT - -- name: Cache build - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-deploy-upyun-${{ steps.hash.outputs.build_hash }} - path: ./public -``` - -如果缓存命中了,说明构建产物没变化,就可以跳过部署步骤,省不少时间。 - -### 注意和系统 upx 重名 - -Linux 系统里可能已经装了另一个 `upx`,就是那个可执行文件压缩工具。为了避免冲突,我在脚本里把下载的又拍云 upx 改名叫 `upyun-upx`,路径放在 `/tmp` 下面,这样就不会搞混了。 - -## 一些注意事项 - -1. **敏感文件别传上去了**:部署前确认一下 `.git`、`.env` 这些东西不在 public 目录里,不然传到 CDN 上就尴尬了。 - -2. **权限尽量给小一点**:去又拍云后台创建一个专门的操作员,只给上传权限就行,别用主账户密码。安全第一。 - -3. **CDN 刷新按需来**:upx sync 上传的文件会自动更新,一般不需要手动 Purge。除非你改了文件名没变内容,那可能需要手动刷新一下。 - -4. **网络问题**:GitHub Actions 的服务器在海外,部署到又拍云国内节点可能会慢一些。我自己用下来还能接受,如果觉得慢的话,可以考虑用国内的 CI 服务。 - -**相关链接:** -- upx 开源项目:[https://github.com/upyun/upx](https://github.com/upyun/upx) -- 又拍云官方文档:[https://help.upyun.com/](https://help.upyun.com/) \ No newline at end of file