2032
This commit is contained in:
1 parent
ae1a59789e
commit
2a403918c9
2 files changed
-262
No files matched your search
Binary file not shown.
|
Before Width: | Height: | Size: 309 KiB |
@@ -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
|
||||
|
||||

|
||||
|
||||
这个工具是我在逛官方文档时偶然发现的。之前一直用 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/)
|
||||
Reference in new issue
Block a user