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

12 KiB
Raw Blame History

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 是 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)

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 <脚本>):

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 已封装;适合做归档,不适合日常迁移
# 旧机 → 新机(在新机上执行,或先把 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 启动

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 步之前就把旧机停掉 —— 那会出现「新的还没验证、旧的已经没了」。


七、验证清单

# ① 本机:两个远端都是新地址
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 验一次,别等要用备份时才发现。


八、回滚

迁移全程是加法(新实例起来 → 改地址 → 推一次),旧实例在最后一步之前一直没动:

# 回滚本机 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 项(线上那两处),否则容器会一直对着一个已经没了的辅仓报错。