Files
blog/docs/Gitea迁移指南.md
T
zqlit 7abef4ac13 docs: 全量梳理文档结构;补 Gitea 迁移指南 / 架构精简候选;架构总览补证书管家子系统
用户四问:「把项目文件全部理一遍 还有readme文件」「gitea 后面可能还要迁移 帮我写一个迁移文档」
「证书管家做完了吗」「感觉现在架构还是复杂了」

一、文档梳理:根目录 15 个 md → 2 个

根目录只留 README.md(入口)+ 架构总览.md(现状唯一事实源),其余全部归位:

  docs/
  ├── README.md              ← 新增:文档地图(入口 / 当前有效 / 历史归档 三层)
  ├── 证书管家.md             ← 原「函数版证书管家-方案.md」改名
  │                              (它早已是「现状+沿革」文档,标题名不副实 —— 签发早已不在 CF Worker)
  ├── Gitea迁移指南.md        ← 新增
  ├── 架构精简候选.md         ← 新增
  ├── CNB构建落地方案.md / GITEA_SECRETS.md
  └── archive/{平台与选型,功能与修复,主题与内容}/   ← 32 份

- 32 个归档文档统一加 `> 📦 本文档已归档` banner,并指向架构总览
  —— 这个仓库历史文档里全是「权威口径」,混看很容易拿废弃结论当现状
- gitea-backup/ → deploy/gitea/(原名「backup」不准,它是部署包;与其它 deploy 单元并列)
- 新增 md 链接校验(python 脚本,中文路径用 sed 不安全)→ 首轮 18 处断链
  (多一层目录要让 banner 里的相对路径补一个 ../)→ 修正后 0 断链

二、新增 docs/Gitea迁移指南.md(★ 有死线:境外 VPS 11 月到期)

★ 核心价值是「指出迁移面已大幅缩小」:仓库原有的 deploy/gitea/迁移前检查清单.md
(2026-10-04)是为「Gitea + act_runner + 又拍云同步 *整套* 搬迁」写的,**那个前提已经不存在**——
构建归 CNB,runner / upyun-sync / 中转机 / COS 都不需要了。迁移实际只剩「搬数据卷 + 改地址」。

★★ 全文最重要:本仓有 9 处写死了旧地址,按易漏程度排序(1-3 本机,4-5 线上)
  1 本机 .git/config 的 gitea remote
  2 scripts/setup-cnb-remotes.sh 的 GITEA_URL 默认值
  3 deploy/editor-api/bootstrap.sh 的 GITEA_URL 默认值
  4 ★ /srv/editor-api/.env 的 GITEA_URL      ← 漏了会「持续报错但只警告不阻断」,没人发现
  5 ★ /srv/blog 的 gitea remote               ← 同上
  6 docs/GITEA_SECRETS.md
  7-9 架构总览.md / README.md / editor-api/README.md
(不用改:docker-compose.editor.yml、server.mjs、pushall 别名 —— 只引用 remote 名字)

另含:建议新实例改用域名而非 IP(以后搬机器只改 DNS;⚠️ Gitea 28 起只读 ROOT_URL 不读
[server] DOMAIN);rsync 而非 dump(属主必须 uid/gid 1000);切换顺序「先建新的→验证→再拆旧的」;
验证清单强调 `git push gitea --dry-run`;第九节给出「干脆不留这个辅仓」的选项与判断依据。

顺带修掉两个硬伤:
- deploy/gitea/docker-compose.yml 的镜像 tag `1.28.0-rootless` 根本不存在
  (Gitea 28 起去掉 1. 前缀)→ 改 28.0.0-rootless(pin 死,不用 latest)
- deploy/gitea/.gitignore 漏了 backups/(跑一次备份就会把含 secrets 的 dump 写进历史)

三、证书管家核实(结论:已上线运行,2 个遗留)

线上实测:容器 Up (healthy);/preflight 7/7 全过;3 组域名;下次自动续期 04:10。

