CLAUDE.md 13 KB

CLAUDE.md

本目录是公司 GPU 服务器 192.168.110.67(4 × RTX 2080 Ti 22G)的工作区。真正的服务和模型都在那台远程机器上,这里只放手册、脚本和记录。

目前有两条并行的工作线:

目录 工作线 状态
llm-setup.md / deploy-scripts/ / CHANGELOG.md LLM 推理服务(llama.cpp,对外 8000 端口) Qwen3.8-27B 已上线
raylight/ MiniMax-H3 文生视频(ComfyUI + Raylight 插件,4卡 FSDP 集群) 2026-08-22 起生产默认后端,minimax-h3/ 转热备份

两条线共用同一批 GPU,改动前先 nvidia-smi 看余量——LLM 服务常驻约 29 GB / 88 GB,剩下的才是可用的。下面第「硬件的三条硬限制」一节对两条线都适用(尤其 bf16 不可用这条,minimax-h3/scripts/probe-turing-caps.py 就在探这个)。

接入

ssh admin@192.168.110.67        # 密码 P@ssw0rd

没装 sshpass。建议用 ControlMaster 复用连接(socket 路径必须短,否则超 Unix domain socket 长度限制):

# 一次性建连(用 expect 喂密码),之后所有命令走复用 socket 免密
ssh -o ControlMaster=yes -o ControlPath=~/.ssh/cm67 -o ControlPersist=30m -N admin@192.168.110.67 &
ssh -o ControlPath=~/.ssh/cm67 admin@192.168.110.67 '<命令>'

sudo 用 echo "P@ssw0rd" | sudo -S -p "" <命令>。注意 /data 是 root 属主,admin 直接 mkdir 会失败。

不在公司局域网时(在家、出差),192.168.110.67 连不通,见 remote-access-from-home.md—— 公网 HTTP 隧道(qwen.wudaima.com)能用,但公网 SSH 隧道(22→6000)2026-08-21 实测不通, 详见该文档和 docs/todo/ssh-public-tunnel-broken.md。

先读这个

HANDOFF.md 是每次进来的入口——现在是什么状态、哪些服务停着、下一步该干什么、 已经踩过哪些坑。它比本文件更新得勤,因为它记的是"此刻"。

权威文档

llm-setup.md 是这台机器的完整运维手册——模型清单、启动命令、显存核算、回滚流程、FRP/nginx 映射、硬件限制全在里面。动手前先读它,不要凭记忆回答。

⚠️ 本地这份是服务器上 /home/admin/llm-setup.md 的副本,两边会漂移。 以服务器上那份为准(SSH 进去的人读的是它)。同步:

# 从服务器拉最新
ssh -o ControlPath=~/.ssh/cm67 admin@192.168.110.67 'cat /home/admin/llm-setup.md' > llm-setup.md
# 推回服务器(改完本地后)
cat llm-setup.md | ssh -o ControlPath=~/.ssh/cm67 admin@192.168.110.67 'cat > /home/admin/llm-setup.md'

硬件的三条硬限制

细节见 llm-setup.md 第二、三节,这里只列最容易白费功夫的:

  1. 4 × RTX 2080 Ti = Turing / compute capability 7.5。装不了 FP8 模型(原生需 Ada 8.9+,vLLM 的 fp8_marlin 回退也需 Ampere 8.0+),不支持 bf16。 LLM 走 GGUF + llama.cpp 是唯一实际路径。扩散/视频模型不受此限——PyTorch + bitsandbytes NF4 在 sm_75 上实测可用,见 minimax-h3/。 bf16 的坑比想象中深:sm_75 上 bf16 没有任何高效 attention kernel,PyTorch 不报错、静默掉到 MATH 后端物化整个注意力矩阵,长序列表现为莫名 OOM。全链路改 fp16 才行。
  2. llama.cpp build 有下限。qwen35 架构需 ≥ b10450,低于此加载正常、显存正常、tok/s 正常、但输出全是乱码。上线前必须 --version 查 build 号。
  3. 网络:huggingface.co 与 ghcr.io 的 blob 层都不通,改用 hf-mirror.com 和 ghcr.nju.edu.cn。出口带宽约 5.9 MB/s 封顶,并行下载不会更快,只会互相抢——下载必须用 curl -C - 断点续传 + setsid nohup 脱离 SSH 会话。

目录约定(迁移工作内容时按此放)

一个工作负载一个顶层目录,目录名 = 服务/模型的短名。目录内固定三样:

<workload>/
  README.md      # 这个负载是什么、跑在哪张卡、怎么起、怎么停、怎么回滚
  scripts/       # 服务器上对应目录的副本(保持文件名一致,方便双向 diff)
  notes.md       # 可选:踩坑记录、被推翻的假设、实测数据

