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