遗留 ① writeapi.usj.cc 证书没纳管(真问题):nginx 配置在 /www/conf.d/writeapi.usj.cc.conf,
不在 /www/sites/ 管理树里 → 1Panel 的 ssl/upload+sslID 物化碰不到它。
实测仍是 RSA / CN=usj.cc / 到期 2026-12-07,本项目续期不会更新 → 12 月会断。
遗留 ② dnsapi.usj.cc 没上 HTTPS。
(澄清假问题:200181.xyz 公网看到的 LE 证书是 CF 自家边缘证书,与本项目无关)

四、新增 docs/架构精简候选.md(回应「架构还是复杂了」)

结论:复杂度不在件数,在「跨 6 个环境,其中 4 个要自己维护」。国内机 9 个容器里属于本项目的
只有 3 个,能动的只有 2 个。5 个候选 + 建议执行顺序:
  1 停 certimate 容器(工作流已全停用)——先 stop 观察,别急着删
  2 cn-dns-helper 大概率已是遗留(「Worker 侧签发」时代产物;现在签发在国内机且自带 dnsprovider,
    /preflight 显示 CF 凭据可读;Worker 侧 dnsremoted.ts 已无任何引用)
  3 Gitea:迁 or 不留
  4 两套写作前端收敛(本轮不动,先看使用频率)
  5 writeapi.usj.cc 证书纳管(12 月死线)
并明确列出 6 项「必要复杂度,不建议动」。

五、架构总览补齐证书管家子系统(★ 之前完全缺席)

一个完整子系统在「事实源文档」里一个字都没有 —— 这本身就是文档债。补:
- §0 一句话(四→五个子系统)、§1.1 子系统表、§1.2 地址地图两行
- **新增 §5.7 证书管家**:职责划分 / 为什么非要这么分(Worker 免费版 CPU 10ms)/
  三条不能破的红线(Worker 不得签发 · 证书路由只认 Bearer 会话 · 角色必须正着枚举放行)/
  纳管域名表 / 与 certimate 的关系 / 已知遗留
- .cnb.yml 行数 284 → 337(数字漂了);头部更新时间 → 2026-10-06;附录表改指 docs/
- §6 待办:#13(证书遗留)、#14(复杂度盘点)新增;#7 改写为「Gitea 11 月到期,★ 有死线」

六、记忆

.workbuddy/memory/MEMORY.md 新增「文档结构」「Gitea 迁移死线」两节;证书管家节补实测与遗留。
2026-10-06 22:27:36 +08:00

