Files
dsh_shenxian/dsh-server-docs/04-调整方案/122-搬运与共享重建方案-guest-w47到w106.md
T
admin 04776af4b1 docs: 工作区根项目文档批量入仓(04-调整方案 113–128 / 交接单 T09–T21 / ops / archive)
起因:用户 2026-09-17 明确「所有文档都要同步,都放在开发仓库 docs 对应文件夹下」。
判据:工作区根 *.md 中在仓库(git ls-files --quotepath=false)搜不到的那些。

入仓 33 份(一律复制,工作区根原件保留不动,避免引用断链):
- 04-调整方案/113–128(16 份 · 原子占号后落盘):覆盖网络 传输方案取舍 / 应用场景与待完善清单 /
  插件化vs改内核 / 问题逐条推演 / 参数表与观测口径;会合中继拆分取证与改造方案;
  集群化改造方案 Manager-Worker;跨节点迁移与节点自举;项目代码分层范式与迭代风险评估;
  搬运与共享重建方案 guest w47→w106;方案规划方法提炼;文档无效信息审计报告;
  会话接续机制复盘与修复;会话接续规范;dsh 客户端化部署方案;dsh 桌面客户端开发方案
- 交接单/archive/交接单-已完成/T09–T21(13 份 · 覆盖网络线已完成单归档)
- ops/(2 份运行态指针:接续入口 / 接续包 · 覆盖网络线)
- archive/(2 份临时与内部简报)

已排除(无需重复入仓):覆盖网络线 10 份方案正文已入档案 103–112(文件名不同)。

登记:INDEX.md §二 新增 04-113–128 共 16 行 + §四 追加 T09–T21 说明 + 机器摘要行刷新
(⛔ 未跑 docs-index-stats.py --write:该脚本会按 \r\n 归一化全文件行尾,故改为字节级单行替换);
README.md 追加 1 条入仓指针;docs-manifest.json 复跑 scripts/docs-manifest.py 刷新。

验收:工作区根 47 份 .md —— 同名已入仓 8 / 本次内容一致 29 / 已知改名映射 10 / 未入仓 0;
git status 待提交清单只含本次新增与登记 3 件(未涉 src/ 与 relay 代码面)。
2026-09-17 18:24:19 +08:00

175 lines
13 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.
# 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`)。