顶层只放跨负载的东西:本文件、CHANGELOG.md(按时间倒序,一次变更一节)、llm-setup.md(整机手册)。

迁移进来的工作必须补三件事,否则等于没迁:

  1. 在下面「当前负载」表里加一行(占哪张卡、多少显存、对外端口)。
  2. 在 CHANGELOG.md 顶部加一节,写清结果、被推翻的假设、实测数据——CHANGELOG 的价值在踩坑记录,不在流水账。
  3. 说明脚本对应服务器上的哪个绝对路径。本地全是副本,服务器上那份才是运行的那份,会漂移。

当前负载

目录 跑什么 卡 显存 对外
deploy-scripts/ Qwen3.8-27B-Q5_K_M(llama.cpp,容器 llm38) 0-3 分片 57.3 GB(256K 上下文,2 槽位) 8000 → qwen.wudaima.com
raylight/ 2026-08-22 起 h3-console 的默认后端:ComfyUI + Raylight 插件,4卡当一个 FSDP 集群跑 MiniMax-H3(不是4个独立worker,任何时刻只处理1个请求)。server.py(8100)对外契约兼容旧 gateway.py+api.py,管理常驻 ComfyUI 进程(8188,仅内部) 0-3 全部(FSDP集群) 832×480×64帧实测峰值约20-21GB/卡;124帧会OOM,见 raylight/README.md「已知限制」 8100(内部,h3-console 的 H3_GATEWAY_URL 指向这)
minimax-h3/ 热备份(2026-08-24 起彻底禁用,双重保险)。MiniMax-H3 FL2VA/Ref2VA NF4 文生视频,api.py 四实例(GPU0-3,端口 8010-8013)+ gateway.py 调度层(端口 8000),四卡各自独立能同时处理4个请求。代码/权重保留,但 start-all.sh/start-gateway.sh 和 api.py/gateway.py 本体都已改名成 .disabled-20260824(见目录内 DISABLED-README.md)——2026-08-24 反复出现"停了又自己起来",查了半天不是 cron/systemd,也不是 PM2(那两个是 sd-forge/embedding 的事,见下),真正触发源没查到,干脆把 api.py/gateway.py 本体也改名,python -m uvicorn api:app 从 import 这层就会失败,不管谁/什么在尝试重启都没用。别再凭记忆当"还能秒级拉回"用,得先把三份改名的文件都恢复原名 四卡各一进程(停着,彻底禁用) 单卡 fp32 峰值约 9-14 GB(视时长/分辨率),另占 CPU 内存约 32 GB/worker 8000(停着)
h3-console/ H3 Web 管理台:上传/生成/预览三界面 + 任务持久化。两个进程共享同一份 sqlite/assets:8080(局域网直连,BASE_PATH 空)+ 8081(H3_CONSOLE_BASE_PATH=/console,专供公网子路径用)。2026-08-22 起两个实例的 H3_ENABLE_SUPERVISOR 都关了——fl2va/ref2va 弹性调度器(按partition flip某张卡)是给 minimax-h3 那套"4个独立worker"架构设计的,raylight 的FSDP集群架构下没有"单卡分区"这个维度,开着会出错 不占显存(自己不跑模型) — 局域网 http://192.168.110.67:8080;公网 http://qwen.wudaima.com/console(frp locations=/console 分流,gateway.wudaima.com 那条独立域名确认走不通,缺口在 frp 服务端 60.205.214.169,见 CHANGELOG)

⚠️ "(常驻)whisper GPU3 3.2GB"这条 2026-08-24 确认是过期信息——当时机器上根本没有任何 带"whisper"字样的进程。实际常驻的是 stable-diffusion-webui-forge(PID对应/data/models/ stable-diffusion-webui-forge)和一个 embedding 服务(gunicorn:7590),跟这台服务器的 LLM/H3 两条工作线都无关,2026-08-24 一并停掉了(详见 CHANGELOG)。排卡前还是先 nvidia-smi 看实际 余量,不要照着这张表的旧数字猜,llama.cpp 默认 tensor-split 按总显存而非剩余显存均分, 起新负载不加 -ts 容易把公网服务挤 OOM——不过 LLM(llm38) 目前也已停(见上)。

⚠️ 2026-08-18 上下文提到 256K 后余量大幅收紧:四卡合计已用 61.4 / 88 GB,只剩约 26 GB,GPU2 最紧只剩 5.4 GB。minimax-h3 按「空闲显存 − 2 GB」自适应,等于它的可用显存被同步压缩了。两条线同时跑之前先看 nvidia-smi。

部署脚本

deploy-scripts/ 下是服务器 /home/admin/qwen38-deploy/ 的副本:

脚本 作用
cutover.sh 切换到 Qwen3.8(校验完整性 → 停旧容器 → 起新容器 → 等健康)
restore.sh 一键回滚到 Qwen3.6,约 3-5 分钟
download.sh 断点续传下载模型
pull2.sh 从 NJU 代理拉镜像并 retag
ctx256k.sh 把单请求上下文切到 256K(旧容器改名保留,失败自动回滚)
ggufmeta.py 读 GGUF 元数据(查模型原生上下文上限、层数、KV 头维度)——别凭记忆猜上下文上限,跑这个

回滚依赖三样东西,都不要删:镜像 tag llamacpp-rollback:b9570、模型文件 /data/models/Qwen/Qwen3.6-27B-GGUF/、脚本 restore.sh。

MiniMax-H3 脚本

minimax-h3/scripts/ 对应服务器 /data/models/minimax-h3/scripts/(run.sh 在 /data/models/minimax-h3/run.sh)。权威文档是 minimax-h3/README.md——为什么官方示例 照抄跑不起来、Turing 的 dtype/attention/量化实测结论、多卡为什么只能做独立 worker。

其中 probe-*.py 四件套是当初得出兼容性结论的探测脚本。 换硬件或升级 torch / bitsandbytes 后,先跑这四个再动别的。

当前状态(2026-08-18)

对外服务 http://qwen.wudaima.com(仅 HTTP,无鉴权),经 FRP 指向本机 8000,跑 Qwen3.8-27B-Q5_K_M。

上下文:单请求 256K(--parallel 2 --ctx-size 524288,每槽 262144)。256K 是模型原生上限——GGUF 元数据 qwen35.context_length = 262144,不是 128K;早期文档里的 128K 是低估。已用 150K token 大海捞针实测通过(truncated=0)。代价是并发从 4 降到 2,且 256K 提示光 prefill 约 4.5 分钟。

回滚:docker rm -f llm38 && docker rename llm38_prev llm38 && docker start llm38(约 15 秒)。旧容器 llm38_prev 停着保留,稳定后再删(/ 只剩 29 GB)。

⚠️ 已知安全问题:公网可无鉴权直调 27B 推理;SSH 22 端口经 FRP 映射到公网 6000 且允许密码登录、admin 在 sudo 组。

2026-08-24 更新:LLM(llm38)和旧 H3(minimax-h3 的 gateway.py+api.py)都已下线,不占显存。 llm38 容器 docker ps -a 显示 6 天前就已经停了(Exited (0)),不是本次操作停的,只是文档一直 没更新——qwen.wudaima.com 现在实际上是打不通的,上面这段"对外服务中"的描述已经过期。 minimax-h3 的四个 worker(8010-8013)+ gateway.py(8000)是本次手动按端口 kill 停的, 停完反复自己又起来——一开始误以为是别的并发会话在手动重启,后来确认这台机器当时只有一个人 在用,排查出真正原因是 PM2(系统级进程管理器)在管另外两个不相关的服务(sd-forge/embedding), 已经 pm2 delete+pm2 save 处理掉;minimax-h3 本身的重启触发源没有查到,改用把 api.py/gateway.py 本体也改名的办法从根上堵死,完整排查过程见 CHANGELOG 2026-08-24 「更正」那条。现在这台机器上生产只跑 raylight/(h3-console 默认后端),代码/权重 都还在,两条线要拉回来都还是走各自的 start.sh/docker start llm38,没有删任何东西。

这是一台测试机(2026-08-18 用户明确)

这台机器的用途就是测试各种模型,不是生产环境。 显存不够时直接停 llm38 给别的负载让路是允许的、也是预期做法,不需要为此设计复杂的共存方案。

这条推翻了本文件早期版本里「8000 是对公网服务的,停服务是对外可见动作」的前提。不要再把显存争抢当成需要精巧规避的约束——串行跑就行:

docker stop llm38     # 让出 ~57 GB,四卡各回到约 21 GB 空闲
# ... 跑 minimax-h3 / 别的模型 ...
docker start llm38    # 跑完起回来,约 15 秒

副作用:这样测出来的数字才是干净的(不被抢占扭曲),比在争抢状态下测更有参考价值。

操作纪律

  • 改动线上服务前先确认回滚路径可用。停服务本身不敏感(测试机),但起不回来会很麻烦。
  • 大文件下载和镜像拉取一律后台跑(setsid nohup),不要占着前台等。
  • 不要凭记忆回答这台机器的事。 先读 llm-setup.md 和 CHANGELOG.md——里面记的坑(build 号下限、FP8 不可行、MTP 不激活)都是已经花过时间验证的,重复踩一遍是纯浪费。
  • 多个进程可能同时在这个目录里干活。改共享文件(本文件、CHANGELOG.md、llm-setup.md)用追加/局部编辑,不要整文件覆写。