249 lines
12 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.
# Gitea 迁移指南
> **什么时候看这篇**:自建 Gitea 要换机器 / 换地址时,**从头照着做**。
> 迁移的坑不在「搬数据」,而在**本仓有 9 处地方写死了旧地址**(第三节),漏一处就会在旧机下线那天才报错。
---
## 一、为什么要迁
| 项 | 现状 |
|---|---|
| 承载机器 | **境外 VPS `23.254.236.47`**(2 核 / 2 G 内存 / 50 G 磁盘) |
| **到期时间** | **2026 年 11 月**(见 `deploy/gitea/README.md` 原始记录) |
| 版本 | Gitea **28.0.0**(`/api/v1/version` 实测) |
| 实例地址 | `http://23.254.236.47:3001`(HTTP 直连 :3001,**没有走域名/证书**) |
| 仓库 | `zqlit/blog`(私有) |
| 账号 | `zqlit` |
**它在架构里的角色**:**代码辅仓** —— 只推不拉、不参与构建、不受第三方平台规则约束。
主仓是 CNB(`origin`,推送即触发构建)。
> 换句话说:**它是备份体系里「在线的那一路」**,不是构建链路的一环。
> 这一点决定了迁移比想象中简单得多 —— 见下一节。
---
## 二、★ 迁移范围已经大幅缩小(先读这节,别照旧清单瞎忙)
仓库里的 [`deploy/gitea/迁移前检查清单.md`](../deploy/gitea/迁移前检查清单.md) 是 **2026-10-04** 写的,
当时的目标是「把 **Gitea + act_runner + 又拍云同步** 整套搬到国内机」。
**那个前提已经不存在了**:
| 2026-10-04 的迁移面 | 现在 | 为什么 |
|---|---|---|
| Gitea 本体 | ✅ **仍要迁** | 唯一的代码辅仓 |
| `act_runner`(自建 Actions) | ❌ **不需要** | 构建已由 **CNB** 托管,`.github/` 早已删除 |
| `upyun-sync`(又拍云同步容器) | ❌ **不需要** | 又拍云同步已在 CNB 流水线里 |
| 广州中转机 | ❌ **不需要** | CNB 构建节点在国内,直连又拍云 |
| `cos_sign.py` / COS | ❌ **早已砍掉** | 见归档的 `砍COS改造步骤.md` |
→ **迁移实际只剩一件事:把 Gitea 的数据卷搬到新机器,再把 9 处地址改掉。**
`deploy/gitea/docker-compose.yml` 里那一大坨 `runner` / `upyun-sync` 服务定义,
**迁移时不必启动**(可以直接注释掉,或干脆不管 —— 只 `up -d gitea` 即可)。
---
## 三、★★ 必须同步修改的位置(漏一处 = 旧机下线后才报错)
按「易漏程度」排序。**第 4、5 项是最容易漏的**,因为它们在线上机器上,不在本仓库里。
| # | 位置 | 改成 | 漏掉的后果 |
|---|---|---|---|
| 1 | **本机** `.git/config` 的 `gitea` remote | 新地址 | `git pushall` 推不动辅仓 |
| 2 | `scripts/setup-cnb-remotes.sh` 里的 `GITEA_URL` 默认值 | 新地址 | 重跑脚本时把远端**指回旧机** |
| 3 | `deploy/editor-api/bootstrap.sh` 里的 `GITEA_URL` 默认值 | 新地址 | 新部署的容器指向旧机 |
| 4 | ★ **线上国内机 `/srv/editor-api/.env`** 的 `GITEA_URL` | 新地址 | 写作后台发文章时辅仓推送**持续报错**(主仓成功、只警告,**不会有人发现**) |
| 5 | ★ **线上国内机 `/srv/blog`** 的 `gitea` remote | 新地址 | 同上 |
| 6 | `docs/GITEA_SECRETS.md` | 地址 + 凭据 | 后人照文档操作时指向旧机 |
| 7 | `架构总览.md`(§1.2 地址地图 / §2 旅程图 / §5.2 远端表 / §6 待办 #7) | 新地址 | 文档失真 |
| 8 | `README.md`(发布流程图 + 推送说明 + 脚本表) | 新地址 | 文档失真 |
| 9 | `editor-api/README.md`(辅仓那一行)、`scripts/backup-run.mjs`(头部注释) | 新地址 | 文档失真 |
**不用改的**(它们只引用 remote 名字 `gitea`,不含地址):
`docker-compose.editor.yml`(`PUSH_REMOTES: origin,gitea`)、`editor-api/server.mjs`、
`pushall` 别名本身。
### 一条命令改本机那两处(1 + 2)
```bash
cd /e/GitHub/blog
# 1) 本机 remote —— 换 GITEA_URL 即可,凭据会自动拼进去
GITEA_URL='http://<新机IP>:3001/zqlit/blog.git' \
GITEA_PASS='<Gitea 密码或访问令牌>' \
bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
# 验证:两个远端都在、地址是新的
git remote -v | sed -E 's#://[^@]*@#://***@#'
```
### 线上国内机那两处(4 + 5)
国内机没有 SSH,走 1Panel API 执行(本机 `python .editor-tmp/cnrun.py <脚本>`):
```bash
set -uo pipefail
NEW='http://<新机IP>:3001/zqlit/blog.git'
PASS='<Gitea 密码或访问令牌>'
# ① .env —— 改 GITEA_URL(GITEA_PASS 不变的话不用动)
sed -i "s#^GITEA_URL=.*#GITEA_URL=$NEW#" /srv/editor-api/.env
# ② /srv/blog 的 remote
git -C /srv/blog remote remove gitea
git -C /srv/blog remote add gitea "$(printf '%s' "$NEW" | sed -E "s#^(https?://)#\1zqlit:${PASS}@#")"
# ③ 重建容器(容器启动时读 .env)
cd /srv/editor-api && docker compose up -d --build
# ④ 验证
docker exec editor-api sh -c 'cd /srv/blog && git remote -v' | sed -E 's#://[^@]*@#://***@#'
curl -s http://127.0.0.1:8017/health; echo # remotes 应为 ["origin","gitea"]
```
---
## 四、建议:新实例用域名,不要再用 IP
现在写死的是 `23.254.236.47:3001` —— **IP 一换,上面 9 处全部要改**。
如果新实例挂个域名(如 `gitea.usj.cc`,DNS 指向新机),以后再搬机器**只改 DNS**,上面 9 处全都不用动。
> ⚠️ **Gitea 28 起不再读 `[server] DOMAIN`** —— 实例域名(含默认 SSH 域名)全部来自 **`ROOT_URL`**。
> 改域名时**只改 `ROOT_URL`** 一处。(`deploy/gitea/docker-compose.yml` 里两个都填了,所以现在的配置没问题。)
换域名的额外成本:要在 1Panel 的 OpenResty 里加一个反代站点 → `127.0.0.1:3001`,并配证书。
(目标机 80/443 已被 1Panel 的 OpenResty 占用,Gitea 容器映射的是宿主 `3001`/`2222`。)
**权衡**:花一次配置,换掉以后每一次迁移都要改 9 处的麻烦。**推荐做。**
---
## 五、迁数据
### 5.1 迁移前的硬伤检查(已修,确认一下即可)
| 项 | 状态 |
|---|---|
| `docker-compose.yml` 镜像 tag | ✅ **已修**(2026-10-06):原来是 `gitea/gitea:1.28.0-rootless` —— **这个 tag 不存在**,Gitea 28 起去掉了 `1.` 前缀。现为 `28.0.0-rootless`(pin 死版本,别用 `latest`) |
| `deploy/gitea/.gitignore` 缺 `backups/` | ✅ **已补**:跑一次 `backup.sh` 会在 `backups/` 落 `gitea-dump-*.zip`(含 secrets)与完整 data 卷,原先漏忽略,`git add .` 一下就进历史 |
### 5.2 数据怎么搬 —— **用 rsync,不用 dump**
| 方式 | 适用 | 说明 |
|---|---|---|
| **rsync 数据卷**(推荐) | 同架构迁移 | 保完整(含仓库、app.ini、头像、LFS)。**用 `rsync -a` 保留属主** |
| `gitea dump` | 跨版本 / 跨数据库 | `deploy/gitea/scripts/backup.sh` 已封装;适合做归档,不适合日常迁移 |
```bash
# 旧机 → 新机(在新机上执行,或先把 data 卷打包传过去)
rsync -av --progress root@23.254.236.47:/opt/gitea/data/ /srv/gitea/data/gitea/
```
> ⚠️ **属主必须是 uid/gid 1000** —— Gitea rootless 镜像按 1000 跑,属主不对容器起不来。
> `rsync -a` 会保留;如果中间过了 Windows 或换了 uid,事后要 `chown -R 1000:1000`。
> ⚠️ **新版本必须 ≥ 旧版本**(现在是 28.0.0)。把数据从新版本搬到旧版本会出兼容问题。
### 5.3 启动
```bash
cd deploy/gitea
cp .env.example .env # 填 GITEA_DOMAIN / GITEA_ROOT_URL(用域名的话)
mkdir -p data/gitea
docker compose up -d gitea # 只起 gitea,runner/upyun-sync 不需要
docker compose ps
```
> `deploy/gitea/docker-compose.yml` 里还留着 `runner` / `upyun-sync` 两个服务定义 ——
> 那是 2026-10-04 的形态,**现在不需要**(构建已交给 CNB)。只 `up -d gitea` 即可,
> 或在迁移时顺手把这两段注释掉。
---
## 六、切换顺序(关键:**别让辅仓出现空窗**)
辅仓的价值在于「主仓出问题时它那儿还有」。所以顺序是 **先建好新的、验证通过、再拆旧的**:
```
1. 新机起 Gitea,数据已就位,验证能 clone
2. 本机配好新 remote(第三节 1、2 项)
3. 从本机推一次,确认新实例能收到 ← 新的已经可用
4. 改线上国内机(第三节 4、5 项),重建容器,实测双推 ← 两条推送入口都切过来了
5. 观察一天:`git pushall` 与后台发文章,都确认辅仓同步正常
6. 旧机下线(等到期即可,不必急) ← 旧的才拆
```
**不要在 3、4 步之前就把旧机停掉** —— 那会出现「新的还没验证、旧的已经没了」。
---
## 七、验证清单
```bash
# ① 本机:两个远端都是新地址
git remote -v | sed -E 's#://[^@]*@#://***@#'
# ② 本机实测双推(推临时分支 → 确认 SHA → 删除)
git pushall
git push gitea HEAD:refs/heads/tmp-migrate-test
git ls-remote gitea refs/heads/tmp-migrate-test # 应回新实例的 SHA
git push gitea --delete refs/heads/tmp-migrate-test
git ls-remote gitea refs/heads/tmp-migrate-test # 应为空
# ③ 三处 main 对齐
echo "local = $(git rev-parse HEAD)"
echo "origin = $(git ls-remote origin refs/heads/main | cut -f1)"
echo "gitea = $(git ls-remote gitea refs/heads/main | cut -f1)"
# ④ 历史完整性:新实例上的提交数应与本地一致
git ls-remote gitea refs/heads/main
git rev-list --count HEAD
# ⑤ 线上容器(国内机执行)
curl -s http://127.0.0.1:8017/health # "remotes":["origin","gitea"]
docker exec editor-api sh -c 'cd /srv/blog && GIT_TERMINAL_PROMPT=0 git push gitea --dry-run'
```
> ★ **最后一条特别重要**:`editor-api/src/git.mjs` 对辅仓失败是**只警告不阻断**的 ——
> 辅仓悄悄推不上去,发布照样报成功。所以**必须主动 `--dry-run` 验一次**,别等要用备份时才发现。
---
## 八、回滚
迁移全程是**加法**(新实例起来 → 改地址 → 推一次),旧实例在最后一步之前一直没动:
```bash
# 回滚本机 remote
GITEA_URL='http://23.254.236.47:3001/zqlit/blog.git' \
GITEA_PASS='<旧密码>' bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
# 回滚线上国内机 .env(把 GITEA_URL 改回去)+ docker compose up -d --build
```
**只要没停旧机,回滚就是把地址改回去。**
---
## 九、★ 迁移前先决定:这个辅仓还要不要留
用户提过「感觉架构还是复杂了」。Gitea 是当前**唯一需要你自己运维的常驻服务**(2 核 2 G 境外机),
而它**不参与构建、不参与发布** —— 纯粹是备份体系里「在线的那一路」。
| 选项 | 换来什么 | 代价 |
|---|---|---|
| **A. 迁到新机**(本文档前面全部内容) | 保留「可 clone、可按提交追溯、不受平台规则约束」的在线副本 | 继续养一台机器 + 一处需要运维的实例 |
| **B. 不迁,去掉辅仓** | 少一台机器、少一个要运维的部件;架构回到「CNB 主仓 + F50 离线加密 bundle」两层 | 少一层在线冗余。离线层仍是**完整历史**(`git bundle --all`),且备份已带失败告警 |
| **C. 换成托管平台的私有仓** | 不用自己运维 | CNB 已是托管平台,再挂一家性质相同的,收益有限(见归档的 `平台与选型/` 系列) |
**判断依据**:Gitea 现在挡的是什么?——「CNB 跟 GitHub 一样出问题」。
但离线 bundle 已经覆盖了这个场景(且加密、带告警、每天自动跑)。
Gitea 多出来的独有价值只有两点:**在线可浏览**、**可增量拉取**(不用等一天一次的备份)。
如果这两点你用不上,**选项 B 是合理的简化**。
> 若选 B:把 `deploy/gitea/` 保留在仓库里即可(迁移清单和部署包留着,随时能重建),
> 然后参考本文档第三节,把那 9 处的 `gitea` 相关引用一并清掉
> —— 别忘了 **第 4、5 项**(线上那两处),否则容器会一直对着一个已经没了的辅仓报错。