Files
blog/架构总览.md
T
zqlit af1bbf9c1e chore(远端): 彻底剔除 GitHub —— 删除已退役的 GitHub Actions 定义
用户明确「GitHub 现在整改,不能再作为备份库」,据此把 GitHub 从本项目
所有环节清干净。上一轮(b39dc297)已完成 remote / 推送别名 / 发布链路的
去 GitHub 化,本轮清掉最后一块实体:CI 定义。

- 删除整个 `.github/` 目录(3 个文件、共 1268 行):
    deploy.yml          1169 行  原主构建发布流水线(已由 .cnb.yml 接管)
    aliyun-backup.yml     54 行  备份到阿里云 OSS
    cleanup.yml           45 行  清理 artifacts / cache
  已确认无任何脚本、配置或流水线引用它们(全仓只有 .cnb.yml 的注释提过 deploy.yml)。
  实现完整保留在 git 历史里,需要时可翻。

同步更新引用它们的注释与文档(否则会指向不存在的文件):
- `.cnb.yml` 头部:说明原流水线已删除、要查旧实现请翻 git 历史;
  去掉「见 deploy.yml 第 290 行」这类失效指向
- `CNB构建落地方案.md`:
    §4.0 标题「CNB 主仓 + GitHub 备份」→「+ 辅仓备份」
    §pushurl 缺陷论证里「GitHub 那份备份」→「辅仓那份备份」
    §敏感文件:「已核实 github.com 返回 Page not found」→ 更新为
      账号被标记 + 仓库被 AUP 清空,该威胁面已消失
    §历史清理的判断已变:当初不做的唯一顾虑(备份会分叉)已随 GitHub 消失,
      filter-repo 现在是可行窗口(代价:SHA 全变、自建 Gitea 需重推)
- `架构总览.md`:§5.1 标注当前实际只用 `git push origin main`(gitee remote 未建);
  §6 #1 改写为「GitHub 已彻底退出备份体系」、#5 由「可删」改为「已删除」;
  末尾「已退役参考」改为「已删除,可翻 git 历史」
- `README.md`:脚本表补上 `backup-bundle.mjs` / `backup-task.cmd`;
  `setup-cnb-remotes.sh` 的说明去掉 GitHub
2026-10-06 21:11:48 +08:00

