用户四问:「把项目文件全部理一遍 还有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 迁移死线」两节;证书管家节补实测与遗留。
12 KiB
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 项(线上那两处),否则容器会一直对着一个已经没了的辅仓报错。