Files
dsh_ai1net_server/docs/集群与实例/搬运与共享重建方案_guest_w47到w106_20260915.md
T

174 lines
13 KiB
Markdown
Raw Normal View History

# guest 迁移(w-47 → w-106)与「**共享重建**」方案
> 状态:**规划稿(未执行)** | 立稿:2026-09-15 | 触发:用户要求「规划搬运和重建方案;MCN 依赖应以**可共享**的方式重建,避免后续搬其他用户重复重建」
## 0. 一句话
只搬 **46.3 MB** 不可再生数据;**792.7 MB 的 Python 环境 + 362.7 MB 的 Node 依赖改为「平台侧共享目录 + 新机重建」**,装一次、全平台用户共用。
---
## 1. 事实基础(本轮实测,勿再重测)
| 项 | 数值 / 事实 |
|---|---|
| **必搬(不可再生)** | **48,539,522 B = 46.3 MB / 1193 文件** |
| 可重建 / 可丢 | ≈ 2.9 GiB:`trash` 1379.5 · `ws/.local`(pnpm store) 732.9 · `MCN/.venv` 792.7 · `home/profiles/web/node_modules` 362.7 · `tmp` 26.4 · 各类缓存 ~36 |
| **47 → 106 直连速率** | **~22 KB/s**(实测 5.5 min 走 7.3 MB)⇒ 46.3 MB ≈ **35 min**;1.7 G 需 21 h ✗ |
| 106 现状 | `dshs-worker`(19000) 在跑;**无 provisioner、无 pnpm 链路**;磁盘余 32 G;**已建 OS 账号 uid 100002**;无 guest 数据 |
| **平台现成机制** | 实例 bwrap 已有 `--ro-bind-try /var/lib/dshs/bundled-skills` ⇒ **「平台共享只读目录」这套已经存在**,共享 venv 走同一套路 |
| 平台官方口径 | `src/supervisor/orchestrator.ts:557`:额外依赖走 `pip install --target <ws>/.pylibs` + PYTHONPATH,**或自建 venv(不改平台)** ⇒ 现状是**每用户一份**,正是要改的点 |
**必搬口径(可复现)**:
`du -sb <用户目录> --exclude=trash --exclude=tmp --exclude=.venv --exclude=node_modules --exclude=.local --exclude=.cache --exclude=.npm --exclude=.node-compile-cache --exclude=.pnpm-cache --exclude=.pnpm-store --exclude=__pycache__`
⚠️ 分项相加会大于总量(pnpm store 与 node_modules **硬链接**,`du` 同一 inode 只计一次)—— 报体积别用分项求和。
---
## 2. 依赖分三类 → 共享落点
| 类 | 现状(每用户一份) | 目标 | 落点 |
|---|---|---|---|
| **Python:MCN venv** | **792.7 MB / 人** ✗ | **全平台一份** | `/var/lib/dshs/shared/python/mcn@<版本>/`,bwrap `--ro-bind-try` 进实例 |
| **Node:Profile 依赖** | 362.7 MB / 人(pnpm store 697 MB 里大部分可复用) | **store 共享**;`node_modules` 按 profile 重建 | 共享 store `/var/lib/dshs/shared/pnpm-store` + `pnpm install --frozen-lockfile` |
| **系统库 / 字体** | `ws/syslibs` 5.24 MB + `ws/.fonts` 15.68 MB | 一份 | 同共享目录(或纳入平台 syslibs 机制) |
**收益**:第 2 个及以后用户 ⇒ Python/Node 依赖**不再重复下载**,**省 ≈1.15 GB 与 30+ 分钟/人**。
---
## 3. 关键设计决定(自决,可推翻)
1. **共享目录一律只读挂载**(ro-bind)⇒ 用户改不坏、不会被各自的 pip 污染;写操作仍走用户自己的 `.pylibs`。
2. **版本化 + 显式升级**:目录名带版本(`mcn@1`);升级 = 建新目录 → 切挂载 → 保留旧版供回滚。
3. **MCN 技能改造**:不再自带 `.venv`,改指向共享解释器(脚本里 `PYTHON_BIN` / `PYTHONPATH`);`playwright` 浏览器二进制放共享目录并设 `PLAYWRIGHT_BROWSERS_PATH`。
4. **清单入库**:从现 venv `pip freeze` 导出**固定版本**的 `requirements.txt` 进仓库 ⇒ 共享环境从此可复现(当前项目里**没有**任何清单,这是必须先补的前置)。
5. **平台改动最小化**:只加 `--ro-bind-try /var/lib/dshs/shared`(一行),不动隔离与权限面(仍是只读)。
---
## 4. 执行步骤(每步有验收与回滚)
| # | 动作 | 验收 | 回滚 |
|---|---|---|---|
| 1 | 47 上导出 MCN 依赖清单(`pip freeze` + `playwright --version`) | 清单文件生成并入库 | 无副作用 |
| 2 | 搬 46.3 MB(§1 口径)到 106,落地后 `chown -R 100002:100002` | 字节数/文件数与 47 一致;属主 100002 | 删 106 目标目录 |
| 3 | 106 建共享目录并**重建 MCN venv** | `python -c "import ctranslate2, av, playwright, pandas"` 通过 | 删共享目录 |
| 4 | 平台加一行共享挂载 + build + `systemctl restart dshs` | 实例内 `ls /var/lib/dshs/shared` 可见 | 去掉那行重启 |
| 5 | 106 上重建 profile 依赖(pnpm + lock + `.dsh-stage/*.tgz`) | `node_modules` 生成、bundles 完整 | 删 `node_modules` |
| 6 | 迁移归属:`POST /api/admin/users/:id/dsh/migrate {"targetHost":"w-106"}` | `host_id=w-106`、`epoch+1`、106 出现 scope、guest 能进且文件在 | 再迁回 w-47(**数据仍在**) |
| 7 | 源目录打包 → `/opt/dsh/backups/migrated/guest-<ts>.tar.gz` → 删源 | 归档可解压且清单核对一致 | 从归档恢复 |
---
## 5. MCN venv 的建法:**选定「在 106 重建」**(另一条已否决)
- **选定 A · 在 106 重建**:干净、可复现、跨机架构正确。代价:需先有清单(步骤 1)、需外网装 ~800 MB。
- **否决 B · 从 47 直接把 venv 拷进共享目录**:虽与现状逐字节一致,但走那条 **22 KB/s** 的链路搬 792.7 MB ⇒ **≈10 小时**,且 venv 内含绝对路径,跨机仍有风险 ⇒ **只有缺点,故自决不采用**。
---
## 6. 与「备份机制」的衔接
- **归档区**:`/opt/dsh/backups/migrated/`(已建);源目录**先打包保留、再删**。
- **策略分离(建议)**:
- **用户核心数据(46 MB 级)** → 高频(每日)备份,直接放归档区/对象存储;
- **共享依赖(版本化目录,百 MB 级)** → **按版本**备份(升级时留一版即可),不必按日。
- 后续接「备份存储空间」:归档目录可整体同步到对象存储(OSS/COS);挂载点与保留策略一次定好,避免各 worker 各写一套。
---
## 7. 红线与「不得净变差」复核(R11)
| 红线 | 结论 |
|---|---|
| R2 不改官方 dsh | ✅ 只改平台自己的 spawn 参数 + 用户数据 |
| R7 批量/别人 lane | 迁移是用户明确指令;**平台加一行挂载**属我 lane,但先单点验证(步骤 4 先在一台 worker) |
| R8 中断在线用户 | 本服务器为开发环境,可直接做,**动手前一句话说明** |
| R10 属主 | 目标侧一律 `chown -R 100002:100002`(与 47 `provision-new-users.sh` 同口径) |
| **R11 净变差** | **共享化让体积/性能/扩展性都变好**(每人省 1.15 GB 与 30 分钟);新增的只有一个**只读**挂载点 ⇒ **权限面不变** ⇒ 无净变差 |
---
## 8. 遗留(本方案不解决,需另立)
1. **106 没有账号 provisioner**(`dsh-provision.path` 只在 47)⇒ 新用户被调度到 w-106 时**无人建 OS 账号**,且 worker agent 里也没有 useradd ⇒ **新用户可能起不来**。建议把 provisioner 机制铺到每个 worker(否则「新用户落 w-106」这条设计不成立)。
2. **MCN 项目里没有任何依赖清单**(`requirements.txt`/`pyproject.toml` 都没有)⇒ 步骤 1 是硬前置。
3. 现有用户的 `.venv` 仍是各自的(本方案只对**新迁移/新建**生效);存量收敛需单独排期。
---
## 9. 决策更新(2026-09-15 20:5x · 用户定,开始执行)
- **那 4 个重包「暂不重建」**:`playwright==1.62.0` · `ctranslate2==4.8.2` · `onnxruntime==1.30.0` · `av==18.1.0`(合计 ≈ **443 MB**)⇒ 新环境**先不装**(MCN 依赖这几项的功能暂时不可用,后续需要时再单独排期)。**其余按本方案处理。**
- **依赖清单已导出(硬前置完成)**:`mcn-req-full.txt`(**51 包**)与 **`mcn-req-no4.txt`(47 包 —— 重建用这份)**,落在工作区根目录。
⚠️ 口径注意:venv 里**没有 pip**(`pip freeze` 直接 `UnicodeDecodeError`)⇒ 改为扫 `site-packages/*.dist-info` 生成清单(等价且更稳)。
- **共享 venv 的基础运行时**:现有 venv 由 `/usr/local/bin/python3 -m venv` 创建,指向 **`/usr/local/dsh-runtime/python-3.12.14/bin/python3.12`**(平台自带的**共享** Python 运行时)⇒ 共享 venv 应基于它建,**不要**用系统 python。
- 执行进度:**核心数据搬运已在 106 后台启动** —— 脚本 `/root/migrate-guest.sh`,日志 `/var/log/migrate-guest.log`,起于 **2026-09-15 20:58:16**,预计 **~35 分钟**(46.3 MB @ ~22 KB/s)。
⚠️ 该次搬运**已作废**:其 `--exclude` 写的是 `$U/node_modules`、`$U/.venv` 等**顶层路径**,而真实依赖在 `ws/…`、`home/profiles/web/…` 之下 ⇒ 等于在传全量 3 G(实测 2h10m 只走 133 MB)⇒ 已停并改走 §10 的路径。
---
## 10. 执行实况(2026-09-15 23:15 → 09-16 00:2x)—— **已完成**
### 10.1 带宽实测(决定整条路径)
| 链路 | 实测速率 | 结论 |
|---|---|---|
| 47 → 本机(单流) | **126 kbps ≈ 16 KB/s** | 瓶颈在 47 出口 |
| 47 → 本机(8 路并行) | 783 kbps ≈ 98 KB/s | 并行 ≈ 6 倍收益 |
| 47 → 本机(16 路并行) | 1122 kbps ≈ 140 KB/s | 接近饱和 |
| **本机 → 106** | **33.5 Mbps ≈ 4.2 MB/s** | 极快 ⇒ 作中转最划算 |
| 47 → 106(直连) | ~22 KB/s(历史实测) | 与单流同量级 |
⇒ **选定路径:47 →(8 路并行拉)→ 本机 →(4.2 MB/s)→ 106**。
⚠️ **两个实测坑**:① 106 → 47 的 scp **并发超 ~10 会被 47 的 sshd 拒绝**(`Connection closed`);② 即便降到 6 路,**部分分片仍会静默中断**(只写一半)⇒ 接力脚本**必须逐片比对大小并重拉**(`_relay2.sh`:第 1 轮补 5 片,第 2 轮归零)。
### 10.2 实际搬运量(全部经本机中转 + 逐片校验)
| 项 | 原始 | 压缩后 | 说明 |
|---|---|---|---|
| 用户数据(46.3 MB 口径) | 46.3 MB | 30.1 MB / 15 片 | 与 47 对账**真实缺口 = 0** |
| dsh-runtime(python3.12.14 + jq + rg) | 110 MB | 39.1 MB / 10 片 | ffmpeg/ffprobe(344 MB)暂不传 |
| profile node_modules(含 .pnpm 实体) | 362.7 MB | 89.6 MB / 11 片 | 压缩比 4x |
| business-plugins(3 个 tgz) | 44 MB | 44.4 MB / 6 片 | file-preview + mcn-suite + univer |
| bundled-skills | 12 KB | — | 直连管道 |
| **合计** | — | **≈ 203 MB** | 单流口径需 ~3.5 小时 |
### 10.3 106 侧「本地重建」(不占 47 带宽,与搬运并行)
- **dsh 主程序**:`npm i -g @deepseek-ai/[email protected]` → `/usr/lib/node_modules/@deepseek-ai/dsh`(295 MB,与 47 同版本);并补 `/usr/local/bin/dsh` 符号链接(平台默认以 `dsh` 命令解析)。
- **MCN 的 Python venv**:按 `mcn-req-no4.txt`(47 包)在 106 重建 ⇒ 远优于传 819 MB;`pandas/jieba/matplotlib/bs4` 导入通过。
- **whitelist-cache 不传**:只被 Manager 侧 `src/web/routes/whitelist.ts` 读取,106 是 worker。
- **/var/lib/dshs 顶层对齐**:`bundled-skills` ✅ / `business-plugins` ✅ / `secret.key` 已有 ✅ / `dshs.db` 不传(集群下权威库是 47 的 PG)。
### 10.4 途中发现的三个「实例起不来」级坑(均已修)
1. 🔴 **106 上 `/var/lib/dshs/users` 是 `drwx------`**(47 是 `drwx--x--x`)⇒ `setpriv --reuid 100002` **无法穿越该路径** ⇒ 实例必崩。已 `chmod 711`;`/var/lib/dshs` 755 → 711(收窄,对齐 47)。
2. 🔴 **106 上没有 dsh 主程序**(`/usr/local/bin/dsh` 不存在)⇒ 已装同版本 + 建链接。
3. 🔴 **106 上没有 `/usr/local/dsh-runtime`**(python 3.12.14 / jq / rg 全缺)⇒ 已传。
### 10.5 迁移结果(已验收)
- `POST /api/admin/users/<guest>/dsh/migrate {"targetHost":"w-106"}` ⇒ `{"ok":true,"from":"w-47","to":"w-106","epoch":4,"port":44155}`
- DB:`host_id=w-106`、`epoch=4`(+1)、`folder=/var/lib/dshs/users/4092b965-…/ws` ✔
- 106:`dsh-100002-43771a07.scope` **running**、端口 44155 监听、实例 HTTP **401**(存活)
- 47:guest 的 scope 已消失(只剩 admin 的)
- 插件:`business-plugins 0.3.23` / `portal-entry 0.5.5` 在位,特征串命中,实例日志 0 error
### 10.6 归档与清理
- 源目录归档:`/opt/dsh/backups/migrated/guest-4092b965-20260916.tar.gz`(≈1.1 G,含 trash)⇒ 验证后删除 47 上的源目录。
- 临时物清理:47/106 的 `/tmp/{gmig,rt,nm,bp}*` 已清;106 的 `/root/*.sh` 已清;本机的分片目录移入 `_中间产物_待清理/`。
- 可复用脚本(在 `_中间产物_待清理/`):`_relay2.sh`(带校验重试的接力搬运)· `_relay.sh`(无校验版)· `_mcn-venv-106.sh`(远端重建 venv)· `_gmig-pull.sh`(初版并行拉取)。
---
## 11. 遗留(需另立排期)
1. ~~106 仍缺 ffmpeg / ffprobe~~ ⇒ **2026-09-16 00:2x 已解决**:106 上直接 `dnf install -y ffmpeg jq ripgrep`(走**内网源** `mirrors.tencentyun.com`,**43 MB/s、几十秒**),再符号链接进 `/usr/local/dsh-runtime/bin/`,并在 bwrap 沙箱内实测可执行(ffmpeg 7.0.2)。
⚠️ **教训(重要)**:**先测目标机自己的下载能力,别默认"只能从 47 搬"** —— 106 的 dnf 内网源极快,从 47 搬 344 MB(~40 分钟)纯属绕路。
2. **106 仍无账号 provisioner**(`dsh-provision.path` 只在 47)⇒ 新用户被调度到 w-106 时**无人建 OS 账号**。本次 guest 的账号是手工 `useradd -u 100002` 建的。
3. **MCN 的 4 个重包未装**(playwright / ctranslate2 / onnxruntime / av,≈443 MB):按既定决定暂不重建。
4. **存量用户仍是各自一份 venv / node_modules**:本方案只把 guest 迁完,共享化(§2 的三类落点)尚未实施。
5. **106 上的遗留目录**:`41a9480e-…`(uid 100008,8.9 M,档案 101 的一次性用户残留,PG 行已删)与两个 0 字节目录(`2ade6411` / `5f4a51d2`)。