301 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 优世界博客 · 架构总览
> 更新时间:2026-10-04(CNB 迁移完成当日)
> 状态:**迁移已落地并跑通**(四线路全绿,单次发布约 3.5 分钟)
> 本文档只描述**现状**;迁移前的复杂度审计与选型过程见文末「相关文档」。
---
## 0. 一句话
四个子系统,一条 **3 步**的托管发布链路。构建与发布已从「自建 Gitea + act_runner + 广州中转机」迁到
**腾讯云 CNB(cnb.cool)** —— **需要自己运维的机器从 3 台降到 0 台**。
---
## 1. 全景
### 1.1 四个子系统
| # | 子系统 | 技术栈 | 跑在哪 | 状态 |
|---|---|---|---|---|
| **1** | **内容系统** | Hugo 0.128.2 extended + Ying 主题,138 篇 md;产物约 3200 文件 / 424MB | 产物分发到 3 个 CDN | 正常 |
| **2** | **评论系统** | `blog-admin/` = artalk-cf(Artalk v2 兼容服务端)+ RSS 机器人 | Cloudflare Workers + D1 + KV → `api.200181.xyz` | ✅ 零服务器零运维 |
| **3** | **写作系统** | `write-server/`(Next.js 16,`post.usj.cc`)<br>`write/`(本地 Windows 前端) | 独立 Docker 主机/本机 | 可用(两套前端功能重叠) |
| **4** | **发布基础设施** | **CNB 流水线**(`.cnb.yml`)+ 又拍云 + 多吉云 + EdgeOne | 腾讯云 CNB(**托管**) | **新建,已跑通** |
### 1.2 服务与地址地图
| 角色 | 地址 / 位置 | 说明 |
|---|---|---|
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发 |
| **代码备份** | `github.com/zqlit/blog` | 仅作备份,**不参与构建** |
| **构建 + 发布** | CNB 流水线(腾讯云国内节点,4 核 8G) | 托管,0 元(免费额度内) |
| **密钥仓库** | `cnb.cool/zqlit/blog-secrets` | CNB「密钥仓库」类型,10 项变量经 `imports` 注入 |
| **境内源站** | 又拍云对象存储 | 由 CNB 国内节点**直传**(原为 `if: false` 的死代码) |
| **国内加速** | 多吉云 CDN | 回源又拍云 |
| **境外线路** | EdgeOne Pages(项目 `hugo-blog`,`--area overseas`) | 腾讯 EdgeOne 国际站 |
| **评论后端** | `api.200181.xyz` | Cloudflare Workers |
| **通知** | QQ 邮件(`imql@qq.com`) | 已收敛为**单一通道** |
> 关键点:境内、境外仍是**两条独立线路**,但**由同一条流水线一次推完** ——
> 这是本次迁移最大的结构性改善。
---
## 2. 一次发布的完整旅程(3 步)
```
① 写作 write-server(网页) 或 write/(本地 Windows)
│
② git push git pushall = git push origin main ; git push gitee main
│ ├─ origin → cnb.cool/zqlit/blog (主仓,触发构建)
│ └─ gitee → gitee.com/…/blog (辅仓,异地备份,不构建)
▼
③ CNB 流水线(国内节点,约 3.5 分钟,7 个 stage 顺序执行)
├─ 1. Hugo 构建 草稿隐藏预处理 → hugo --minify --gc → 算构建哈希
├─ 2. 同步到又拍云 upx sync(境内直传,非阻断但如实上报)
├─ 3. 刷新又拍云 CDN purge 首页 + sitemap/rss/archives/posts 等固定入口
├─ 4. 刷新多吉云 CDN node scripts/refresh_cdn.js
├─ 5. 部署 EdgeOne edgeone pages deploy --area overseas
├─ 6. 上报部署状态 POST api.200181.xyz/api/deploy-status
└─ 7. 邮件通知 成功 → stages 末尾;失败 → failStages
```
**对照**:迁移前是 **7 步,中间有 3 个环节是你自己运维的服务器**。
---
## 3. 流水线细节(`.cnb.yml`,284 行)
| 触发 | 条件 | 说明 |
|---|---|---|
| push | 推送到 `main` | 主链路 |
| crontab | `0 9 * * *`(Asia/Shanghai) | 每天 09:00,与 push **共用同一组 stage**(YAML 锚点) |
| 机制 | 实现 |
|---|---|
| 构建镜像 | `deploy/Dockerfile`,hugo 二进制**随仓库携带**(`bin/linux/hugo`,linux/amd64) |
| ⚠️ docker 上下文 | `by: [bin/linux/hugo]` —— CNB 的 docker build **只看得见 Dockerfile + by 列出的文件**,漏写会报 `not found` |
| 密钥注入 | `imports: https://cnb.cool/zqlit/blog-secrets/-/blob/main/secrets.yml` |
| stage 间传状态 | 落盘 `.ci_status`(原版靠 `needs.*.outputs`) |
| 并发控制 | `lock.cancel-in-progress`(对应原 `concurrency`) |
| 非阻断 | 又拍云三步 + 状态上报 + 邮件用 `allowFailure: true`,失败仍写状态如实上报 |
| 资源 | `cpus: 4`(4 核 8G) |
---
## 4. 迁移前后对比
| 维度 | 迁移前(Gitea 自建) | 迁移后(CNB) |
|---|---|---|
| 发布链路环节 | **7 步** | **3 步** |
| 自维护机器 / 服务 | **3 台**(Gitea 主机 + act_runner + 广州中转机) | **0**(构建托管) |
| 流水线定义 | GitHub Actions **1169 行** | `.cnb.yml` **284 行** |
| 产物传递 | 打 `tar.zst` → artifact → 中转机跨境下载 | 同一 pipeline **共享工作目录**,无需传递 |
| 又拍云直传 | `if: false`(境外 runner 必挂,**从未跑通**) | **已跑通**(国内节点直传) |
| COS 中转层 | 需要(存储 + 流量账单) | **已砍** |
| `github.server_url` 兼容分支 | **8 处** | **0** |
| 通知 | 3 套(TG / 飞书 / 邮件),约 400 行 | **1 套**(邮件) |
| 图片优化 + 反向 commit | CI 内每轮白跑(15 张老图处理不掉) | **未迁**(如需保留,照搬 `scripts/optimize_images.js`) |
| 构建环境 | 境外 VPS | 腾讯云**国内节点** 4 核 8G |
| 费用 | VPS 月租 | **0 元**(100GiB 仓库 + 160 核时/月) |
| 单次发布耗时 | — | **约 3.5 分钟** |
> 原链路里那些补丁(COS 中转 / `sync.sh` 每分钟轮询 / artifact 打包 / 兼容分支)
> **唯一根因是「runner 在境外」**。CNB 节点在国内,这一整层随之消失。
---
## 5. 运维要点
### 5.1 推送
```bash
git pushall # 别名 = git push origin main; git push gitee main
git push origin main # ← 当前实际只用这条:gitee remote 尚未建立(见 §6 #2)
```
用 `;` 而非 `&&` —— **一段失败不影响另一段**(实测:串联 `pushurl` 时第一个失败会中止后续)。
### 5.2 远端
| remote | 地址 | 角色 |
|---|---|---|
| `origin` | `https://cnb.cool/zqlit/blog.git` | **主仓**(fetch + push,触发 CNB 构建) |
| `gitee` | `https://gitee.com/<用户名>/blog.git` | **辅仓**(push,异地备份,不参与构建) |
| `gitea` | 自建 `23.254.236.47:3001` | 只读参考,稳定后手工删 |
> ⚠️ **`gh`(GitHub)已于 2026-10-06 移除。**
> 起因:GitHub 账号被平台标记,随后仓库被按 AUP 合规条款清空 ——
> 远端留下一条**孤立提交**(无父提交):
> ```
> c21d5669 2026-10-06 20:08:32 +0800 zqlit <zqlit@users.noreply.github.com>
> chore: remove repository content (AUP compliance)
> ```
> 同时远端 `main` 的历史与本地**完全分叉**(两边只共享 2024-07-11 的 `Initial commit`,
> 之后每个提交 SHA 都不同;远端那份是 989 提交、剥离了 `.env`/私钥/大二进制的变体)。
> 教训:**别把「备份」寄托在会对内容做合规处置的平台上**;
> 且一旦某次历史被重写,SHA 从此再也对不上。
> 远端配置的改动前快照留在 `.workbuddy-backup/git-remotes.20261006-201354.txt`。
### 5.3 辅仓选型(2026-10-06 实测核算)
体积分布(HEAD,实测 457.4 MB / 2509 文件):
| 目录 | 体积 | 说明 |
|---|---|---|
| `content/`(含 `posts/`) | ≈ 308 MB | 博客正文与媒体(mp4/mp3/大图),**搬不走** |
| `bin/linux/hugo` | **83.1 MB** | CNB 构建用的 hugo 二进制,**可移出 git** |
| `static/` | ≈ 38 MB | 站点静态资源 |
| `themes/` | 20.3 MB | Ying 主题 |
| 其余全部 | ≈ 8 MB | 代码、文档、脚本 |
**Gitee 的两条硬约束 —— 都不满足:**
| 约束 | Gitee 免费版 | 本仓库当前 | 判定 |
|---|---|---|---|
| 单仓库容量 | **≤ 500 MB** | `.git` **620 MB** | ❌ 超 24% |
| 单文件大小 | **≤ 50 MB** | `bin/linux/hugo` **83.1 MB** | ❌ **必删** |
| 用户总仓库容量 | 5 GB | 620 MB | ✅ |
| 私有仓协作人数 | 5 人 | 个人 | ✅ 无关 |
`bin/linux/hugo` 是**硬门槛**:**不管怎么瘦身历史,只要它还跟踪在 HEAD 里,Gitee 一律拒收**。
→ 移出后 HEAD ≈ **374 MB**,才有一份余量。
**对照:阿里云效 Codeup 基础版 —— 两条硬约束一条都不存在:**
| 项 | Gitee 免费版 | 云效 Codeup 基础版 |
|---|---|---|
| 单库容量 | ≤ 500 MB | **Git 5 GiB + LFS 5 GiB** |
| 单文件 | ≤ 50 MB | **Web 50 MB / 命令行 200 MB** |
| 620 MB 的 `.git` | 超 24%,推不上 | 占 **12.1%** ✅ |
| 83.1 MB 的 hugo | 超 66%,一律拒收 | 占 **41.6%** ✅ |
| 价格 | 免费 | 0 元/人/年,不限人数、不限仓库数 |
→ 若选 Codeup,`bin/linux/hugo` **不必出库**,`deploy/Dockerfile` 与 `.cnb.yml` 的 `by:`
字段**一行都不用改**(首推 620 MB 也在其 60 分钟推送超时内)。
注意两点:① 默认「中心组织」**只支持阿里云 RAM 账号登录、且必须绑钉钉**,
不接受的可选 2025-12 上线的「地域组织」(华东2上海,支持自建账号密码);
② 基础版无「删库保护」,容量到 90% 提醒、**超限直接禁止写操作**(连删文件都做不了)。
已有的阿里云 OSS AccessKey **不能**用来建 Codeup 仓库 —— git 推送走独立的克隆账号密码或 SSH Key。
> ★ **但这些都只是「换一个篮子」,不是容灾。**
> CNB 与 Gitee/Codeup 同属国内大区,GitHub/GitLab 又执行同一套合规逻辑
> (本轮 GitHub 被按 AUP 清空即为例证)。
> 真正的第三层是**不依赖任何平台账号**的离线备份 —— 见 §5.6。
### 5.4 凭据
- 令牌存**仓库外**:`~/.workbuddy/secrets/cnb-token`
- 本仓 `credential.helper` 指向一个自定义脚本;`.git/config` **无明文**
- ⚠️ 本机全局 helper 是 **GCM**(会弹窗、对第三方 HTTPS 远端还会挂死);wincred 对 cnb.cool 有过「幽灵记录」删不掉 → 故用自定义 helper 绕开
### 5.5 密钥
- 修改密钥 → 编辑 CNB 密钥仓库的 `secrets.yml`(**网页编辑,禁 clone**)
- 本地 `cnb-secrets.yml` 只是粘贴草稿(已 gitignore,**不入库**)
- ⚠️ CNB 的 `imports` 是**一层映射**(key 就是变量名本身);又拍云服务名在 CNB 里必须叫
**`UPYUN_SERVICE`**(旧 Gitea 里叫 `UPYUN_BUCKET`,照抄会「变量未定义」)
### 5.6 整仓离线备份(中兴 F50 / OpenList,2026-10-06 上线)
**目标介质**:中兴 F50 5G CPE 内置 256 GB 存储,上面跑 OpenList
(`http://192.168.0.1:5244`,WebDAV 端点带 `/dav` 前缀)。7×24 常开、走局域网、
**不依赖任何 git 平台账号** —— 这是它比「再选一个代码托管平台」更值钱的地方。
**为什么是 bundle 而不是直接推 git**:WebDAV 不支持原子的 rename/lock,
把 bare repo 挂上去直接 `git push` 会让对象写坏 —— 表面成功、实际随机损坏,
可能几个月后才发现。bundle 是**单文件顺序写**,没有这个问题。
**为什么加密**:本仓库历史里含 `.env`、TLS 私钥、`GITEA_SECRETS.md`。
介质是一台随身设备的内部存储,明文等于把密钥放在可能丢失 / 刷机 / 送修的设备上。
OpenList 的登录只保护**访问通道**,不保护**存储介质** —— F50 丢了拆开就能读。
**为什么不用 gpg**:本机 gpg 2.4.9 在 Windows 下已损坏(反复 `removing stale lockfile`,
node spawn 直接 `EBUSY`)。改用 Node 内置 `crypto` 的 **AES-256-GCM**:
零外部依赖、带认证标签(能检测篡改/截断)、可流式处理 600 MB 不爆内存。
**加密文件布局**:`magic(8) | salt(16) | iv(12) | 密文(...) | GCM tag(16)`,
密钥由 `scrypt(N=32768, r=8, p=1)` 从口令派生。
**用法**(`scripts/backup-bundle.mjs`,零 npm 依赖):
```bash
node scripts/backup-bundle.mjs # 加密 + 上传 + 比对字节数
node scripts/backup-bundle.mjs --verify # 额外下载回来比对 sha256(端到端最可靠)
node scripts/backup-bundle.mjs --dry # 只打包加密,不上传
node scripts/backup-bundle.mjs --keep 3 # 远端保留最近 3 份
node scripts/backup-bundle.mjs --list # 列出远端现有备份
node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
```
**配置**(按序查找,先找到生效):环境变量 → `~/.openlist-backup.env`
→ `.workbuddy-backup/openlist-backup.env`(**已在 .gitignore 内**)。
键为 `OPENLIST_URL` / `OPENLIST_USER` / `OPENLIST_PASS` / `OPENLIST_DIR` / `BACKUP_PASSPHRASE`。
> ⚠️ `BACKUP_PASSPHRASE` 是恢复 bundle 的唯一钥匙,**必须另存进密码管理器**。
> 但它不构成单点:CNB 主仓与自建 Gitea 仍是明文,口令丢了只是少一份备份。
**定时任务**:Windows 计划任务 `Blog-BundleBackup`,每天 **03:30(本地)**
执行 `scripts/backup-task.cmd`(默认 `--verify --keep 3`)。
设置:`InteractiveToken` + `LeastPrivilege`、`StartWhenAvailable`(错过就补跑)、
`ExecutionTimeLimit=PT1H`、`MultipleInstances=IgnoreNew`(防重入)。
日志追加到 `.workbuddy-backup/logs/backup.log`,超 5 MB 自动轮转。
★ `backup-task.cmd` **内容必须全 ASCII**:cmd.exe 按**当前代码页**(zh-CN 是 GBK)
解析批处理文件,而 node 输出 UTF-8;UTF-8 中文注释会吞掉 CR/LF、
把下一行当命令执行(实测踩到:一条 `rem` 被当命令跑了)。
ASCII 是 UTF-8 子集,纯英文注释与 node 的中文输出混写不会乱。
**恢复流程**:
```bash
node scripts/backup-bundle.mjs --decrypt blog-<时间戳>.bundle.enc --out blog.bundle
git bundle verify blog.bundle # 应报 "records a complete history"
git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
```
**实测数据**(2026-10-06,由计划任务实际拉起跑通):
| 阶段 | 耗时 |
|---|---|
| `git bundle create --all` | 6.8 ~ 9.9 s(605.5 MB) |
| AES-256-GCM 加密 | 1.9 ~ 2.9 s(体积不变) |
| WebDAV 上传 | 19.4 ~ 20.6 s → **29.4 ~ 31.3 MB/s** |
| 下载回读 + sha256 比对 | ≈ 19 s,**一致** |
端到端退出码 0;恢复链路已演练(解密 → `git bundle verify` → 1008 提交完整一致)。
---
## 6. 遗留待办
| # | 项 | 说明 |
|---|---|---|
| 1 | `gh` remote(GitHub) | ✅ **已移除**(2026-10-06)。GitHub 账号被标记、仓库被 AUP 清空 —— **GitHub 已彻底退出本项目的备份体系**:远端、推送别名、发布链路、CI 定义全部清干净。辅仓选型见 #2 |
| 2 | **辅仓选型(未定)** | 当前 `pushall` 已写成 `origin + gitee`,但 `gitee` remote **尚不存在** → 每次 `git pushall` 的第二段会报错(**不影响 CNB 那段**)。两条路:**Gitee**(需先解 §5.3 两条硬约束)或**云效 Codeup**(见 §5.3 对照,零改造,但需绑钉钉或选「地域组织」)。定下后再 `git remote add` |
| 3 | `bin/linux/hugo` 出库 | 83.1MB,超 Gitee 单文件上限。**只有选 Gitee 才必须做**;选 Codeup 则不必(占其命令行 200MB 上限的 41.6%)。要做的话需改为构建时获取(国内镜像 / CNB 对象存储),并同步改 `.cnb.yml` 的 `by:` 字段 |
| 4 | `gitea` remote | 只读保留,稳定几天后 `git remote remove gitea` |
| 5 | GitHub 侧旧 workflow | ✅ **已删除**(2026-10-06)。整个 `.github/` 目录移除(`deploy.yml` 1169 行 + `aliyun-backup.yml` + `cleanup.yml`)—— **GitHub 已不再是任何环节的依赖**。旧实现可在 git 历史中查 |
| 6 | 令牌 scope | 缺 `repo-cnb-history:r`(读构建日志);补上后 agent 可自行排错 |
| 7 | Gitea 主机 / 广州中转机 | 发布链路已不依赖;是否退役取决于其它用途 |
| 8 | `README.md` | 仍写着旧 GitHub Actions 流程,待更新 |
| 9 | **敏感文件仍在跟踪中** | `.env`、`GITEA_SECRETS.md`、`write-server/nginx/ssl/privkey.pem`、`content/posts/2024/.../setup-secrets.png` —— 推任何一个新平台前都应先 `git rm --cached` 并轮换凭据。⚠️ 当初「不改写历史」的唯一顾虑是备份会分叉;**GitHub 那份已消失,这个顾虑没有了** → 现在是 `git filter-repo` 彻底抹掉它们的最佳窗口(代价:全部提交 SHA 改变、自建 Gitea 需重推)。离线 bundle 已加密(§5.6),不受此影响 |
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
---
## 附:相关文档
- `CNB构建落地方案.md` — 迁移实施方案与实测数据
- `代码源与构建平台选型.md` — 平台对比(CNB / Gitee / GitLab / EdgeOne / 阿里云 ESA)
- `砍COS改造步骤.md` — COS 下线记录
- `EdgeOne双区域方案评估.md`、`Gitee方案评估.md`、`阿里云ESA评估.md`、`精简方案-只留CF和Hugo.md` — 决策期评估
**证据出处(现行)**:`.cnb.yml`(284 行)、`deploy/Dockerfile`、`bin/linux/hugo`、
`scripts/send_mail.js`、`scripts/refresh_cdn.js`、`scripts/setup-cnb-remotes.sh`、
`scripts/backup-bundle.mjs`、`scripts/backup-task.cmd`。
原 GitHub Actions 流水线(`.github/workflows/deploy.yml`,1169 行)已于 2026-10-06 删除,
需要查请翻 git 历史。