diff --git a/.gitignore b/.gitignore index 682d9af4..1d2c0030 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ write\.env # Claude Code 临时 worktree(曾被误当 submodule 提交) .claude/worktrees/ .workbuddy-backup/ +# WorkBuddy 工作数据(记忆/日志/会话),不入库 +.workbuddy/ diff --git a/CNB构建落地方案.md b/CNB构建落地方案.md new file mode 100644 index 00000000..7001c661 --- /dev/null +++ b/CNB构建落地方案.md @@ -0,0 +1,436 @@ +# CNB 构建落地方案:双 CDN 架构不动,砍掉中转链 + +> 状态:2026-10-04 · 待你确认后落地 +> 回答的问题:「CNB 构建的前提下,能不能继续现在的架构(境外 EdgeOne + 境内多吉云 → 又拍云源站)?」 + +--- + +## 一句话结论 + +**能,而且一个字都不用改。** 域名解析、备案、EdgeOne 项目、多吉云配置、又拍云存储全部保持原样。 +CNB 只替换「构建 + 搬运」这一段,顺带砍掉广州中转机上那个每分钟轮询的 `sync.sh`。 + +--- + +## 零、GitHub 依赖审计:全链路零依赖 + +回答「GitHub 账号被标记了,还能连上 CNB 吗」——**能,而且这条链路从头到尾不需要 GitHub。** + +逐环节核实(来源:腾讯云官方文档「社区版快速入门」「个人中心」、CNB 官方 Wiki): + +| 环节 | CNB 的做法 | 碰 GitHub 吗 | +|---|---|---| +| 注册 / 登录 | **微信扫码**,扫码即自动注册,无邮箱验证、无登录密码 | ❌ | +| 实名认证 | 微信扫码授权手机号 | ❌ | +| 建组织 / 仓库 | CNB 内建(无「个人私有仓库」,仓库必须挂在组织下) | ❌ | +| 导入代码 | 标准 git:`git push --mirror `,源仓可以是**自建 Gitea** | ❌ | +| 拉构建依赖 | CNB 自带「国内外依赖源默认加速」(腾讯云 CDN + Docker/NPM/Maven 镜像) | ❌ | +| hugo 二进制 | **已随仓库携带**(`bin/linux/hugo`),构建时零跨境下载 | ❌ | +| 部署 | EdgeOne API + 又拍云 upx v0 | ❌ | + +官方原文: + +> 云原生构建默认使用微信扫码注册登录,未注册用户在扫码登录后,将自动完成注册。 +> —— 腾讯云文档《社区版快速入门》 + +> 云原生构建为微信扫码登录注册,没有登录密码。 +> —— 腾讯云文档《个人中心》 + +### 唯一会碰到 GitHub 的两个场景,你都不走 + +1. **用 CNB 的「从 GitHub 导入」一键迁移工具** —— 那个需要 `GITHUB_TOKEN`。 + **但你从自建 Gitea 迁,用 `git push --mirror`,不需要它。** +2. **构建时在线拉 GitHub 资源** —— 这正是**已经提前拆掉**的雷:hugo 二进制改为随仓库携带。 + +### 顺带纠正一个概念 + +**「账号被标记」≠「IP 被墙」。** 标记是账号级限制;匿名 HTTP 访问 github.com 的公开资源不受影响 +(实测国内节点 `curl` hugo release 仍返回 302 正常)。所以即便真要在构建里拉 GitHub,也只是**慢**,不是**不通**。 + +### 你真正要做的三件准备(都与 GitHub 无关) + +1. 微信扫码注册 CNB +2. 微信扫码完成**实名认证**(官方说明:未实名用户禁止写行为 —— 创建仓库 / 推送代码) +3. 先建一个**组织**,仓库建在组织下 + +--- + +## 一、决定性发现:答案你自己早就写在代码里了 + +`deploy.yml` 第 290 行起,那段被 `if: false` 停用的 `Upload to UpYun`,注释原话: + +``` +│ 2026-10-03 起永久跳过海外直传:本 runner 在境外,对 UpYun v0 接口 +│ 无论 -w 10/--strong 还是 -w 3 均为 70 秒左右 EOF,3 轮重试全败, +│ 每次部署白耗 5 分钟(2026-10-03 build #25 日志实锤)。 +│ 国内线路由腾讯云广州中转机兜底(1Panel 计划任务「又拍云同步(国内 +│ 中转)」每分钟比对 COS build hash → 下载 355M → upx sync → +│ 又拍云 purge → 多吉云刷新,实测 5 分钟内完成且它自己会刷 CDN)。 +│ 将来若把 runner 挪到国内,把下面的 if: false 删掉即可恢复。 +``` + +**当初被迫上中转机,唯一的原因是「runner 在境外」。** +链路是这样被逼出来的: + +| 环节 | 为什么存在 | +|---|---| +| runner 在境外 | Gitea 跑在境外 VPS | +| 直传又拍云失败 | 境外 → 又拍云 v0 接口,**跨境 + 并发敏感 → 70 秒 EOF,必败** | +| COS 中转 | 境外的产物,国内机器得有个地方取 | +| 广州中转机 + sync.sh | 只有它在国内,能直传又拍云 | + +**CNB 的构建节点就在国内(腾讯云)。** 那句注释的复活条件直接成立 —— 中转机、COS、artifact 传递全都失去了存在理由。 + +--- + +## 二、目标架构 + +``` +写作 / 本地 + │ git push + ▼ + CNB(腾讯云,构建节点在国内 4 核 8 GiB) + │ 一次构建,三条出口 + ├──► 又拍云存储(境内源站) upx sync + upx purge + │ │ 多吉云回源自动跟随 + │ └──► 多吉云 CDN 刷新(API 精确刷新) + │ + └──► EdgeOne Pages(境外) npx edgeone pages deploy --area overseas +``` + +**保留不动**(零改动): +- 又拍云存储 `imzql`(境内源站) +- 多吉云 CDN(境内解析,回源又拍云) +- EdgeOne Pages 项目 `hugo-blog`(境外解析,`--area overseas`) +- 域名解析、ICP 备案、Ying 主题、content 内容 + +**替换掉**: +- Gitea Actions `build` job → CNB 流水线 +- act_runner → 不需要 +- COS / Gitea artifact 传递 → 不需要(含那串 v3/v4 坑) +- 广州中转机 `sync.sh` 轮询 → 不需要 +- `deploy.yml` 里 8 处 `github.server_url` 兼容分支 → 不需要(另写 `.cnb.yml`) + +--- + +## 三、CNB 能力核对(全部官方文档核实) + +| 能力 | 值 | 对本项目的意义 | +|---|---|---| +| 构建节点 | 默认 8 核 16 GiB(**内存 = CPU 核数 × 2**) | 全面优于广州机 2 核 1.9 G | +| 免费额度 | **160 核时/月**(0.125 元/核时超出) | 见下方换算 | +| 构建最大时长 | 20 小时;单任务无输出 10 分钟 / 超 1 小时触发超时(可声明,上限 12 小时) | 传 372MB 绰绰有余 | +| 自定义环境 | `docker.image` 或 `docker.build`(Dockerfile) | 可固定 hugo 0.128.2 extended | +| 密钥管理 | 密钥仓库 + `imports` 导入环境变量 | 又拍云/多吉云/EdgeOne 凭据不落仓库 | +| 定时任务 | 支持 cron,最小间隔 5 分钟,时区 Asia/Shanghai | 可替代每日定时构建 | +| **出网限制** | **无白名单限制**(官方:出口 IP 动态,明确不建议白名单) | 又拍云 / 多吉云 / EdgeOne API 都可直连 | +| Docker-in-Docker | 支持(`services: docker`) | 备用 | +| 官方集成 | 腾讯云官方有 **CNB → EdgeOne** 的部署文档示例(`npx edgeone makers deploy`) | 已验证路径 | + +### 额度换算(关键决策数字) + +| 规格 | 内存 | 每次构建耗(按 10 分钟) | 160 核时可用次数 | +|---|---|---|---| +| 8 核 | 16 GiB | 1.33 核时 | **120 次/月** | +| **4 核** | **8 GiB** | **0.67 核时** | **240 次/月** | +| 2 核 | 4 GiB | 0.33 核时 | 480 次/月 | + +你的触发频率是「每天 1 次定时 + 每次发文」≈ **60–90 次/月**。 + +**建议用 4 核 8 GiB** —— 240 次/月的余量,即使构建耗时翻倍到 20 分钟也还有 120 次。hugo 构建吃 CPU(4 核够并行编译),`upx sync` 吃网络不吃 CPU。 + +--- + +## 四、落地骨架 + +### 4.0 第一步:仓库迁移与「双远端推送」(CNB 主仓 + GitHub 备份) + +**目标形态** + +``` +origin → https://cnb.cool/<组织>/<仓库> ← 主仓(fetch + push),CNB 构建由它触发 +gh → git@github.com:zqlit/blog.git ← 备份(push),只存档不参与构建 +gitea → 自建 Gitea ← 暂时留着当只读参考,链路稳定后手工删 +``` + +一条命令推两段:`git pushall`(等价于 `git push origin main; git push gh main`)。 + +**★ 为什么不用「一个 remote 挂多个 pushurl」** + +现有 `origin` 就是那种写法(pushurl 同时挂了 GitHub 和 自建 Gitea)。但它有个被忽视的缺陷,我做了实测: + +| 场景 | 结果 | +|---|---| +| 两个 pushurl 都可用 | ✅ 两个都收到,一次 push 搞定 | +| **第一个失败** | ❌ **git 立即中止,第二个根本不会被推** | + +也就是说:**CNB 推失败时,GitHub 那份备份也不会更新** —— 备份的意义就没了。 +所以拆成两个独立 remote,别名里用 `;` 而不是 `&&`,保证互不阻塞。 + +**脚本**(已就位,可直接用) + +```bash +bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库> +``` + +它会先把你现有的远端配置(含明文凭据)备份到 `.workbuddy-backup/git-remotes.<时间戳>.txt`, +再重建 `origin`/`gh` 并注册 `pushall` 别名 —— 可回退。 + +**★ CNB 不支持 SSH(官方明确)** + +> SSH Not Supported —— 平台不支持 SSH 协议访问。 +> 认证方式:用户名**固定填 `cnb`**,密码填**访问令牌**。 +> —— docs.cnb.cool《Git Address and Authentication Instructions》 + +这跟 GitHub 的 SSH 不同,所以两段要各用各的认证: + +```bash +git config --global credential.helper manager # Windows 的 Git Credential Manager +# 首次 push 时:用户名 cnb,密码 = 访问令牌(个人设置 → 访问令牌 → 勾「代码仓库/读写」) +``` + +仓库地址直接用仓库页地址即可(`https://cnb.cool/组/仓`),带不带 `.git` 都支持。 + +**建仓顺序不能乱**(官方要求) + +1. 微信扫码注册 → 2. 微信扫码**实名认证**(未实名禁止建仓/推码)→ 3. 先建**组织**(CNB 无「个人私有仓库」,仓库必须挂在组织下)→ 4. 建仓库 → 5. 跑上面的脚本 + push + +### ⚠️ 推 CNB 之前必须先知道的敏感文件(已审计) + +这些文件**当前就被 git 跟踪**,会在首次 push 时连**全部历史**一起进 CNB: + +| 文件 | 内容 | 状态 | +|---|---|---| +| `write-server/nginx/ssl/privkey.pem` | **真实 TLS 私钥**(EC P-256,`BEGIN PRIVATE KEY`),2026-06-22 起在库 | 已跟踪 | +| `.env` | `SILICONFLOW_API_KEY`、`ZHIPU_API_KEY` | 已跟踪 | +| `GITEA_SECRETS.md` | Gitea 相关凭据说明 | 已跟踪 | +| `content/posts/2024/.../setup-secrets.png` | 疑似密钥配置截图 | 已跟踪 | + +**好消息**:已核实 `https://github.com/zqlit/blog` 返回 **Page not found**,仓库不可公开访问 +→ **私钥没有被公开暴露**,无需紧急处置。 + +**但两件事建议顺手做掉**: + +1. **CNB 仓库建成「私有」** —— 这些文件在私有仓里影响可控 +2. **`git rm --cached` 停止继续跟踪**(历史里的删不掉,但至少不再新增): + ```bash + git rm --cached .env GITEA_SECRETS.md write-server/nginx/ssl/privkey.pem write-server/nginx/ssl/fullchain.pem + ``` + ⚠️ 注意:`.gitignore` 里其实**已经有** `.env` / `.env.*` 规则 —— 它们失效的原因不是规则写错, + 而是**忽略规则对「已跟踪」文件无效**,所以必须显式 `git rm --cached`。 + (另:`.gitignore` 里那条 `write\.env` 是写错的 —— gitignore 中 `\` 是转义符, + 它实际匹配文件名 `write.env` 而非 `write/.env`;幸好 `write/.gitignore` 里有 `.env*` 兜住了。) + +**不建议现在做历史清理**(`git filter-repo`):会重写 955 个提交、让 GitHub 备份随之分叉, +收益(去掉一个未公开的私钥)远小于风险。正确做法是**轮换凭据**而不是重写历史。 + +--- + +### 4.1 `deploy/Dockerfile`(构建环境) + +CNB 默认镜像不含 hugo / upx,写一个专属镜像固定版本: + +```dockerfile +FROM node:22-bookworm-slim + +# 换国内 apt 源(CNB 只明示加速 Docker/NPM/Maven,apt 默认源在国内节点可能慢) +RUN set -eux; \ + for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources; do \ + if [ -f "$f" ]; then \ + sed -i -e 's|deb.debian.org|mirrors.aliyun.com|g' \ + -e 's|security.debian.org|mirrors.aliyun.com|g' "$f"; \ + fi; \ + done; \ + apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl tar zstd python3 xz-utils git \ + && rm -rf /var/lib/apt/lists/* + +# Hugo extended 0.128.2(linux/amd64)——★ 随仓库携带,构建时零跨境下载 +COPY bin/linux/hugo /usr/local/bin/hugo +RUN chmod +x /usr/local/bin/hugo && hugo version + +# 又拍云 upx +RUN set -eux; \ + curl -fsSL -o /tmp/upx.tgz \ + "https://collection.b0.upaiyun.com/softwares/upx/upx_0.4.9_linux_amd64.tar.gz"; \ + tar -xzf /tmp/upx.tgz -C /usr/local/bin upx; \ + chmod +x /usr/local/bin/upx; \ + rm /tmp/upx.tgz + +# EdgeOne CLI +RUN npm i -g edgeone@latest + +CMD ["hugo", "version"] +``` + +> ✅ 已实测(2026-10-04): +> 1. **hugo 二进制改为随仓库携带**(`bin/linux/hugo`,84MB,进 git 后仅 24MB)。实测国内节点直连 GitHub 拉这个 release 要 **127 秒**(本机只要 7 秒),而每次构建都要付这个成本 → 自带更稳。 +> 2. 又拍云 `upx` 的下载源是 `collection.b0.upaiyun.com`(国内 CDN),直连稳定,保留在线安装。 +> 3. **架构不能混用**:`E:\Hugo\hugo.exe` 是 `windows/amd64`(本地预览用),CNB 构建节点是 `linux/amd64`(`bin/linux/hugo`),两者互不替代。 +> 4. 若以后主仓改选 **Gitee(单仓 500MB)**,这 84MB(git 内 24MB)会挤占配额,届时改用 CNB/又拍云对象存储托管该二进制,别放仓库。 +> 5. **apt 源改阿里云镜像**:CNB 官方只明示加速「Docker / NPM / Maven 镜像」,Debian 默认 apt 源不在承诺范围内,而构建节点在国内 → 换阿里云兜底,避免构建卡在 `apt-get update`(首轮构建时留意这一步耗时)。 + +### 4.2 `.cnb.yml`(流水线) + +```yaml +main: + push: + - name: 构建并发布(境内外双线路) + imports: + # 密钥仓库(私有),存放 UPYUN_* / DOGE_* / EDGEONE_API_TOKEN + - https://cnb.cool/<你的组织>/<密钥仓库>/-/blob/main/secrets.yml + runner: + cpus: 4 # 4 核 → 8 GiB + docker: + build: deploy/Dockerfile + stages: + - name: Hugo 构建 + script: | + set -euo pipefail + hugo version + python3 scripts/add_draft_to_hidden.py + rm -rf public resources/_gen + hugo --minify --gc + BUILD_HASH=$(find public -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}') + echo "$BUILD_HASH" > public/.build_hash + echo "$BUILD_HASH" > public/build_hash.txt + echo "$BUILD_HASH" > .build_hash.out + echo "✅ 构建完成 hash=${BUILD_HASH:0:12} 文件数=$(find public -type f | wc -l)" + + - name: 同步到又拍云(境内源站) + script: | + set -euo pipefail + upx login "$UPYUN_SERVICE" "$UPYUN_OPERATOR" "$UPYUN_PASSWORD" + upx sync public/ / -w 5 --delete + upx put public/.build_hash /__build_hash || true + echo "✅ 又拍云同步完成" + + - name: 刷新又拍云 CDN + script: | + set -euo pipefail + # ★ 必须刷固定入口页:只刷首页会让 sitemap.xml / rss.xml / + # archives.html 一直发 CDN 里的旧副本(2026-10-01 实测复现) + { echo "https://usj.cc/"; + for p in sitemap.xml rss.xml archives.html posts.html comment.html links.html; do + echo "https://usj.cc/$p" + done + } > /tmp/purge.txt + upx purge --list /tmp/purge.txt \ + || echo "⚠️ purge 失败,降级不影响源站已更新" + + - name: 刷新多吉云 CDN + script: | + set -euo pipefail + python3 - <<'PY' + import hmac, hashlib, json, os, urllib.request + AK, SK = os.environ['DOGE_AK'], os.environ['DOGE_SK'] + SITE = 'https://usj.cc' + P = '/cdn/refresh/add.json' + urls = [SITE + '/', SITE + '/sitemap.xml', SITE + '/rss.xml', + SITE + '/archives.html', SITE + '/posts.html'] + def refresh(rtype, u, label): + body = json.dumps({'rtype': rtype, 'urls': json.dumps(u)}) + sig = hmac.new(SK.encode(), (P + '\n' + body).encode(), hashlib.sha1).hexdigest() + req = urllib.request.Request( + 'https://api.dogecloud.com' + P, data=body.encode(), + headers={'Authorization': 'TOKEN %s:%s' % (AK, sig), + 'Content-Type': 'application/json'}, method='POST') + with urllib.request.urlopen(req, timeout=30) as r: + print('%s -> %s' % (label, r.read().decode()[:200])) + refresh('path', [SITE + '/'], '目录刷新') + refresh('url', urls, '精确刷新 %d 条' % len(urls)) + PY + + - name: 部署 EdgeOne(境外线路) + script: | + set -euo pipefail + test -n "$EDGEONE_API_TOKEN" || { echo "缺 EDGEONE_API_TOKEN"; exit 1; } + npx edgeone pages deploy ./public -n hugo-blog -t "$EDGEONE_API_TOKEN" --area overseas + + - name: 上报部署状态 + script: | + set -euo pipefail + H=$(cat .build_hash.out) + curl -s --max-time 20 -X POST \ + "https://api.200181.xyz/api/deploy-status?token=${RSS_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"hash\":\"$H\",\"phase\":\"built\"}" || true + curl -s --max-time 20 -X POST \ + "https://api.200181.xyz/api/deploy-notify?token=${RSS_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"hash\":\"$H\"}" || true +``` + +--- + +## 五、两个必须复刻的细节(`sync.sh` 里的血泪) + +### 5.1 purge 不能只刷首页 + +`sync.sh` 的注释记录了一个实测坑: + +> 历史写法只有 `upx purge "$SITE/"`,即只刷首页 —— `sitemap.xml` / `rss.xml` / +> `archives.html` / `posts.html` 这些「老路径」会一直发 CDN 里的旧副本。 +> 2026-10-01 实测复现:中转机产物 sitemap.xml 已含新文章(22812B), +> CDN 上那份却仍是 7/24 的 22592B;手动 purge 后立刻变新。 + +所以骨架里固定刷「首页 + 6 个入口页」。**进阶做法**(完整复刻 `sync.sh` 的精确刷新): +把文件清单存到又拍云 `/__manifest.txt`,每次 CNB 拉下来与本次产物比对,得出「变化 URL」精确刷新。 +好处是新增文章时只刷那几页;代价是多维护一个清单文件。**建议先跑固定入口版,观察 1–2 周再决定要不要加。** + +### 5.2 又拍云并发参数 + +`sync.sh` 用 `-w 10 --strong`。当时失败是因为**跨境**(境外 runner 直传),国内直传理论无此问题。 +但建议首次仍保守用 `-w 5`(去掉 `--strong`,它会把请求量放大),实测稳定后再往上调。 + +--- + +## 六、砍掉清单 + +| 砍掉 | 说明 | +|---|---| +| act_runner | 不再需要自建 runner | +| Gitea artifact 传递链 | 含 `upload-artifact@v3/v4` 那串坑 | +| 腾讯云 COS | 中转存储不再需要 | +| 广州中转机 `sync.sh` + 1Panel 定时任务 | **注意:只停任务,机器上还有 mysql/openresty 等 7 个容器** | +| `deploy.yml` 的 8 处 `github.server_url` 补丁分支 | `.cnb.yml` 重新写 | +| 两套冗余通知(TG/飞书/邮件三选一) | 独立小优化 | + +**发布链路:7 环节 → 3 环节**(写作 → CNB 构建 → 双线上线) +**自维护构建节点:1 台 → 0 台** + +--- + +## 七、前置动作与待核实项 + +### 必须先确认(按重要性) + +1. **★ EdgeOne 从国内上传 372MB 的耗时**。现在部署是从境外 runner 上传(同区域快),改到 CNB(国内)后是跨境上传到 EdgeOne 国际站。**这是本方案最大的未知数**,必须实测一次。 +2. **★ CNB 构建节点能否顺畅拉取 `github.com` 的 hugo release**。不行就换国内镜像源,或把 hugo 二进制预置进 CNB 制品库。 +3. **又拍云 v0 接口从 CNB 出口的真实表现**。国内直传预期无碍,但需实测确认(这是整个方案的立论基础,务必第一次就验)。 +4. 主仓放哪: + - **CNB 当主仓** → 最简,Gitea 可退役,写作后台改 `origin` + - **Gitea 当主仓 + push mirror 到 CNB** → 写作零改动,代价是多留一台机器 +5. 多吉云刷新配额(`sync.sh` 里有 200 条截断逻辑,CNB 版本固定入口页不触及)。 + +### 建议的验证步骤(不动任何现有东西) + +1. 建一个 CNB 测试仓库(或就用一个分支),放最小化 `.cnb.yml` + Dockerfile +2. 跑通「hugo build」→ 确认镜像与构建时长 +3. 单独测「`upx sync` 到一个测试 bucket」→ 确认国内直传可行 +4. 单独测「`edgeone pages deploy` 到测试项目」→ 拿到跨境上传耗时 +5. 四项全绿,才动正式仓库 + +--- + +## 八、与之前几份方案的关系 + +| 方案 | 结论 | 与本方案的关系 | +|---|---|---| +| `代码源与构建平台选型.md` | CNB 首选(100GiB 仓库 + 160 核时) | 本方案是它的落地细化 | +| `Gitee方案评估.md` | 可用,但需瘦身 588MB → 500MB | **用 CNB 就不用瘦身** | +| `EdgeOne双区域方案评估.md` | 建议 `-a global` 或双项目 | 双区域照旧,只是构建方换成 CNB | +| `阿里云ESA评估.md` | 2000 文件上限卡死 | 不适用 | +| `精简方案-只留CF和Hugo.md` | 只留 CF 会丢又拍云+多吉云 | **本方案保留双 CDN,复杂度收益同样大** | + +**核心区别**:之前几份在讨论「CDN 选谁」;本方案证明**你不需要换 CDN,只需要换掉「构建 + 搬运」那一层**。而这一层恰恰是复杂度的大头。 diff --git a/EdgeOne双区域方案评估.md b/EdgeOne双区域方案评估.md new file mode 100644 index 00000000..2302f92d --- /dev/null +++ b/EdgeOne双区域方案评估.md @@ -0,0 +1,234 @@ +# EdgeOne 双区域方案评估 + +> 2026-10-04 · 针对「境外用 EdgeOne 国际站、境内改用 EdgeOne 国内站」的构想 +> 以及「write-server 拆成后端服务 + 前端与 blog-admin/Worker 合并」的可行性 + +## 一、结论先说 + +**方案成立,而且比 Cloudflare 方案好。** 三个理由: + +1. **不违反任何服务条款** —— CF 要加速国内只能走「优选 IP」那条明文违规的路,EdgeOne 是正规产品能力。 +2. **国内速度是量级差异** —— 备案域名的 EdgeOne 大陆节点实测 **50–90ms**;你现在 CF 后端实测 **750ms**(差 8–10 倍)。 +3. **你已经在用了** —— `deploy-edgeone` job 现在就部署在这上面(`edgeone pages deploy --area overseas`),迁移成本接近零。 + +**而且我发现了你可能没注意到的一件事**:EdgeOne Pages 有个 `--area` 参数,你现在用的是 `--area overseas`(**主动排除了中国大陆**)。这可能意味着你**只要改一个参数**就能达到目的,而不必新建一个项目。详见第三章。 + +--- + +## 二、你的备案状态(关键前提,已核实) + +- 站点页脚:**鄂ICP备2022015735号-1** +- 官方文档明确:*当项目的加速区域为「中国大陆可用区」或「全球可用区(含中国大陆)」时,添加的域名必须完成 ICP 备案* +- **你的域名已备案 → 具备启用大陆节点的资格** ✅ + +这一条是整个方案成立的地基。反过来说,如果没备案,这条路直接堵死(只能用海外节点,国内 670–1400ms)。 + +--- + +## 三、★ 关键发现:`--area` 参数 + +EdgeOne Pages 的 CLI 部署命令带一个区域参数: + +| 参数 | 含义 | +|---|---| +| `--area overseas` | 只覆盖境外,**不含中国大陆** ← **你现在用的就是这个** | +| `-a global` | 覆盖全球 **+ 中国大陆**(需备案域名绑自定义域名) | + +也就是说,你现在这个 `hugo-blog` 项目**是刻意把大陆排除在外的**。于是有两条路: + +### 路 1(最省):单项目改参数 + +把 CI 里那一行改成 `-a global`,一个项目、一套配置、一个 token,同时覆盖境内外。 + +- 好处:**零新增组件**,改动只有一个参数 +- 风险:**境外质量可能下降**。有多位站长实测反馈,global 模式下「海外就差点意思,只有一个节点,走 Anycast 网络」——这可能正是你当初选 `overseas` 的原因 +- **必须实测**:改完之后用境外节点测一次,别只看国内变快了 + +### 路 2(你提的):双项目 + +- **国际站项目**(`console.intl.cloud.tencent.com`,免备案)→ `--area overseas`,保境外质量 +- **国内站项目**(`console.cloud.tencent.com`,需实名+备案)→ `-a global`,保大陆速度 +- 两个控制台、两套 token、两次部署 + +> ⚠️ **一个待实测的疑点**:官方文档里,国际站的 Pages 与国内站的 Pages 对「大陆节点」的支持描述**互相矛盾**(国际站文档说 `-a global` 含大陆,产品介绍又说国际站不含国内节点)。**结论不明,必须自己测一次**。测法见第七章。 + +--- + +## 四、EdgeOne Pages 免费额度(官方定价页) + +| 项目 | 免费额度 | 你的用量 | 够吗 | +|---|---|---|---| +| 安全加速流量 | **不限** | — | ✅ | +| 安全加速请求数 | **不限** | — | ✅ | +| 构建次数 | **500 次/月** | 约 60–90 次 | ✅ | +| Edge Functions 请求 | 300 万/月 | — | ✅ | +| Cloud Functions 请求 | 100 万/月 | — | ✅ | +| KV 存储 | 1 GB | — | ✅ | +| 站点数 | 1 个主域名 + 200 子域名 | — | ✅ | +| 超出用量 | **收费版推出前不中断服务** | — | ✅ | + +**唯一的硬限制(重要)**: + +> **单文件、单线程限速 4 Mbps(约 500 KB/s)** —— 多位站长实测确认,官方未披露。 + +要正确理解它: +- 是**单文件单线程**限速,**不是整站带宽限速**。并发请求互不影响 +- 你的 `zql-v2.woff2` 字体 1.2 MB → 首次加载约 **2.4 秒**(之后走缓存,不再走网络) +- 那 7 个 mp4 里最大的 12.8 MB → 首次加载约 **25 秒**(这是这个方案最实际的痛点) +- 腾讯云自家 CDN 也有这个限制,属地是「免费套餐的取舍」,不是 bug + +**如果视频是核心内容**,这一条要认真权衡;如果只是少量点缀,影响可控。 + +--- + +## 五、能砍掉什么 + +对比**现在的架构**(EdgeOne 国际 + 又拍云 + 多吉云 + 广州中转机 + sync.sh 轮询): + +| 组件 | 现架构 | EdgeOne 双区域 | +|---|---|---| +| 境外 CDN | EdgeOne 国际站 | EdgeOne 国际站(不变) | +| 境内 CDN | 又拍云 + 多吉云 | **EdgeOne 国内站** | +| 产物搬运 | 广州中转机 + `sync.sh` 每分钟轮询 | **删除** | +| 产物中转 | 腾讯云 COS | 已砍(进行中) | +| 凭据面 | 又拍云密码 + 多吉云 SK(明文写在 sync.sh) | **删除** | +| 厂商数 | EdgeOne + 又拍云 + 多吉云 + 腾讯云 COS | **腾讯云一家** | + +**净收益**:少 2 家厂商、少 1 台常驻机器、少 1 个每分钟跑的轮询脚本、少 2 套硬编码凭据。 +**发布链路从 7 环节降到 4 环节**(写作 → 构建 → 双区域部署 → 通知)。 + +--- + +## 六、write-server 拆分评估(你问的第二件事) + +### 6.1 结论:技术可行,而且**必须拆**才能上边缘平台 + +这不是"能不能"的问题,而是**只能这样**。EdgeOne Pages(和 CF Workers 一样)的运行时限制是硬的: + +- **无持久化文件系统** —— 无法读写 `content/` 下的 `.md` +- **不能执行 git** —— 无法 `git pull/push` +- 单次函数执行有超时(通常 30s) + +而 `write-server` 的核心恰恰是这两件事:**读写本地 md 文件 + git 操作**。 + +### 6.2 现状的耦合点(已扫描) + +``` +write-server/src/ + app/{edit,login,posts,images,links,feeds,comments,users,bot,wechat}/ ← 页面(可上前端) + app/api/ ← 23 个 API 路由 + lib/ + posts.ts ← fs 读写 md ⛔ 必须留后端 + git.ts ← child_process git ⛔ 必须留后端 + hugo.ts ← 调 hugo 二进制 ⛔ 必须留后端 + recycle.ts ← fs ⛔ 必须留后端 + auth.ts ← fs(用户表) ⛔ 必须留后端 + bot/* ← 子进程轮询 ⛔ 必须留后端 + ai.ts / artalk.ts / rssapi.ts / wechat.ts / fetch.ts ← 纯 HTTP(可两边) +``` + +**好消息**:页面与 API 已经分得比较清楚,`src/lib` 里哪些能上边缘、哪些必须留后端,界限是清晰的。这不是一个烂摊子。 + +### 6.3 拆分后的形态 + +``` +前端(EdgeOne Pages / CF Worker) + React SPA(由现有页面改造)+ 调用后端 API + │ + ▼ +后端(一台有文件系统的服务器) + Node 服务 + 复用现有 src/lib(几乎不用改) + → 读写 md、跑 git、调 hugo、发 RSS +``` + +必须改的四件事: +1. **认证**:现在是同域 session/cookie,分离后要改成 token(Bearer / JWT)+ CORS +2. **数据获取**:服务端组件直读 md → 改成客户端 fetch API(多一次往返,但要加 loading 态) +3. **图片上传**:写本地磁盘 → 改成对象存储(又拍云 / R2 / EdgeOne KV) +4. **API 地址配置化**:`src/lib/config.ts` 里加后端 base URL + +### 6.4 ⚠️ 但先问一句:**为什么要拆?** + +如果目的是**"一个统一的管理入口"**(体验问题),那拆分会**让架构变复杂**——从 1 个应用变成 2 个(前端 + 后端),组件数不减反增。这跟你要的"简化"是反方向。 + +**更省的做法(零重构)**:用**一个域名 + 路径分流**把两个后台挂在一起。 + +``` +admin.usj.cc/ → blog-admin(CF Worker,评论/RSS 管理) +admin.usj.cc/write/ → write-server(写作后台) +``` + +三种实现,任选: +- **OpenResty 反代**(你国内机已经有):加几行 `location /write/ { proxy_pass ...; }`,**成本最低** +- **EdgeOne 规则引擎**:用 URL 重定向 / 路径规则做分流 +- **CF Worker 薄代理层**:能做,但 CF 国内慢,不适合做入口 + +**判断标准**: +- 只要"一个入口" → **反代,别重构** +- 真的需要"前端独立部署到边缘、后端瘦成纯 API" → 才做拆分,且要接受工作量和组件数增加 + +--- + +## 七、建议的推进顺序 + +### 第 0 步(今天就能做):一次实验,验证单项目可行性 + +这是**成本最低、信息量最大**的一步,只需改一个参数: + +```bash +# 用已有的 token,把产物部署到 global 区域(覆盖大陆)测一次 +npx edgeone pages deploy ./public -n hugo-blog -t "$EDGEONE_API_TOKEN" -a global +``` + +然后用国内机实测(可复用我之前写的 `probe_speed.py` 那套): +- 大陆解析到的 IP 是不是国内 IP +- TTFB 是否降到 100ms 级 + +**如果单项目 `-a global` 就达标 → 方案收敛为「改一个参数」,收工。** +如果不达标或影响境外 → 再走双项目。 + +### 第 1 步:确定区域策略 + +根据第 0 步结果,二选一: +- 单项目 `-a global` +- 双项目(国际站 + 国内站,两套 token) + +### 第 2 步:接上大陆域名 + +国内站的 Pages 项目绑定自定义域名,走 ICP 备案校验(你已有备案号)。 + +### 第 3 步:砍掉旧链路 + +确认 EdgeOne 双区域稳定运行 **1–2 周**后,再依次停掉: +- 广州中转机的 `sync.sh` 定时任务(1Panel 里 `id=15` 那个每分钟的) +- 又拍云、多吉云的同步逻辑 +- `deploy.yml` 里对应的通知与检查步骤 + +**不要跳步**,也不要和"write-server 重构"混在同一次变更里。 + +### 第 4 步(可选,独立评估):write-server + +先回答 6.4 那个问题。如果要拆,它是一次独立的重构工程,单独排期。 + +--- + +## 八、待核实项(我标出来,不装作确定) + +| # | 事项 | 状态 | 怎么验 | +|---|---|---|---| +| 1 | 国际站 Pages 的 `-a global` 是否真能启用大陆节点 | **文档矛盾,待实测** | 第七章第 0 步 | +| 2 | `--area overseas` 当初是否是刻意选择(境外质量考虑) | 待你确认 | 问你自己 | +| 3 | 500KB/s 单文件限速对 7 个 mp4 的实际影响 | 已知限制,待评估 | 测一个最大视频的首字节 | +| 4 | 国内站的备案接入校验是否需要变更接入商 | 待核实 | 绑定域名时会暴露 | +| 5 | EdgeOne Pages 的 Git 集成是否支持 Gitee | 有第三方提到,**未官方确认** | 若支持,可再砍掉 Gitea + act_runner | + +> 第 5 条如果成立,价值很大:**Gitee 是国内平台**,用它的 Git 集成自动构建, +> 就能把自建 Gitea + act_runner + 中转机整条链一起砍掉。但需要先确认支持情况。 + +--- + +## 九、一句话总结 + +**你的方向是对的,而且比 CF 方案好——不违规、快 8–10 倍、你已经在用了。** +**但在动手建第二个项目之前,先花十分钟做第七章那个「改一个参数」的实验。** +它可能直接告诉你:**这件事根本不需要两个项目。** diff --git a/Gitee方案评估.md b/Gitee方案评估.md new file mode 100644 index 00000000..cab2b5a5 --- /dev/null +++ b/Gitee方案评估.md @@ -0,0 +1,156 @@ +# Gitee 方案评估 + +> 回答:**「Gitee 算不算一个替代方案?」** +> +> **结论**:算,而且它是唯一一个**官方支持作为 EdgeOne Pages 代码源**的国内平台。 +> 但有一道硬门槛 —— **Gitee 免费版单仓库上限 500MB,你的 `.git` 实测 588MB,超了 17.6%**。 +> 好消息是:**这道门槛跨得过去**(见第四节)。真正要权衡的是另一件事 —— **内容审查**。 + +--- + +## 一、实测数据(刚量的,不是估算) + +| 指标 | 实测值 | +|---|---| +| `.git` 总体积 | **588 MB** | +| `git count-objects` size-pack | **580 MB**(对象净体积,说明已经打包过了) | +| 提交总数 | 955 | +| **HEAD 中被跟踪文件合计** | **372.9 MB / 2427 个文件** | +| HEAD 中 >1MB 的文件 | 65 个,合计 143.9 MB | + +**关键推论**:`580MB(pack) − 373MB(HEAD) ≈ 207MB` 是**历史里的旧版本 / 已删除对象**。 +也就是说,**光留当前状态只需要 373MB** —— 这一条决定了瘦身可行。 + +--- + +## 二、四条硬指标逐项核对 + +Gitee 官方配额(help.gitee.com/account/usage-quota): + +| Gitee 免费版限制 | 官方值 | 你的情况 | 判定 | +|---|---|---|---| +| 单仓库容量 | **≤ 500 MB** | 588 MB | ❌ **超限 17.6%** | +| 单文件大小 | ≤ 50 MB | 最大 12.8 MB(mp4) | ✅ 安全 | +| 用户总仓库容量 | 5 GB | 588 MB(瘦身后 373 MB) | ✅ 安全 | +| 私有仓库协作人数 | 5 人 | 个人 | ✅ 无关 | +| 仓库数量 | 1000 个 | 1 个 | ✅ 无关 | + +**只有一条不过:仓库体积。** 而且它刚好卡在一条"能解"的线上。 + +--- + +## 三、一个被大多数人算错的点:Gitee 方案**不消耗** Gitee Go 的额度 + +Gitee Go 免费额度很小(单仓库 200 分钟永久 + 每月 500 分钟)。**但这跟你的方案无关** —— + +因为你走的是 **EdgeOne Pages 的 Git 集成**:代码放 Gitee,**构建跑在 EdgeOne 侧**(4 核 6GB,500 次/月)。 +Gitee 在这里**只当代码网盘**,一次构建都不消耗它的 CI 分钟。 + +> ⚠️ 同理:**不要用 Gitee Pages**。它的构建能力和额度都远不如 EdgeOne, +> 而且 2022 年那次全站审查的触发点之一就是 Pages。 + +--- + +## 四、唯一的硬伤与解法:588MB → 500MB 以下 + +`.git` 588MB,其中 **207MB 是历史垃圾**(旧版本 + 已删除文件)。三种解法: + +### 方案 A:只保留当前状态(最彻底,推荐) +重建一个只有 HEAD 的仓库 → `.git` 降到 **≈ 373MB**,**留 25% 余量**。 + +代价:**955 个提交历史会丢**。 +但这在你这儿几乎无损失 —— 历史仍在自建 Gitea 那边完整保留,Gitee 只当"构建用的镜像"。 + +```bash +# 思路(务必先在副本上试,不要直接动主仓库) +git checkout --orphan clean-tmp +git add -A && git commit -m "squash: 当前站点状态" +git branch -D main && git branch -m main +git reflog expire --expire=now --all && git gc --prune=now +``` + +### 方案 B:保留历史,只清理历史中的大文件 +`git filter-repo --analyze` 先定位占空间的历史大文件,再针对性剔除。 +体积介于 373–580MB 之间,**需要实测才知道能不能压到 500MB 以下**,不保证成功。 + +### 方案 C:升级 Gitee 付费版 +标准版单仓库 ≤ 1GB,1298 元/年起 —— **为了一个博客每年花 1300,不值得**。 + +**结论:只有方案 A 是确定可行的,而且可接受。** + +--- + +## 五、真正要权衡的:内容审查(这条比 500MB 更重要) + +Gitee 不是中立的文件存储,它有**实名认证 + 内容审查体系**。有实锤的历史: + +- **2022-05-19,Gitee 把全部约 1000 万个公开仓库临时私有化,做强制审核**,合规的才恢复。 + 开发者必须逐个提交申请、人工复核。有用户 24 个仓库里被恢复了 22 个。 +- 审查范围包括仓库名、注释、变量名 —— 有开发者抱怨"写代码时还要想这个词会不会触发敏感词列表"。 +- 常见封号原因:仓库含违规内容(赌博/诈骗/侵权/涉政)、批量建仓被判滥用、开 Pages 发不合规内容。 +- Gitee 数据全部境内存储,实名是硬要求(这是它拿政府/信创订单的前提)。 + +### 但落到你身上,风险评估是**低**的 + +理由三条: + +1. **你的内容是技术博客** —— Android 模块化魔改、K3s、Docker、自建服务。这类内容不在敏感区间。 +2. **把仓库设为私有**。审查压力的大头在**公开仓库**上(2022 那次动的就是公开仓库)。 + 你要用 EdgeOne 构建,私有仓库完全可以(授权访问即可)。 +3. **国内平台在"政策风险"上对你反而是加分项** —— 你 GitHub 账号被标记, + 正说明境外平台对你有不确定性。Gitee/CNB 的规则是**明确、可预期**的: + 别传违禁内容就不会出事,而不是"不知道哪天被风控"。 + +--- + +## 六、三个国内/境外代码源横向对比 + +| | **Gitee** | **CNB(cnb.cool)** | GitLab.com | +|---|---|---|---| +| 仓库容量 | 500 MB ⚠️ **需瘦身** | **100 GiB** ✅ 不用动 | 2 GB ✅ | +| EdgeOne Pages 官方支持 | ✅ 明确支持 | ✅(有官方插件文档) | ✅ 明确支持 | +| 构建额度消耗 | 不消耗(构建在 EO 侧) | 不消耗 / 或用它自己的 160 核时 | 不消耗 | +| 登录 | 实名注册 | 微信扫码 | 邮箱 | +| 国内推送速度 | 快 | 快 | 跨境,588MB 首次推送慢 | +| 内容审查 | ⚠️ 有实锤事件 | ⚠️ 官方明写"平台级内容安全审查" | 无(但美国平台) | +| 政策风险 | 低(国内合规) | 低 | ⚠️ **与 GitHub 同源风险** | +| 与 EdgeOne 的距离 | 第三方 | **同为腾讯体系** | 第三方 | + +### 排序建议 + +1. **CNB** —— 100GiB 不用瘦身,零改造,和 EdgeOne 同属腾讯、有官方集成文档 +2. **Gitee** —— 官方支持、国内快,但**要先做一次历史瘦身**(一次性工作,可接受) +3. **GitLab.com** —— 容量够,但**美国平台,和 GitHub 同类风险**,且首次推送跨境 588MB 很慢 + +**Gitee 输给 CNB 的唯一一点就是那 500MB**,如果你不想动仓库历史,选 CNB; +如果你更信任 Gitee 这个老牌子、也愿意做一次瘦身,Gitee 完全可用。 + +--- + +## 七、Gitee 方案的完整形态 + +``` +write-server / 本地 hugo → push Gitee(私有仓库) + → EdgeOne Pages Git 集成自动构建(4核6G,500 次/月,不消耗 Gitee CI 额度) + → EdgeOne 国际站(境外解析) + EdgeOne 国内站 Makers(境内解析,绑备案域名) +``` + +砍掉:act_runner、广州中转机、sync.sh、COS、Gitea artifact 链、8 处 Gitea 兼容分支、 +又拍云、多吉云、两套冗余通知。 +**3 台机器 → 0 台;7 环节 → 3 环节。** + +--- + +## 八、前置动作(按顺序) + +1. **先备份**:整仓 `cp -r` 一份(或 `git bundle create` 全量) +2. **在副本上做瘦身**,验证 `.git` 能压到 500MB 以下 +3. 注册 Gitee,建**私有**仓库,首次推送验证体积 +4. 在 EdgeOne Pages 里接 Gitee 仓库,跑一次构建 +5. 确认无误后,才把主仓 remote 切过去 + +**第 1、2 步不做,其余都别动。** + +--- + +*本文档基于 2026-10-04 实测数据与官方文档。仓库体积为 `git count-objects -vH` 实测,非估算。* diff --git a/bin/linux/hugo b/bin/linux/hugo new file mode 100644 index 00000000..df093255 Binary files /dev/null and b/bin/linux/hugo differ diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 00000000..6e2f2c27 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,47 @@ +# CNB 构建环境:固定 hugo 0.128.2 extended(与现网一致)+ 又拍云 upx + EdgeOne CLI +# +# ★ 为什么 hugo 二进制随仓库携带,而不是构建时在线下载: +# 实测国内节点直连 github.com 拉这 22MB 的 release 需要 127 秒(本机仅 7 秒), +# 而这是每次构建都要付的成本。随仓库携带后构建零跨境下载、100% 可复现。 +# 二进制体积 84MB,进 git 后仅 24MB(zlib),对 CNB 100GiB 免费额度可忽略。 +# +# ★ 注意架构:CNB 构建节点是 linux/amd64。 +# 本机 E:\Hugo\hugo.exe 是 windows/amd64,在容器里跑不了(会报 cannot execute +# binary file),所以必须是 linux 版。文件在 bin/linux/hugo。 +FROM node:22-bookworm-slim + +# ★ 换国内 apt 源:CNB 官方只明示加速「Docker / NPM / Maven 镜像」, +# Debian 的默认 apt 源(deb.debian.org)不在承诺范围内,而构建节点在国内, +# 直连默认源可能慢或超时 → 换成阿里云镜像兜底。 +# bookworm 镜像存在两代源文件格式(旧 /etc/apt/sources.list 与 +# 新 deb822 /etc/apt/sources.list.d/debian.sources),两个都改,避免格式差异踩空。 +RUN set -eux; \ + for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources; do \ + if [ -f "$f" ]; then \ + sed -i -e 's|deb.debian.org|mirrors.aliyun.com|g' \ + -e 's|security.debian.org|mirrors.aliyun.com|g' "$f"; \ + fi; \ + done; \ + apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl tar zstd python3 xz-utils git \ + && rm -rf /var/lib/apt/lists/* + +# Hugo extended 0.128.2(linux/amd64),随仓库提供 +COPY bin/linux/hugo /usr/local/bin/hugo +RUN chmod +x /usr/local/bin/hugo && hugo version + +# 又拍云 upx(源在国内 CDN,直连稳定,保留在线安装) +RUN set -eux; \ + curl -fsSL -o /tmp/upx.tgz \ + "https://collection.b0.upaiyun.com/softwares/upx/upx_0.4.9_linux_amd64.tar.gz"; \ + tar -xzf /tmp/upx.tgz -C /usr/local/bin upx; \ + chmod +x /usr/local/bin/upx; \ + rm /tmp/upx.tgz; \ + upx --version | head -1 + +# EdgeOne CLI(用于境内外两个 Pages 项目部署) +RUN npm i -g edgeone@latest + +WORKDIR /workspace + +CMD ["hugo", "version"] diff --git a/gitea-backup/迁移前检查清单.md b/gitea-backup/迁移前检查清单.md new file mode 100644 index 00000000..476e61e1 --- /dev/null +++ b/gitea-backup/迁移前检查清单.md @@ -0,0 +1,165 @@ +# 迁移前检查清单(2026-10-04 实测) + +> 目标:把境外机 `23.254.236.47` 上的 Gitea + runner 搬到**国内机**(当前中转机 +> `119.29.215.187`,Ubuntu 24.04)。 +> 下面是**照着做完再动手**的清单。带 ★ 的是会**直接卡住迁移**的硬伤。 + +--- + +## 一、★ 硬伤:compose 里 pin 的 Gitea 镜像 tag 不存在 + +`docker-compose.yml:29` + +```yaml +image: gitea/gitea:1.28.0-rootless # ❌ 这个 tag 不存在 +``` + +**原因**:**Gitea 28.0.0 起去掉了历史 `1.` 前缀** —— 原本的 `1.28.0` 现在就叫 `28.0.0`。 +境外那台 `/api/v1/version` 实测返回 `{"version":"28.0.0"}`。 + +**已在目标机实测(`docker pull`,决定性)**: + +| 镜像 tag | 结果 | +|---|---| +| `gitea/gitea:1.28.0-rootless` | ❌ `not found` | +| `gitea/gitea:28.0.0-rootless` | ✅ 可拉取 | +| `gitea/gitea:latest` | ✅ 可拉取 | +| `gitea/act_runner:0.2.11` | ✅ 可拉取 | +| `gitea/act_runner:4.0.0` | ❌ `not found` | + +**改**:`gitea/gitea:28.0.0-rootless`(建议 pin 死版本,别用 `latest`,免得下次再被动升级)。 +`28.0.0-rootless` 与 `latest` **已经拉到目标机上了**,迁移时省一次等待。 + +--- + +## 二、★ `.gitignore` 没忽略 `backups/` + +`scripts/backup.sh` 的产物落在 `backups/`: + +``` +backups/gitea-dump-<时间戳>.zip # gitea dump,含 secrets(加密但仍是敏感物) +backups/data-<时间戳>.tar.gz # 完整 data 卷:gitea.db / app.ini / runner 凭据 / upyun 目录 +``` + +而 `.gitignore` 里**只有** `.env` / `data/` / `*.log` —— **没有 `backups/`**。 +跑一次备份,这两个文件就出现在仓库里了,`git add .` 一下全进历史。 + +**改**:`.gitignore` 加一行 `backups/`。 + +--- + +## 三、凭据散落情况(迁移时容易漏) + +| 位置 | 内容 | 风险 | +|---|---|---| +| `gitea-backup/.env.example` | `UPYUN_BUCKET=imzql`、`UPYUN_OPERATOR=1770186415` | **真实值**,不是占位符 | +| `write-server/nginx/ssl/privkey.pem` | SSL 私钥 | **已被 git 跟踪**(仓库历史里有) | +| 中转机 `/opt/upyun-sync/sync.sh` | 又拍云密码 + 多吉云 AK/SK **明文硬编码** | 该文件会被整体搬进 `data/upyun-sync/` | +| 本地 `.git/config` 的 `gitea` remote | `http://用户:密码@...` | 明文写在 remote URL 里 | + +容器化后 `upyun-sync` 是用 `.env` 注入凭据的,但 `sync.sh` 里**又硬编码了一份** —— +两套并存,容易改一处漏一处。建议统一成只读环境变量。 + +--- + +## 四、容器网络:脚本要走服务名,别绕公网 + +`upyun-sync` 容器与 `gitea` 同在 `gitea-net` 这个 bridge 网络里。 +如果脚本里继续用 `https://gitea.usj.cc`,会**出到公网 DNS 再绕回来**(还可能撞上 CDN)。 + +**改**:`sync.sh` 里 `GITEA` 默认值改成 `http://gitea:3000`(compose 服务名 + 容器内端口)。 +脚本已经写成 `GITEA="${GITEA:-...}"`,用环境变量覆盖即可,不必改脚本。 + +--- + +## 五、反向代理要补一条 + +目标机上 **OpenResty 已占用 80/443**(1Panel 托管的站点)。 +`gitea` 容器映射的是宿主 `3001`(HTTP)/ `2222`(SSH),**这两个端口在目标机是空闲的** +(已实测)。 + +要让 `https://gitea.usj.cc` 生效,需在 1Panel 的 OpenResty 里加一个反代站点 +→ `127.0.0.1:3001`,并配好证书。DNS 也要把 `gitea.usj.cc` 指到目标机。 + +> ⚠️ Gitea 28 起**不再读取 `[server] DOMAIN`**,实例域名(含默认 SSH 域名)全部来自 `ROOT_URL`。 +> compose 里两个都填了,所以没问题;但改域名时**只需改 `ROOT_URL`**。 + +--- + +## 六、资源评估:内存是短板 + +目标机现状(实测): + +| 项 | 值 | +|---|---| +| CPU | **2 核** | +| 内存 | **1.9 G**(已用 665 M,free 126 M,buff/cache 1369 M) | +| **Swap** | 1987 M,**已用 832 M** ← 已经在吃 swap | +| 磁盘 | 50 G,已用 17 G,**剩 31 G** | +| 已在跑 | 1Panel(3721)、mysql 8.4、certimate、OpenResty(80/443)、frps、alist、vaultwarden | + +要再加进:**Gitea + act_runner + 一次 hugo 构建**(构建要跑 npm ci、hugo --minify、 +打包 ~380 MB 产物)。这台机器现在就已经在换页了,**2 G 内存很紧**。 + +磁盘侧估算(够用但不宽裕):Gitea 数据(仓库全历史,含大量图片)+ artifact +(~380 MB/次 × 保留 7 天)+ `data/upyun-sync/` 里的产物副本(~750 M)≈ **6–10 G**。 + +**建议**:迁移前把内存加到 **4 G**;或将 `mysql` 等非必需容器停掉腾资源。 + +--- + +## 七、★ 迁移后会变的架构(决定要不要保留中转机) + +`deploy.yml` 里 `Upload to UpYun` 那一步现在写死 `if: false`,注释原话: + +> 国内线路由腾讯云广州中转机兜底……**将来若把 runner 挪到国内,把下面的 `if: false` 删掉即可恢复。** + +也就是说 **runner 一旦在国内,又拍云直传就可行**(当初失败是因为境外出口被 GSLB 调度到坏节点)。 +那时整条链路可以简化成: + +``` +runner(国内) → build → 又拍云直传 → purge → 多吉云刷新 +``` + +**「中转机 + sync.sh + Gitea artifact 给中转机」这一层就可以整个删掉。** + +但有两个代价要权衡: + +1. **EdgeOne Pages 部署会变成跨境**(境内 runner → 境外 EdgeOne)。目前 runner 在境外, + 这一步是零跨境的。需要实测耗时是否可接受。 +2. 又拍云直传要补 **`upx purge`**(现在这一步在 sync.sh 里,deploy.yml 的直传步骤没有)。 + 多吉云刷新 finalize 里已有(`upyun_status == success` 时才刷)。 + +> 建议:**先按「保留 artifact 传递」把迁移做完**(artifact 在 build → deploy-edgeone +> 之间是必需的,无论如何都要留),跑通之后再单独决定要不要砍中转机。 +> 别把「迁移」和「架构简化」两件事混在一次变更里。 + +--- + +## 八、迁移顺序(照做) + +1. **先修本文档第一节的镜像 tag**,`docker compose up` 才不会卡在第一屏 +2. 补 `.gitignore` 的 `backups/` +3. 目标机:`mkdir -p data/gitea data/runner data/upyun-sync` +4. 旧机器 rsync 数据(沿用 README 的路径): + - `/opt/gitea/data/` → `data/gitea/` + - `/opt/upyun-sync/` → `data/upyun-sync/` + - ⚠️ 用 `rsync -a` 保留属主;Gitea rootless 镜像按 **uid/gid 1000** 跑, + 属主不对会起不来 +5. `cp .env.example .env` 并填好(`ROOT_URL` 必填) +6. **停掉 1Panel 的计划任务「又拍云同步(国内中转)」`id=15`** —— 否则 + 容器版 `upyun-sync` 与它**同时**同步,双写又拍云 +7. `docker compose up -d` → `docker compose ps` 三个服务 healthy +8. Gitea 后台重新拿 runner 注册 token → 填 `.env` → `up -d --force-recreate runner` +9. OpenResty 加反代站点 + DNS 切 `gitea.usj.cc` +10. 验证:`git push` 一次,看 Actions 是否正常构建部署 + +--- + +## 九、尚未核实的两点 + +- `scripts/backup.sh` 里 `gitea dump --config ... -R -S`:这两个参数的语义需核对 + (担心是「跳过仓库」之类,导致 dump 不完整)。**建议实跑一次备份,检查 zip 里有没有 + `repositories/`**。 +- 旧机器上的 `docker-compose.yml` 里 Gitea 用的到底是哪个 tag(`latest` 还是 pin 的版本)—— + 直接把 `:latest` 的数据目录搬到一个 pin 死的旧版本上会有兼容风险,**版本必须 ≥ 旧机**。 diff --git a/scripts/setup-cnb-remotes.sh b/scripts/setup-cnb-remotes.sh new file mode 100644 index 00000000..9805f81a --- /dev/null +++ b/scripts/setup-cnb-remotes.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# 把主仓从「GitHub + 自建 Gitea 双推」切换成「CNB 主仓 + GitHub 备份」 +# +# 为什么不用「一个 remote 挂多个 pushurl」: +# 实测(2026-10-04)git 对多个 pushurl 是「顺序推、遇错即停」—— +# 第一个失败,后面的远端一个都不会推。那就失去备份意义了。 +# 故拆成两个 remote + 一个 alias,两段各自独立、互不阻塞。 +# +# 用法:bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库> +# 附带令牌(可直接把凭据写进系统凭据管理器): +# CNB_TOKEN='你的令牌' bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库> +# 回退:脚本会把改动前的配置备份到 .workbuddy-backup/git-remotes.<时间戳>.txt + +set -euo pipefail + +CNB_URL="${1:-}" +GH_URL="${GH_URL:-git@github.com:zqlit/blog.git}" + +if [ -z "$CNB_URL" ]; then + cat <<'USAGE' +用法:bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库> + +提示: + · CNB 仓库地址直接用仓库页地址,带不带 .git 都行 + · CNB 不支持 SSH,只能 HTTPS + 访问令牌(用户名固定填 cnb,密码填令牌) + · 令牌在「个人设置 → 访问令牌」创建,勾选「代码仓库 → 读写」 +USAGE + exit 1 +fi + +case "$CNB_URL" in + https://cnb.cool/*) ;; + *) echo "地址看起来不是 CNB 的:$CNB_URL"; exit 1 ;; +esac + +ROOT="$(git rev-parse --show-toplevel)" +cd "$ROOT" + +STAMP="$(date +%Y%m%d-%H%M%S)" +BACKUP=".workbuddy-backup/git-remotes.$STAMP.txt" +mkdir -p .workbuddy-backup +{ + echo "# 改动前的 git 远端配置($STAMP)" + echo "# 用途:回退参考。本文件含明文凭据,勿提交、勿外传。" + echo + echo "## git remote -v" + git remote -v + echo + echo "## 原始 remote.* 配置" + git config --get-regexp '^remote\.' || true +} > "$BACKUP" +echo "原配置已备份:$BACKUP" + +# 1) origin = CNB(主仓,fetch + push) +git remote remove origin 2>/dev/null || true +git remote add origin "$CNB_URL" + +# 2) gh = GitHub(备份) +git remote remove gh 2>/dev/null || true +git remote add gh "$GH_URL" + +# 3) 自建 Gitea 暂时留着当只读参考。确认新链路稳定后再手工执行: +# git remote remove gitea + +# 4) 一条命令推两段。用「;」而不是「&&」——CNB 失败时照样把 GitHub 推上去 +git config alias.pushall '!git push origin main; git push gh main' + +# 5) 凭据:CNB 只认 HTTPS + 令牌(用户名固定 cnb) +# +# 本机全局 helper 是 GCM(git-credential-manager.exe)。实测它对 cnb.cool 这类 +# 第三方 HTTPS 远端会**每次都弹窗**,而且 GCM_INTERACTIVE=never + +# GIT_TERMINAL_PROMPT=0 都压不住 —— git ls-remote 直接挂死(timeout 25s 未返回)。 +# → 本仓显式改用 wincred:同样写 Windows 凭据管理器(加密),但没有 UI 弹窗。 +# (另:PortableGit 未打包 git-credential-store,exec-path / mingw64/bin 都没有) +if [ -n "${CNB_TOKEN:-}" ]; then + git config --local credential.helper "" + git config --local credential.https://cnb.cool.helper wincred + printf 'protocol=https\nhost=cnb.cool\nusername=cnb\npassword=%s\n\n' "$CNB_TOKEN" \ + | git credential-wincred store + echo "凭据已写入 Windows 凭据管理器(wincred,无弹窗);本仓已屏蔽 GCM" +else + echo "未提供 CNB_TOKEN,已跳过凭据写入。需要时重跑:" + echo " CNB_TOKEN='你的令牌' bash scripts/setup-cnb-remotes.sh $CNB_URL" +fi + +echo +echo "=== 当前远端 ===" +git remote -v | sed -E 's#://[^@/]*@#://***@#g' +echo +echo "=== 令牌权限检查(CNB 常见坑)===" +echo " · 令牌须在「个人设置 → 访问令牌」勾选 repo-code(读写)" +echo " 只勾了其它 scope 会报 403:token does not have the repo-code:r scope" +echo " · 使用范围(Scope of Use)要涵盖本仓库/" +echo " · 用户名固定 cnb,密码填令牌(不支持 SSH)" +echo +echo "=== 下一步 ===" +echo " git push origin main # 推到 CNB(首次会传全量历史,约 588MB)" +echo " git push gh main # 推到 GitHub 备份" +echo " git pushall # 两段一起" diff --git a/static/human-demo.html b/static/human-demo.html new file mode 100644 index 00000000..db1bc2ca --- /dev/null +++ b/static/human-demo.html @@ -0,0 +1,51 @@ + + + + + +人机验证 · 真实评论区预览 + + + +
+ 真实评论区预览 + + + + + 控件出现在评论输入框上方(iframe 里是真的 Artalk 评论区) +
+ + + + + diff --git a/代码源与构建平台选型.md b/代码源与构建平台选型.md new file mode 100644 index 00000000..b509bb67 --- /dev/null +++ b/代码源与构建平台选型.md @@ -0,0 +1,199 @@ +# 代码源与构建平台选型 + +> 回答的问题:**「想简化是不是只能走 EdgeOne?」** +> +> **结论先行**:不是"只能",但在你当前的约束下,**EdgeOne 是最省事的那一个**。 +> 而且真正要换的不是 CDN —— 是**「托管构建」**。EdgeOne 恰好一条顶两条(构建 + 分发), +> 这是 Cloudflare 和阿里云 ESA 都做不到的。 +> +> 另外有一个**会改变结论的数字**:Gitee 免费版单仓库上限 **500MB**,而你的 `.git` 是 **588MB**。 +> 所以国内代码源的第一选择不是 Gitee,而是 **CNB**。 + +--- + +## 一、把问题重新定义一次 + +你说「之前是 GitHub Actions 部署,用得很稳,但账号被标记了」。 + +那么现在这一整套东西,本质是**你自己复刻了一套 GitHub Actions**: + +| GitHub Actions 原来的角色 | 你现在用自建件替代 | +|---|---| +| github.com(代码托管) | 自建 Gitea | +| GitHub 的 runner(构建执行) | act_runner | +| `upload-artifact`(产物传递) | Gitea artifact → COS → 中转机 | +| Pages 直连(产物落地) | sync.sh → 又拍云 → 多吉云 | + +**所以要"简化回去",正确的问题是:找一个托管的构建服务,把这四个自建件一起替掉。** + +CDN 选谁(EdgeOne / CF / ESA / 多吉云)是**另一个问题**,它不解决构建复杂度。 + +--- + +## 二、三条硬约束(都核实过,不是印象) + +| 约束 | 实测/官方值 | 影响 | +|---|---|---| +| 你的仓库体积 | `.git` **588MB** / 955 提交;工作区 803MB | 决定代码源能不能装下 | +| 你的产物体积 | `public` 424MB / **3267 文件**,最大单文件 12.8MB | 决定托管平台的文件数/单文件门槛 | +| Hugo 版本 | 0.128.2 | 托管构建需能指定版本 | +| 备案 | **鄂ICP备2022015735号-1** | 境内加速的前提,你**已具备** | + +--- + +## 三、★ 三个关键发现 + +### 发现 1:EdgeOne Pages **官方支持 Gitee / GitLab 作为 Git 源** + +官方文档原文:*「Pages 目前支持 GitHub, GitLab, Gitee 等 Git 提供商的接入」*。 +第三方 Nitro 部署文档也写:*「EdgeOne supports deployments from GitHub, GitLab, Gitee, and CNB」*。 + +**这条直接决定了「能不能找回 GitHub Actions 那种简单感」—— 能。** +代码推到国内托管平台 → EdgeOne 自动拉取、自动构建、自动部署。**不需要你自己的 runner。** + +### 发现 2:Gitee 免费版装不下你的仓库 + +Gitee 官方配额页(help.gitee.com/account/usage-quota): + +| 项 | 社区版免费额度 | +|---|---| +| 单仓库容量 | **≤ 500 MB** | +| 单文件 | ≤ 50 MB | +| 用户总仓库容量 | 5 GB | + +**你的 `.git` 是 588MB → 超了 17.6%。** 推上去会被锁定推拉服务。 +(Gitee 提供 `git-repo-clean` 瘦身工具可以降到 500MB 以下,但那是额外工作。) + +### 发现 3:CNB 是更合适的国内代码源 —— 而且和 EdgeOne 有官方集成 + +**CNB(Cloud Native Build,cnb.cool)** 是腾讯的 AI Native Git 平台,社区版免费: + +| 项 | 免费额度 | +|---|---| +| 仓库存储 | **100 GiB**(你的 588MB 毫无压力) | +| 对象存储 | 100 GiB | +| 云原生构建 | **160 核时/月** | +| 云原生开发 | 1600 核时/月 | +| 登录方式 | 微信扫码 | + +**160 核时换算**:4 核跑 10 分钟 = 4 × (10/60) ≈ 0.67 核时 → 一个月能跑 **约 240 次构建**。你今天按每月 60–90 次算,**用掉不到 40%**。 + +而且 EdgeOne 官方专门写了 **CNB 插件文档**(`pages.edgeone.ai/document/using-cnb-plugin`): +> 在 CNB 流水线里 `npm run build` → `npx edgeone pages deploy -n <项目名> -t $EDGEONE_API_TOKEN` + +**这条路官方背书,100% 可行。** + +--- + +## 四、四种组合对比 + +| | 代码源 | 构建在哪 | 产物去向 | 自维护机器 | 免费额度 | 卡点 | +|---|---|---|---|---|---|---| +| **① CNB + EdgeOne Pages** ★ | CNB | **CNB 流水线**(托管) | EdgeOne 国际 + 国内 | **0 台** | 100GiB + 160 核时/月 + 500 构建/月 | 需绑微信/腾讯云账号 | +| ② Gitee + EdgeOne Pages | Gitee | EdgeOne Git 集成(4核6G) | EdgeOne 国际 + 国内 | **0 台** | 5GB / 500 构建/月 | **仓库必须先瘦身到 500MB 以下** | +| ③ GitLab.com + CF Pages | GitLab.com | CF 侧 | **仅 CF**(产物拿不出来) | 0 台 | CF 500 次/月 | 境内无解;CF 国内访问慢 3 倍 | +| ④ 保留 Gitea + EdgeOne Pages | 自建 Gitea(mirror→CNB) | CNB | EdgeOne | **1 台**(Gitea) | 同 ① | 没砍干净;但写作后台零改动 | + +**为什么推荐 ①**:唯一一个同时满足「代码源在国内且装得下仓库」+「构建托管」+「境内外都能落」+「免费」的组合。 + +--- + +## 五、推荐形态 + +``` +写作侧 构建(托管) 分发(托管) +┌────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐ +│ write-server │ │ CNB(cnb.cool) │ │ EdgeOne 国际站 Pages │ +│ (本地/服务器) │ push │ 免费 100GiB / 160 核时/月 │ │ → usj.cc 境外解析 │ +│ 直接写 .md + git ├─────────▶│ │ │ │ +│ 只改 remote URL │ │ .cnb.yml: │──▶│ EdgeOne 国内站 Makers │ +└────────────────────┘ │ hugo --minify │ │ → 备案域名 境内解析 │ + │ edgeone pages deploy │ │ │ + 或本地 hugo 后手动 push ─────▶│ (境外 + 境内 两个项目)│ └──────────────────────────┘ + └──────────────────────────┘ +``` + +### `.cnb.yml` 骨架(复刻你现在 deploy.yml 做的事) + +```yaml +main: + push: + stages: + - name: 构建 Hugo 站点 + image: klakegg/hugo:0.128.2-ext + script: | + hugo --minify --gc + + - name: 部署境外 + image: node:20 + script: | + npx edgeone pages deploy ./public \ + -n hugo-blog-overseas -t $EDGEONE_TOKEN_INTL -a overseas + + - name: 部署境内 + image: node:20 + script: | + npx edgeone pages deploy ./public \ + -n hugo-blog-cn -t $EDGEONE_TOKEN_CN -a global +``` + +> ⚠️ `-a global`(含中国大陆)目前只在**国际站**文档里明确,且官方文档自相矛盾。 +> 另一条稳妥路径是**国内站 Makers 单独建一个项目**(账号体系与国际站独立), +> 绑你的备案域名。这个需要实测一次定论。 + +--- + +## 六、砍掉 / 保留 + +### 砍掉(9 项) + +| 砍掉 | 原因 | +|---|---| +| act_runner | CNB 托管构建取代 | +| 广州中转机 | 产物直接落在 EdgeOne,不需要中转 | +| `sync.sh` 轮询 | 同上 | +| 腾讯云 COS | 同上(砍 COS 这件事本身也随之结束) | +| Gitea artifact 链 | 那串 `v3/v4` 兼容坑一起消失 | +| 8 处 `github.server_url` 兼容分支 | 不再需要伪装成 GitHub | +| 又拍云 | EdgeOne 国内站直接 serve 境内 | +| 多吉云 | 同上(境内 CDN 层) | +| 三套通知里的两套 | 保留一套即可 | + +**发布链路:7 环节 → 3 环节**(写作 → 构建 → 上线) +**自维护机器:3 台 → 0 台** +**厂商:Gitea自建 + COS + 又拍云 + 多吉云 + EdgeOne + 失效的 GitHub → CNB + EdgeOne(同为腾讯体系)** + +### 保留 + +- **Hugo + content + themes** —— 完全不动 +- **blog-admin(artalk-cf)** —— 本来就跑在 CF Workers,一行不改 +- **write-server** —— **几乎不用改**,只把 `origin` 的 remote URL 指向 CNB。 + CNB 是标准 git + 提供统一 HTTPS Token,`lib/git.ts` 的 `git pull --rebase` / `git push` 逻辑照常工作 + +--- + +## 七、待确认(3 条,按重要性排序) + +1. **EdgeOne 免费版的流量额度**。官方「限制与配额」页**没有列「流量」这一项**;第三方说法互相矛盾(一处说 50GB/月,一处说不限量)。 + 按你的站点规模估算:单页 1–3MB,50GB/月 ≈ 2–5 万次浏览 —— 个人博客大概率够, + 但**那个 12.8MB 的 mp4 如果被反复播放会吃掉不少**。这一条开工前必须先确认。 +2. **CNB 是否可直接作为 EdgeOne Pages 的 Git 源导入**。第三方文档说支持,官方只在插件路径里明确。 + 不影响可行性(走插件路径必然可行),只影响配置方式。 +3. **境内用国际站 `-a global` 还是国内站 Makers 独立项目**。一次实验可定论。 + +--- + +## 八、最小验证(成本极低,建议先做) + +不用改任何现有东西,**一次性验证整条链路是否成立**: + +1. 建一个 CNB 仓库(微信扫码,2 分钟) +2. 往里放一个最小 Hugo 站 + 上面的 `.cnb.yml`(或先放个 hello world) +3. 拿一个 EdgeOne API Token(你已经有),跑一次 push +4. 看三件事:**CNB 能不能构建** → **能不能部署到 EdgeOne** → **国内机实测访问延迟** + +这一步跑通,等于确认了整条路;跑不通,损失是 20 分钟,不是 30 天。 + +--- + +*本文档基于 2026-10-04 核实的官方文档与实测数据。所有配额均引自官方页面,第三方来源已标注。* diff --git a/架构总览.md b/架构总览.md new file mode 100644 index 00000000..cd2425d9 --- /dev/null +++ b/架构总览.md @@ -0,0 +1,173 @@ +# 优世界博客 · 架构总览与复杂度审计 + +> 梳理时间:2026-10-04 +> 目的:把「复杂在哪、为什么复杂、哪些是自己加的、哪些能砍」讲清楚,供架构决策。 +> 方法:以代码/配置为证据,不用推测。凡属推测的地方都标了「待确认」。 + +--- + +## 0. 一句话结论 + +**这不是「一个博客」,而是四个独立系统,绑在一条 7 环节、跨境的、需要自己运维的发布链路上。** + +前三个系统都很轻、也很健康;**复杂度几乎全部来自第四个 —— 为了支撑前三个而自建的基础设施**(Gitea + act_runner + 中转机),以及为了绕过跨境链路而打的一层层补丁。 + +--- + +## 1. 全景:哪里有什么 + +### 1.1 四个子系统 + +| # | 子系统 | 技术栈 | 跑在哪 | 健康度 | +|---|---|---|---|---| +| **1** | **内容系统** | Hugo 0.128.2 + Ying 主题,138 篇 md,产物 424MB / 3267 文件 | 产物分发到 3 个 CDN | 主体,正常 | +| **2** | **评论系统** | `blog-admin/` = artalk-cf(Artalk v2 兼容服务端)+ RSS 机器人 | Cloudflare Workers + D1 + KV → `api.200181.xyz` | ✅ 已上线,**零服务器零运维**,架构最干净的一块 | +| **3** | **写作系统** | `write-server/`(Next.js 16 + Docker,`post.usj.cc`)
`write/`(本地 Windows 前端,带 vbs/bat 自启动) | 独立 Docker 主机(**待确认**)/本机 | ⚠️ **两套前端功能重叠** | +| **4** | **基础设施** | Gitea 28.0.0 + act_runner + 1Panel 中转机 + 各类备份脚本 | 境外 VPS + 腾讯云广州 | ⚠️ 正在迁移,**复杂度的主要来源** | + +### 1.2 机器与服务地图 + +| 标识 | 地址 / 位置 | 承担什么 | +|---|---|---| +| **Gitea 主机** | `23.254.236.47:3001`(境外) | Git 仓库 + **Gitea Actions / act_runner** + artifact 存储 | +| **国内中转机** | `119.29.215.187`(腾讯云广州) | 1Panel + OpenResty + mysql + alist + vaultwarden(7 个容器)+ `sync.sh` 每分钟轮询同步 | +| **EdgeOne Pages** | 项目 `hugo-blog`,`--area overseas` | **境外线路**的托管与 CDN(腾讯) | +| **又拍云** | 对象存储 | **国内源站** | +| **多吉云** | CDN,回源又拍云 | **国内加速** | +| 腾讯云 COS | `cos.ap-*.myqcloud.com` | 曾是「境外构建产物 → 国内中转机」的中转层,**正在砍** | +| Cloudflare | `api.200181.xyz` | 评论后端 + RSS 机器人 | +| 通知渠道 | Telegram / 飞书 / QQ 邮件 | 三套并行,内容 90% 重复 | + +> 关键点:**境外线路和国内线路是完全独立的两套。** 境外走 EdgeOne,国内走 多吉云 → 又拍云。任一篇文章上线,两条链路上的东西都得各自到位。 + +--- + +## 2. 一次发文的完整旅程 + +``` +① 写作 write-server (web) 或 write/ (本地 Windows) + │ +② 提交 git push ──► Gitea(自建,跨境) ← 你自己维护的机器 1 + │ +③ 构建 Gitea Actions / act_runner ← 你自己维护的 runner 2 + │ ├─ hugo --minify --gc (要的) + │ ├─ 图片优化 (sharp, 482 张) (可砍,见 §3-C) + │ └─ 草稿隐藏处理 + │ +④ 传递 424MB 产物 → 打 tar.zst → Gitea artifact ← 踩过 3 个坑才通 + │ + ┌────┴─────────────────────────────┐ + ▼ ▼ +⑤ 境外线路 ⑥ 国内线路 + EdgeOne Pages 广州中转机 sync.sh(每分钟轮询) ← 你自己维护的机器 3 + (runner 直接 deploy) └─ 跨境下载 376MB + └─ upx sync → 又拍云 + └─ upx purge + │ + ⑦ 多吉云 CDN 刷新 + TG / 飞书 / 邮件 三套通知 +``` + +**对照**:一个普通 Hugo 博客 + 托管 CI 的发布链路是 **3 步**(push → 构建 → 上线)。 +你这里是 **7 步,中间有 3 个环节是你自己在运维的服务器/服务**。 + +这就是「复杂」最直观的度量。功能一点都不多,**多的是中继环节**。 + +--- + +## 3. 复杂度归因:三层,性质完全不同 + +### A 层 · 外部约束 —— 不是你的选择,但躲不掉 + +| 约束 | 连带出来的复杂度 | +|---|---| +| **GitHub 账号被标记** | 自建 Gitea → 连带 act_runner 自运维、artifact API 不合规、缓存服务跨网段不可达 | +| **跨境链路不稳** | 境外 runner 直传又拍云 v0 API 稳定 EOF → 必须有境内兜底 | +| **国内 / 境外访问质量不同** | 被迫维护两条独立线路(EdgeOne + 多吉云/又拍云) | +| **EdgeOne Pages 国际站必须 `--area overseas`** | 部署动作只能从境外发起 | + +> A 层是**根因**。B、C 两层的大部分,都是 A 层长出来的。 + +### B 层 · 为绕开 A 打的补丁 —— 半被迫,可以简化但不能直接删 + +| 补丁 | 为什么存在 | 代价 | +|---|---|---| +| 广州中转机 `sync.sh` 每分钟轮询 | 境外直传又拍云不通 | 多一台机器要维护;每分钟一次轮询;产物要跨境搬一次 | +| COS 中转层(正在砍) | 给「境外产物 → 国内源站」当缓冲区 | 存储 + 流量账单 | +| 一套 workflow 硬塞进 Gitea | 原本为 GitHub 写 | **8 处** `github.server_url == 'https://github.com'` 分支判断 | +| artifact 单文件 tar 化 | v4 不支持 / v3 传目录 finalize 500 | 额外的打包/解包步骤 | +| 备份脚本三套(aliyun / gitea / cleanup) | 自建基础设施的必然成本 | — | + +> ⚠️ **一个需要你注意的点**(推测,建议实测):砍掉 COS 之后,中转机变成**直接跨境从境外 Gitea 拉 376MB**。跨境传输这一步并没有消失,只是从「境外 → 境内 COS(一次跨境上传)」变成「境内 ← 境外 Gitea(一次跨境下载)」。**除非实测新链路更快更稳,否则「砍 COS」省的是账单,不是复杂度。** 这条直接关系到你在做的改造是不是真的划算。 + +### C 层 · 自己加的 —— 可以直接砍,收益最大 + +| # | 冗余 | 证据 | 能砍吗 | +|---|---|---|---| +| 1 | **图片优化塞进 CI** | `Optimize Images` + `Commit Optimized Images` + `Push Image Optimizations` 三步 + `npm ci` 装 sharp(~50MB)。扫 482 张图,其中 **15 张 2021–2023 的老图永远处理不掉**,每轮重扫重试 | ✅ **最该砍**。图片在写作时本地处理,CI 就永远不需要 sharp | +| 2 | **CI 反向 commit 回仓库** | `Commit Optimized Images` 把结果 commit,`Push Image Optimizations` 再 rebase push | ✅ 砍掉图片优化后,这两步一起消失 | +| 3 | **三套通知** | `Send Telegram` ×2 + `Send Feishu` ×2 + `Send email` ×1 = **5 个 step,约占 deploy.yml 的 1/3(~400 行)**,同一份内容写 3 遍 | ✅ 留一套(TG 或飞书),其余删 | +| 4 | **永久跳过的死代码** | `Upload to UpYun` 标了 `if: false`,但 **~100 行代码仍在**(upx 下载、登录重试、sync 重试) | ✅ 要么删,要么挪进中转机 | +| 5 | **脚本重复** | `add_draft_to_hidden` 有 `.ps1`(96行) 和 `.py`(110行) 两份,README 写的是 ps1、CI 用的是 py;`deploy_*.sh` 有 4 个共 225 行 | ✅ 各留一份 | +| 6 | **两套写作前端** | `write/`(本地 Windows)与 `write-server/`(网页)功能重叠 | ⚠️ 看你的使用习惯,可合并 | + +--- + +## 4. 量化:复杂度有多少 + +| 指标 | 值 | +|---|---| +| `deploy.yml` 总行数 | **1169 行** | +| job 数 | 7 个(含 1 个手动触发的网络探测 job) | +| 单个 job 的 step 数(finalize) | 12 个 | +| 通知类 step | 5 个(TG×2 / 飞书×2 / 邮件×1),约 400 行 | +| `github.server_url` 兼容分支 | 8 处 | +| `if: false` 死代码 | 约 100 行 | +| 需要自己运维的机器 / 服务 | **3 个**(Gitea 主机、act_runner、广州中转机) | +| 发布链路环节数 | **7 步**(理想 3 步) | +| 涉及的 CDN / 存储 | 4 个(EdgeOne / 又拍云 / 多吉云 / COS) | +| 构建产物体积 | 424MB / 3267 文件 | +| 真正发布所需的机器数(理想) | **1 台**(构建 + 推送)或 0 台(托管 CI) | + +**一句话**:功能是一个静态博客 + 评论 + 写作后台,但支撑它的是 **7 个 job、4 个 CDN、3 台自维护机器、1169 行流水线**。 + +--- + +## 5. 四条可选的收敛方向 + +| | 方向 A · 就地瘦身 | 方向 B · 构建源外包 | 方向 C · 构建下移境内 | 方向 D · 取消 CI | +|---|---|---|---|---| +| **做什么** | 砍 C 层:图片优化移出 CI、合并通知、删死代码、合并脚本 | 引入 gitlab.com(GitHub 已被封)或用 CF Pages 构建 | Gitea + runner 迁到广州机,构建发生在境内 | 写作后台直接构建 + 部署,Gitea 退化为纯代码托管 | +| **产物落地** | 不变 | gitlab.com 共享 runner | 境内直接推又拍云 ✓ | 写作后台直接推又拍云 + EdgeOne | +| **跨境传输** | 不变 | 视方案 | **只剩 EdgeOne 一步** | 同上 | +| **新增依赖** | 无 | gitlab.com 账号 + 跨境 push | 无 | 无 | +| **主要风险** | 只是变短,环节数不变 | **GitLab 免费版仅 400 分钟/月**(你月需约 400–900 分钟,踩线) | 广州机**内存 1.9G / 已跑 7 个容器**,hugo 构建未必跑得动 | 写作后台所在机器算力是否够;构建与发布耦合 | +| **代价** | 最低,纯收益 | 月额度可能超 | 需要瘦身后实测内存 | 需要改造写作后台 | +| **建议** | ✅ **先做,是所有方案的前置** | 备选 | 与 D 二选一,实测决定 | 与你「一键直达」的偏好最契合 | + +### 推荐路径 + +1. **先做方向 A**(无风险、纯收益、且是 B/C/D 的前置)。做完后 `deploy.yml` 预计能从 1169 行降到 600 行以内,构建里只剩 `hugo build`。 +2. **再实测两件事**:① 广州机跑一次 `hugo` 的峰值内存与耗时;② 国内机跑一次 `hugo` 的耗时(对比 CI)。 +3. **依据实测二选一**: + - 广州机跑得动 → **方向 C**(Gitea 迁境内,跨境只剩 EdgeOne) + - 写作后台机器跑得动 → **方向 D**(连 CI 都取消,最符合「一键直达」) + - 都跑不动 → **方向 B**(但要接受 GitLab 400 分钟/月的紧箍咒) + +--- + +## 6. 需要你决定的三件事 + +1. **那 15 张 2021–2023 的老图能清吗?** 能清的话,我本地转 WebP + 修引用,然后 `Optimize Images` 这一步整个从 CI 删掉 —— 这是砍掉 C 层最大块的钥匙。 +2. **构建最终放在哪一侧?** 境外(现状)/境内(方向 C/D)/外包(方向 B)。这决定后面所有改造的走向。 +3. **通知留哪一套?** TG / 飞书 / 邮件 —— 留一个,其余删。另外邮件通知那份的长文本(约 50 行模板)可以整个去掉。 + +--- + +## 附:本次梳理的证据出处 + +- `.github/workflows/deploy.yml`(1169 行,全文通读) +- `blog-admin/README.md`、`write-server/README.md`、`write/`(模块定位) +- `hugo.toml`(`baseURL = https://usj.cc`) +- `gitea-backup/README.md`、`docker-compose.yml`(迁移目标与坑) +- `scripts/` 目录清单与行数统计 +- 线上实测:Gitea `/api/v1/version` = `28.0.0`;广州机 `docker pull gitea/gitea:28.0.0-rootless` 可拉、`1.28.0-rootless` 不存在 diff --git a/砍COS改造步骤.md b/砍COS改造步骤.md index 7a795a02..4732ba8a 100644 --- a/砍COS改造步骤.md +++ b/砍COS改造步骤.md @@ -4,10 +4,40 @@ > 改成「build → Gitea artifact →(中转机拉取)→ 又拍云 → 多吉云」, > 砍掉腾讯云 COS 这一层,省下 COS 存储/流量账单。 -> 状态(2026-10-04): -> - ✅ `deploy.yml` 已改完(本地,未推送) -> - ⏸️ `sync.sh`(国内中转机)待改,**卡在需要一个 Gitea 只读 token** -> - ⚠️ 两者必须一起改一起推,否则国内博客会停更(见下方「为什么不能只推一半」) +> 状态(2026-10-04 更新): +> - ✅ `deploy.yml` 已改完并**已推送** +> - ✅ 中转机 `sync.sh.new`(artifact 版)已上传到 `/opt/upyun-sync/sync.sh.new`,**尚未生效** +> - ✅ Gitea 只读 token 已写入中转机 `/opt/upyun-sync/.gitea_token`(600 root:root) +> - ❌ 链路**尚未跑通**:run #55 倒在 v4 不支持,run #56 倒在 v3 的 finalize 500 +> - ⏸️ 最新修复(单文件 tar,commit `1f568bbf`)**本地已提交、待推送** +> - ⚠️ `deploy.yml` 与 `sync.sh` 必须一起生效,否则国内线路停更(见下) + +--- + +## 零、实测踩到的两个坑(都已修,记录备用) + +### 坑 1:`upload-artifact@v4` 在 Gitea 上直接失败(run #55) + +``` +::error::@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ +are not currently supported on GHES. +``` + +Gitea 被 v4 识别为 GHES,但它没实现新版 artifact API → **降级 v3**(commit `ed38f938`)。 + +### 坑 2:v3 传「目录」时 Gitea finalize 返回 500(run #56,耗时 30 分钟) + +v3 收目录是**逐文件上传**:3216 个 blob / 376MB 全部传完(日志可见 `Processed file #3216`), +但最后的 finalize 调用被 Gitea 拒绝: + +``` +Finalize artifact upload - Attempt 1..5 of 5 failed with error: Request timeout +::error::Finalize artifact upload failed: Artifact service responded with 500 +``` + +**结论:Gitea 扛不住「文件数多」的 artifact。** 改法(commit `1f568bbf`): +build 侧先把 `public/` 打成**单个** `public.tar.zst`,只上传这 1 个文件。 +副作用是好的——产物结构从此完全确定,不再有「解出来会不会多一层 `public/`」的歧义。 --- diff --git a/精简方案-只留CF和Hugo.md b/精简方案-只留CF和Hugo.md new file mode 100644 index 00000000..919537d2 --- /dev/null +++ b/精简方案-只留CF和Hugo.md @@ -0,0 +1,228 @@ +# 精简方案:只留 Cloudflare + Hugo + +> 2026-10-04 +> 目标:砍掉全部自建基础设施,只保留 **Hugo**(构建)和 **Cloudflare**(一切服务端)。 +> 文中所有平台限制均来自官方文档核实,非推测。 + +--- + +## 一、决定一切的硬约束 + +**Cloudflare 唯一不能替你做的是「代码托管」。** 它的两条部署路线直接决定架构形态: + +| 模式 | 代码源 | 构建在哪 | 适用场景 | +|---|---|---|---| +| **Git 集成** | **只认 github.com / gitlab.com**
(官方原文:*does not currently support connecting self-hosted instances*) | Cloudflare 侧自动构建 | 要「push 即上线」 | +| **Direct Upload** | 不需要 git | 不构建,你本地构建好推产物 | 要「零第三方依赖」 | + +> ⚠️ **两种模式在项目创建时定死,事后不能互转**(官方明说)。选错只能重建项目 —— 这是最容易踩的坑。 +> 变通办法:建 **Git 集成**项目,之后在 `Settings → Builds` 关掉「自动部署」,再用 `wrangler` 直传 —— 这样两种方式都能用(**反过来不行**)。 + +### 关键结论 + +**GitHub 被封 ≠ 必须自建 Gitea。** 你不需要「自建 git 托管」这种复杂度,只需要回答一个问题: + +> **你要不要「不开电脑也能发文」?** + +--- + +## 二、两条路线 + +### 形态 A · 本地构建直传(最简,零第三方) + +``` +你的电脑 Cloudflare +┌─────────────────────┐ ┌──────────────┐ +│ 写 md → hugo build │──wrangler─▶│ Pages (静态站) │ +└─────────────────────┘ pages ├──────────────┤ + deploy │ Workers (评论/RSS) │ + └──────────────┘ +``` + +- **组件数**:1 台自己的电脑 + Cloudflare +- **第三方依赖**:0(连 git 托管都不要) +- **发布链路**:3 步,全在本机 +- 代码备份:本地仓库即可,要冗余可推任意 git,或存 CF R2 +- **代价**:发文必须开电脑 + +### 形态 B · GitLab.com 当代码源 + CF 自动构建 + +``` +写 md ──push──▶ gitlab.com ──自动构建──▶ Cloudflare Pages ──▶ 上线 + Cloudflare Workers (评论/RSS) +``` + +- **组件数**:gitlab.com + Cloudflare +- **★ 关键**:构建跑在 **Cloudflare 侧**(免费 500 次/月、单次 20 分钟超时),**不消耗 GitLab 的 400 分钟** + → 所以之前担心的「GitLab 免费版 400 分钟/月不够用」在这套组合下**根本不成立** +- 额度核对:本站 3267 文件 / 最大单文件 12.8MB → CF 上限 20,000 文件 / 25MiB ✅ 完全够 +- **代价**:多一个外部账号 + +--- + +## 三、可以整个删掉的(9 项) + +| # | 删掉的 | 原本的作用 | +|---|---|---| +| 1 | **Gitea** | 自建 git 托管(GitHub 被封后的替代品) | +| 2 | **act_runner** | 自建 CI runner | +| 3 | **广州中转机 + `sync.sh`** | 每分钟轮询、把产物搬去又拍云 | +| 4 | **又拍云** | 国内源站 | +| 5 | **多吉云** | 国内 CDN,回源又拍云 | +| 6 | **EdgeOne Pages** | 境外线路托管 | +| 7 | **腾讯云 COS** | 境外产物 → 国内的中转层 | +| 8 | **artifact 传递链** | 跨 job 传 424MB(含 v3/v4 那串坑) | +| 9 | **兼容分支与冗余通知** | `github.server_url` 判断 ×8、TG/飞书/邮件三套、`if:false` 死代码约 100 行 | + +**发布链路从 7 环节降到 3 环节;需要自己运维的机器从 3 台降到 0 台。** + +--- + +## 四、Cloudflare 侧能白捡留下的 + +**`blog-admin/`(artalk-cf 评论后端 + RSS 机器人)本来就是 CF Workers + D1 + KV —— 一行都不用改。** + +这是你现有架构里唯一「已经完全符合目标形态」的部分: +- 评论系统 → `api.200181.xyz`,照常 +- RSS 订阅抓取、友链朋友圈数据 → 照常 +- 后台管理(`/admin/` 与 `/sidebar/`)→ 照常 + +**换句话说:你的「后端」其实早就已经是 Cloudflare 了,复杂的只是「发布管线」。** + +--- + +## 五、必须接受的代价(诚实说) + +### 1. 国内访问会变慢 ← 最大的一笔 + +Cloudflare 免费版在中国大陆走**境外节点**(没有 ICP 备案就用不了大陆节点,China Network 要企业版)。 +现在这套「又拍云 + 多吉云」的存在意义**就是为了这个**。 + +**2026-10-04 国内机实测**(腾讯云广州,见「五·补」章):CF 首字节 **0.61–0.89s**, +而现在的国内 CDN 是 **0.18–0.32s** —— 即 **CF 比国内 CDN 慢约 2–4 倍**。 +能开、不算快,对个人博客通常可接受,但你要心里有数。 + +### 2. 写作后台(`post.usj.cc`)会没有 + +那套 Next.js 直接读写本地 `.md` 文件、依赖文件系统,**CF 上跑不了**。要么: +- 重写成 CF Worker(内容改存 R2/D1,图片存 R2)—— 你会写 Worker,技术可行,但是工作量 +- 退回本地写作(配合形态 A 正好) + +### 3. Telegram bot 发文、公众号发布会断 + +同理,需要重写成 CF Worker 才能保留。 + +### 4. 构建前的脚本要处理 + +`check_links.js`(友链检查)、`generate_circle_data.js`(朋友圈数据)、`add_draft_to_hidden.py` 这些: +- 要么塞进 CF Pages 的构建命令里(需要 `npm ci`,会拖慢构建、且 CF 构建环境要能装依赖) +- 要么去掉(友情链接检查其实没必要每次构建都做) + +--- + +## 五·补、国内访问能不能救?(2026-10-04 国内机实测) + +用国内那台机器(腾讯云广州,`119.29.215.187`)实测,每项 3 次取代表值: + +| 目标 | 首字节 TTFB | 连接建立 | 走哪个节点 | +|---|---|---|---| +| `usj.cc`(现状:EdgeOne **国内**节点) | **0.18–0.32s** | 0.01–0.04s | 成都 / 浙江 | +| `api.200181.xyz`(你自己的 CF Workers) | **0.61–0.89s** | 0.20–0.45s | CF Anycast(IPv6) | +| `cravatar.cn`(头像是 CF 的) | 0.65–0.71s | 0.23s | CF Anycast | +| `spst2.com`(页首统计脚本) | 0.087s | 0.011s | 有国内节点,**不是问题** | + +**两条硬结论:** + +1. **CF 免费版在国内比国内 CDN 慢约 2–4 倍**(TTFB 0.6–0.9s vs 0.2–0.3s)。 +2. **实测发现 CF 的连接走了 IPv6**(`2606:4700::…`),握手要 0.20–0.45s; + 同一时刻 IPv4 握手只要 **0.058s**。→ **IPv6 到 CF 的路由在国内明显更差**, + 这是一个可以单独利用的抓手。 + +### 能加速的三条路 + +**路 A · 官方(走不通)** +Cloudflare China Network 需要:Enterprise 企业版 + 单独订阅 + **每个主域名持有效 ICP 备案** + +京东云内容审核。个人博客没戏。 + +**路 B · 社区「优选 IP」(有效,但违反 ToS)** +原理:CF 用 Anycast 广播,大陆解析到的 IP 段路由差。做法是**让大陆用户的 DNS 解析结果 +指向另一段「对大陆友好」的 CF IP**。 +- 预期效果:按本机实测基线,合理预期是 **0.6s → 0.2–0.3s**(社区常说的「5s→2s」是针对更差的默认线) +- ⚠️ **Pages 不能直接改 CNAME**(会返回 `1001`)→ 必须「转成 Worker」或「交给第三方 DNS 分线路解析」 +- ⚠️ **明文违反 CF 服务条款 2.2.1(b)**:*causing traffic for your Cloudflare-proxied domain + to be sent to an IP address that was not assigned by Cloudflare for the domain*。 + 官方保留封号权;社区共识是「用别怕,怕别用」,建议**账号隔离**(主站一个号,优选另开小号) +- ⚠️ **要维护**:优选 IP/域名会失效,得定期更换;个别优选域名还会被地方屏蔽 + +**路 C · 不违规的优化(零风险,先做)** +- ✅ **已做**:字体自托管(`/font/zql-v2.woff2`,无 Google Fonts)、CSS 合并成单文件、图片 WebP +- 头像源 `cravatar.cn` 走 CF(0.65s)→ 可换国内源(站内已有 `q1.qlogo.cn` 这类) +- 加 **Cache Rules** 长缓存 + **Early Hints** + **Tiered Cache**(免费可用)→ 改善重复访问 +- 若走第三方 DNS(路 B 顺带),**只下发 A 记录、不下发 AAAA** → 直接规避上面那条差的 IPv6 路由 + +### 一句话结论 + +**你自己的实测就是答案:国内 CDN 快 2–4 倍,而且完全合规。** +CF 免费版在国内的「加速」本质是**可用的灰色技巧 + 持续维护成本**。 + +- 如果「只留 CF」是硬目标 → 走 **Worker + 第三方 DNS 只给 IPv4**(比优选 IP 干净,且顺带修 IPv6 问题) +- 如果真正的目标是「砍复杂度」→ 记住:**复杂度的大头在「构建与搬运」**(Gitea / act_runner / 中转机 / COS / artifact), + **不在 CDN 的数量**。多留一个国内 CDN 不会让架构变复杂;砍掉那串搬运链才会。 + +--- + +## 六、我的建议 + +**先回答那个问题:需不需要「不开电脑也能发文」?** + +| 你的答案 | 选 | 理由 | +|---|---|---| +| **不需要**(本来就在电脑上写) | **形态 A** | 真正的「只要 CF + Hugo」:0 台服务器、0 个第三方托管 | +| **需要**(手机/TG 发文) | **形态 B** | GitLab 只当「代码网盘 + 触发器」,构建和托管全在 CF | + +**两条都不要再去自建 Gitea** —— 那正是你想摆脱的复杂度。 + +**一个必须提前想清楚的点**:CF Pages 的 Git 集成与 Direct Upload **不能互转**,项目创建时就要定。 +如果不确定,**先建 Git 集成项目、之后关掉自动部署用 wrangler 直传**,把两条路都留着。 + +--- + +## 七、如果还想更彻底(建议不要) + +把内容也搬进 CF:文章存 R2/D1,写作后台写成 CF Worker。但 —— + +**Hugo 构建需要文件系统 + 执行二进制,跑不进 Worker。** 所以要么放弃 Hugo(改成运行时渲染),要么保留一个构建执行者(那就回到形态 B 了)。**复杂度会绕回来,不建议。** + +--- + +## 附 A:落地顺序(以形态 B 为例) + +1. GitLab.com 建**私有**项目 → 现有仓库推上去(`.git` 588MB,首次跨境推送需要时间) +2. CF Pages 新建项目 → **Connect to Git → 选 GitLab** + - 构建命令:`hugo --minify` + - 输出目录:`public` + - 环境变量:`HUGO_VERSION=0.128.2`(CF 构建镜像默认是 0.147.7,要锁到你的版本) +3. 绑定自定义域名 `usj.cc` +4. `blog-admin` 保持不动(本来就在 CF) +5. 停掉 Gitea / act_runner / 中转机的同步计划任务;DNS 切到 Cloudflare +6. **观察国内访问速度** —— 这是唯一的实质性风险点 + +## 附 B:形态 A 的落地顺序 + +1. 本机装 `hugo` 0.128.2 + `wrangler`(Node 环境已有) +2. CF Pages 新建 **Direct Upload** 项目(**注意:一旦选它,以后不能改成 Git 集成**) +3. 本机 `hugo --minify && npx wrangler pages deploy public --project-name=<项目名>` +4. 绑定域名,完成 +5. 其余全部关停 + +--- + +## 附 C:本方案的证据出处 + +- Cloudflare 官方文档 *Git integration*:「Pages offers support for GitHub and GitLab」「does not currently support connecting self-hosted instances of GitHub or GitLab」 +- 同上:「You cannot switch to Direct Upload later」 +- Cloudflare 官方文档 *Direct Upload*:Wrangler 上传上限 **20,000 文件 / 25 MiB**;拖拽上限 1,000 文件 +- Cloudflare 官方文档 *Workers Billing and Limitations*:静态资源请求**免费且不限量**,20,000 文件 / 25 MiB +- Cloudflare Blog:*Cloudflare Pages 现已提供对 GitLab 的支持* +- CF Pages 免费额度:500 次构建/月、1 并发、单次 20 分钟超时 +- 本站实测:`public/` = 424MB / 3267 文件,最大单文件 12.8MB diff --git a/阿里云ESA评估.md b/阿里云ESA评估.md new file mode 100644 index 00000000..6120692f --- /dev/null +++ b/阿里云ESA评估.md @@ -0,0 +1,169 @@ +# 阿里云 ESA 评估(对比 EdgeOne) + +> 2026-10-04 · 问题:「阿里云也有个跟 EdgeOne 很像的服务,能不能考虑一下?」 +> 现状:`usj.cc` 域名 **境外解析在 EdgeOne、境内解析在多吉云**(DNS 分线路) + +## 一、结论先说 + +**能考虑,但对你这个站有一条硬伤,直接卡住:ESA Pages 每个项目最多 2000 个文件,你的产物是 3267 个。** + +而 EdgeOne Pages 是 **20000 个**——你只用了 16%。 + +所以结论是:**换阿里云 ESA 不是"简化",是"降级到刚好装不下"。** + +--- + +## 二、阿里云对标的到底是什么 + +| 腾讯云 | 阿里云 | +|---|---| +| EdgeOne(边缘安全加速平台) | **ESA(Edge Security Acceleration)** —— 就是原来的 **DCDN 改名** | +| EdgeOne Pages(Git 导入 + 自动构建 + 静态托管) | **ESA Pages**(也叫「函数和 Pages」) | +| Edge Functions / Cloud Functions | **ESA 边缘函数**(V8 Isolate,只支持 JS) | +| 节点 | 阿里云 3200+ 边缘节点 | + +**能力确实是同级的**——都免费、都支持 ICP 备案、都能绑备案域名走大陆节点、都是「Git 导入 + 自动构建 + 全球分发」一套。你问得没错,这是对标产品。 + +**但两者的尺寸门槛差了 10 倍,这是分水岭。** + +--- + +## 三、★ 决定性的一张表:文件数门槛 + +我逐个查了官方配额页(不是二手说法): + +| 项目 | **ESA Pages** | **EdgeOne Pages** | Cloudflare Pages | +|---|---|---|---| +| **单项目文件数** | **2000** ❌ | **20000** ✅ | 20000 ✅ | +| 单文件大小 | 25 MB | 25 MB | 25 MiB | +| 源码包大小 | 1024 MB | — | — | +| 总存储容量 | — | 5 GB | — | +| 构建次数 | — | 500 / 月 | 500 / 月 | +| 构建超时 | — | 20 分钟 | 20 分钟 | +| 构建算力 | — | **4 核 6 GB** | — | +| 自定义域名 | — | 200 个 | — | +| **你的站** | **3267 → 超限 63%** | 3267 → 占 16% | 占 16% | + +来源:阿里云官方文档《函数和 Pages 使用限制》——*"Pages 文件数 2000 个:每个 Pages 项目最多可上传 2000 个静态文件"*;EdgeOne 官方《限制与配额》——*"单项目文件数 20000"*。 + +**这一条把 ESA 从候选名单里筛掉了,其他指标都不用比了。** + +> 附带发现:**EdgeOne Pages 的构建环境是 4 核 6 GB** —— 比你那台广州中转机(2 核 1.9 G)强得多。如果走它的 Git 集成自动构建,hugo 构建完全不需要你自己的机器。 + +--- + +## 四、你的 3267 个文件是怎么构成的 + +``` +1337 html ← 文章页 + 分类/标签/归档页 +1211 png ┐ + 295 webp │ + 254 jpg ├─ 图片/视频类合计 1778 个(占 54%) + 7 mp4 │ + 6 jpeg │ + 3 svg ┘ + 66 xml ← sitemap / rss + 59 js + 3 woff2 / 3 woff / 3 mp3 / 3 css / 6 json / 2 txt +──────────────── +3267 总计 +``` + +**纯文本类(html/xml/css/js/json)只有 1473 个** —— 这个数字很关键,它意味着「如果非要用 ESA」还有一条窄路(见第六章)。 + +--- + +## 五、ESA vs EdgeOne:真实差异(去掉重复项之后) + +免费套餐**大部分能力是重复的**,真正有差别的是这几条: + +| 维度 | ESA | EdgeOne | 谁赢 | +|---|---|---|---| +| **文件数** | 2000 | **20000** | **EdgeOne(决定性)** | +| 大陆节点 | 有(需备案) | 有(需备案) | 平 | +| 流量 / 请求 | 不限 | 不限 | 平 | +| **开通大陆加速的门槛** | ⚠️ **默认不含大陆,要发帖解锁** | 直接可用 | **EdgeOne** | +| 免费套餐数量 | 每账号限 **1 个** | 站点 1 主域 + 200 子域 | EdgeOne | +| 构建算力 | 未披露 | 4 核 6 GB | EdgeOne(已知值) | +| 单文件限速 | 官方未披露;社区称"不限速",另有称峰值 5 Mbps —— **存疑** | 单文件单线程 4 Mbps(≈500 KB/s) | **ESA 可能赢** | +| WebSocket | 免费版**不支持**(第三方实测) | — | EdgeOne | +| 速度(第三方实测) | 大陆节点覆盖**更多**、网络速度**更高** | 加速效果更佳、全球覆盖更广(Anycast) | 各有说法 | +| 迁移成本 | 要重接一次 | **你已经在用** | **EdgeOne** | + +### 值得注意的两点 + +1. **ESA 免费版默认不含中国大陆加速** —— 官方原文:*"By default, this plan does not include Chinese mainland acceleration."* 要解锁得**在任意社交/博客平台发一篇 30 字以上的推荐帖**(带 `#AlibabaCloudESA #ESAPages` 标签 + 官方图),再进群提交链接。你写博客顺手能完成,但这是个**额外操作**,跟"简化"反向。 +2. **ESA 免费版的「不限速」有矛盾说法** —— 一方说无限速适合大流量,一方说峰值带宽约 5 Mbps。如果你的痛点是那个 12.8 MB 的 mp4(EdgeOne 下首载约 25 秒),这一条**值得实测**,但它是 ESA 唯一可能赢的地方。 + +--- + +## 六、如果非要用 ESA:唯一的一条路 + +把 **1778 个图片/视频剥离到对象存储**(OSS / 又拍云 / 多吉云存储),Pages 只托管 1473 个文本文件 → 落回 2000 以内。 + +**但代价是**:多一个存储服务、多一套域名与缓存配置、构建产物要改写图片链接。**这是"增加复杂度"换"换一家厂商",跟你的目标正好相反。** + +不建议。 + +--- + +## 七、ESA 唯一值得拿的东西(互补用法) + +**如果只是想用阿里云,不要用 ESA Pages,用 ESA 的 CDN 加速能力。** + +ESA 免费版(Entrance plan)本质是**一个不限流量的 CDN**,可以回源到任意源站。它能做的是: +- 回源到你的又拍云 / OSS 存储 → 只加速,不托管,**没有 2000 文件限制** +- 用 ESA 的**边缘函数**收编现在的 `blog-admin`(CF Workers) + +这确实有个价值:**阿里云一家能同时给你 CDN + Pages + 边缘函数 + 对象存储**,比「EdgeOne + CF Workers + 又拍云 + 多吉云」更统一。 + +但这又引入了**第四家厂商**,而你现在的问题恰恰是厂商太多。 + +--- + +## 八、而且你现在的双线路,其实已经是"简化过"的形态 + +你说的「境外 EdgeOne、境内多吉云」,本身就是**分线路 DNS 调度**的结果: + +``` +usj.cc + ├─ 境外线路 → EdgeOne(Pages 项目,--area overseas) + └─ 境内线路 → 多吉云 CDN(融合 CDN,70+ 节点,20 GB/月免费) +``` + +**这已经做到了"境内外分流"** —— 跟 EdgeOne 双区域方案想达成的效果是一样的。所以真正的问题不是「CDN 选阿里云还是腾讯云」,而是: + +> **要不要把这两条线合并成一家?** + +- **合并到 EdgeOne**:改一个参数(`-a global`)或建国内站项目 → 砍掉多吉云 ✅ +- **合并到 ESA**:从零接一次,还撞上 2000 文件上限 ❌ +- **维持现状**:多一条线,但**不增加架构复杂度**——CDN 数量不等于复杂度 + +**注意最后一条**:你架构的复杂度大头在**构建与搬运**(Gitea、act_runner、中转机、COS、artifact 链),不在 CDN 数量。多留一个多吉云,不会让架构变复杂;砍掉那条搬运链才会。 + +--- + +## 九、建议 + +1. **别换 ESA Pages** —— 2000 文件上限是硬伤,你的站 3267,装不下。 +2. **想收敛到一家,就走 EdgeOne** —— 你已经在用,同一个控制台、同一个项目(`-a global` 实验),或者建个国内站项目。迁移成本近零。 +3. **多吉云可以留着** —— 它免费 20 GB/月、融合 CDN(依托大厂节点)、速度好,而且它是**独立的国内线路**,跟 EdgeOne 互为备份。不构成复杂度问题。 +4. **如果哪天站点瘦身到 2000 文件以内**(比如把 1778 个图片全挪到对象存储),ESA 会重新变成候选 —— 但那时 EdgeOne 也一样能用,还是没必要换。 + +--- + +## 十、待核实项(不装作确定) + +| # | 事项 | 状态 | 怎么验 | +|---|---|---|---| +| 1 | 多吉云当前的回源源站是谁(又拍云存储 / EdgeOne / 其他) | **待确认** | 多吉云控制台看回源配置 | +| 2 | ESA 免费版是否真的"不限速"(vs 峰值 5 Mbps) | **说法矛盾** | 需实测,但没必要为它迁移 | +| 3 | ESA Pages 是否支持 Gitee 作为代码源 | 官方只列 GitHub,第三方提到 Gitee | 若成立,对简化链路有价值 | +| 4 | EdgeOne Pages 的 Git 集成是否支持 Gitee | 上次标的第 5 条,仍未确认 | 若支持可砍掉自建 Gitea + act_runner | + +--- + +## 十一、一句话总结 + +**阿里云 ESA 确实是对标 EdgeOne 的产品,能力同级——但 ESA Pages 限 2000 文件,你的站 3267 文件,直接在门口就被拦下。** +**换厂商解决不了你的问题;你的问题在构建与搬运链,不在 CDN 选谁。**