TLUSTY/cno_grid/DIST.md
2026-07-27 10:40:18 +08:00

13 KiB
Raw Blame History

分布式网格计算部署指南master 单向 SSH

把 CNO 网格计算分发到多台 Linux 服务器并行。适配"大部分机器异地、 只有 master 能 SSH 进 worker"的网络。

1. 架构master 单向 SSH 推/拉)

master(WSL2)                              异地 worker
  tasks.json (本地 flock 安全)              (无常驻进程)
  ├ 派任务: ssh worker "nohup run_one.py    ─▶  被动执行单点
  │   --teff X ... &"                          跑完写本地 conv.json + .7
  ├ 取状态: ssh worker "python3 dist_check  ◀─  被动提供结果
  │   .py --results results"
  └ 取种子: scp worker:.7 master(收敛点)    ◀─  汇聚到 master results/
                                                供后续点的 seed_step 查找

核心原则

  • 只需 master → worker 单向 SSHworker 在 NAT 后也行,不回连 master
  • 任务队列只在 master 本地fcntl.flock 强一致,零并发风险)
  • worker 无常驻进程master 直接 SSH 执行 run_one.py/seed_step.py 跑单点
  • 冷启动零依赖seed=NoneLTE grey 自建大气)→ 绝大多数点完美分布式
  • 种子库就是 master 本地 results/:收敛点的 .7 大气393KB/点)由 master scp 取回汇聚于此,run_grid.find_seed 扫描该目录给后续冷启动失败的点做 seed_step 回退。无需任何外部对象存储。
  • 大产物留 worker 本地.spec 等 13MB→ 不跨网传

2. 并发安全(重点)

任务队列 tasks.json 只在 master 本地文件,用 fcntl.flock 排他锁保护。 master 多线程管理多台 worker但所有 claim/report 都走本地 flock无跨机并发问题。 worker 完全不碰 tasks.json。

3. 一次性环境准备

3.1 master本机 WSL2

# pyyaml 可选(有则用,无则用内置极简解析器,零 pip 也能跑)
pip3 install pyyaml 2>/dev/null || echo "用内置解析器,无需 pip"

3.2 每台 worker异地机器

只需:

  • python3(系统自带)
  • openssh-server(让 master 能 SSH 进来)
  • tlusty 树(二进制 + 原子数据)
sudo apt install python3 openssh-server

worker 不需要 curl、不需要 pip、不需要 SSH 回 master。

3.3 SSH 免密配置master → worker单向即可

# 1. 生成密钥对(已有可跳过)
ls ~/.ssh/id_ed25519.pub || ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519

# 2. 推公钥到每台 worker这一步会要求输一次密码
ssh-copy-id user@worker1
ssh-copy-id user@worker2

# 3. 验证免密BatchMode 不弹交互提示dist_master 用的就是这个模式)
ssh -o BatchMode=yes user@worker1 "echo ok"

localhost 也要配(如果 workers 里包含 localhost

# WSL 默认没跑 sshd先启动
sudo apt install openssh-server -y
sudo service ssh start

# localhost 的 host key 首次连接需要确认BatchMode 下会报
# "Host key verification failed",必须预先加入 known_hosts
ssh-keyscan -H localhost >> ~/.ssh/known_hosts 2>/dev/null

# 自己 SSH 自己也要公钥:
ssh-copy-id localhost

# 验证
ssh -o BatchMode=yes localhost "echo ok"

踩坑WSL 重启后 sshd 不会自启。加到 ~/.bashrc

sudo service ssh start 2>/dev/null

或者每次手动 sudo service ssh start

worker 不需要 SSH 回 master。

3.4 分发 TLUSTY 树到每台 worker首次约 2GB

在 master 上对每台 worker

TARGET=user@worker1
REMOTE_ROOT=~/DataSheel/tlusty   # 记到 dist_config.yaml 的 tlusty_root

# 优先用 rsync支持 --exclude
rsync -avz --exclude='cno_grid/results' --exclude='*.log' --exclude='__pycache__' \
    /home/dckj/program/tlusty/tl208-s54/ $TARGET:$REMOTE_ROOT/

没有 rsync 时用 scp 替代(注意 scp 不支持 --exclude

# 只推必要子目录,不要推整个树(避免跟踪符号链接)
ssh $TARGET "mkdir -p $REMOTE_ROOT/{tlusty,synspec,cno_grid}"
scp /home/dckj/program/tlusty/tl208-s54/tlusty/tlusty.exe $TARGET:$REMOTE_ROOT/tlusty/
scp /home/dckj/program/tlusty/tl208-s54/synspec/synspec.exe $TARGET:$REMOTE_ROOT/synspec/
scp -r /home/dckj/program/tlusty/tl208-s54/data $TARGET:$REMOTE_ROOT/
scp -r /home/dckj/program/tlusty/tl208-s54/cno_grid/src \
       /home/dckj/program/tlusty/tl208-s54/cno_grid/templates \
       /home/dckj/program/tlusty/tl208-s54/cno_grid/seeds \
       /home/dckj/program/tlusty/tl208-s54/cno_grid/config_dense.yaml \
       $TARGET:$REMOTE_ROOT/cno_grid/

踩坑scp -r 会跟踪符号链接! 本地 cno_grid/results -> /mnt/e/fmq/grid(几百 MB 历史结果), scp -r cno_grid/ 会把链接指向的实际内容全推过去。 worker 不需要 master 的 results(它自己建),所以:

  • 用 rsync + --exclude='results',或
  • 用 scp 时只推 src/ templates/ seeds/ config*.yaml,不推整个目录

3.5 分发 cno_grid 代码到每台 worker每次代码更新

rsync -avz --exclude='results' --exclude='*.log' --exclude='__pycache__' \
    --exclude='tasks.json' \
    /home/dckj/program/tlusty/tl208-s54/cno_grid/ \
    $TARGET:$REMOTE_ROOT/cno_grid/

无 rsync 时:

scp -r src/ templates/ seeds/ config_dense.yaml $TARGET:$REMOTE_ROOT/cno_grid/

3.6 验证 worker 部署完整性

ssh $TARGET "ls $REMOTE_ROOT/tlusty/tlusty.exe \
    $REMOTE_ROOT/synspec/synspec.exe \
    $REMOTE_ROOT/data/gfVIS99.dat \
    $REMOTE_ROOT/cno_grid/src/run_one.py \
    $REMOTE_ROOT/cno_grid/templates/cno_atmos.5.tpl"

全部列出无报错即可。

3.7 种子源master 本地 results/

种子(收敛点的 .7 大气)只存在 master 本地的 results/ 目录

  • 每个收敛模型在 results/<model_name>/<model_name>.7,附带 conv.json
  • master 回收异地 worker 的收敛结果时,用 scp 把 .7 取回到这个目录
  • 冷启动失败的点做 seed_step 回退时,run_grid.find_seed 扫描该目录, 按同 family (Teff/logg/logHe) 的 CNO 最近邻 → 全局最近邻选取种子

无需任何外部对象存储(七牛/OSS 等)。worker 端完全不碰种子库。

3.8 从单机切分布式时(已有 results 里的收敛点)

如果之前用 run_grid.py 跑过一批,已收敛的 .7 已经在 results/<name>/<name>.7 里——这正是分布式 master 查找种子的位置,无需任何额外操作

确认种子可用:

RESULTS=<你的 results 实际路径>   # 如 /mnt/e/fmq/grid
echo "收敛种子数: $(find $RESULTS -maxdepth 2 -name '*.7' | wc -l)"

注意:旧的 .seeds_remote/ 目录(七牛时代的本地缓存)已不再使用, 可以删除。master 现在直接扫 results/

4. 配置 dist_config.yaml

master:
  workdir: /home/dckj/program/tlusty/tl208-s54/cno_grid
  grid_config: /home/dckj/program/tlusty/tl208-s54/cno_grid/config_dense.yaml
  results: results

workers:
  - host: localhost                          # 本机也算 worker
    tlusty_root: /home/dckj/program/tlusty/tl208-s54
    nproc: 22
  - host: dckj@192.168.7.102                 # 异地机器
    tlusty_root: ~/DataSheel/tlusty
    nproc: 35
  # ... 每台机器一项

5. 运行

5.1 预览dry-run

cd /home/dckj/program/tlusty/tl208-s54/cno_grid
python3 src/dist_master.py dist_config.yaml --dry-run
# 输出:待计算 N 点,总并发 X 核,预计 ~Y 小时 + 部署检查清单

5.2 全量跑

nohup python3 src/dist_master.py dist_config.yaml > results/dist_run.log 2>&1 &

5.3 监控

tail -f results/dist_run.log          # master 视角(每 20s 打印状态计数)
cat results/grid_status.json          # 汇总
# 看某台 worker 的某个任务日志:
ssh user@worker1 "tail ~/tlusty/tl208-s54/cno_grid/worker_jobs/<模型名>.log"

5.4 断点续算

中断后重跑同一命令,已 done 的点自动跳过tasks.json 持久化在 master

6. 工作流程master 内部)

1. init tasks.json5120 点;本地已 converged 标 done其余 pending
   - 按难度排序CNO 总量升序 → Teff 升序 → logg 降序 → logHe 升序)
   - 每个 task 标注 wave 编号(同 CNO 总量归同一 wave
2. 每台 worker 一个管理线程,并发跑:
   循环:
     a. SSH 查 worker 正在跑的进程数 → 空闲槽 = nproc - running
     b. 每个空闲槽:本地 flock 领一个 pending→runningwave 门控:
        只从最小未完成 wave 取任务,低金属 wave 全终态后才开放下一 wave
     c. SSH 调 dist_check.py 查 worker 已完成的点:
        - 收敛 → scp .7 回 master results/ → 标 done
        - 未收敛 → 扫 master 本地 results/ 找邻居种子 → scp 种子到 worker → SSH 派 seed_step.py 重试
        - seed_step 仍失败 → 标 failed物理极限
3. 全部终态 → 写 grid_status.json

Wave 门控的意义:低金属点易收敛,先算完产出 .7 种子;高金属点冷启动 困难,需要种子做 seed_step。如果无序派发高金属点在种子还没产出时就被派出 去冷启动 → 失败 → 无种子可救 → 标 failed。wave 串行保证种子链从低金属向 高金属递进。实测:无序提交时 60K 收敛率 25%wave 调度后期望 >50%。

7. 文件清单

文件 角色 运行在哪
src/dist_master.py master 调度器(推/拉/seed_step/汇总) master
src/dist_check.py worker 状态查询(被 SSH 调用) 每台 worker
src/claim_task.py / report_task.py 任务原子操作master 本地调) master
src/run_one.py 单点冷启动执行器(被 SSH 调用) 每台 worker
src/seed_step.py 单点种子步进执行器(被 SSH 调用) 每台 worker
dist_config.yaml 分布式配置 master
tasks.json 任务队列(运行时生成) master

dist_worker.py 已废弃(旧双向模型残留),当前架构不用。

8. 带宽与时间估算

大小 说明
单点计算 1200-2500s 主耗时
SSH 派任务 ~1KB/点 命令字符串,可忽略
取 conv.json 1.6KB/点 经 SSH可忽略
取种子 .7(收敛点) 393KB/点 scp 回 master2Mbps≈1.6s
seed_step 种子推送 393KB/点 仅失败点,少数
跨网传输占比 <5% 相比计算可忽略

5120 点 / 2台×57核 ≈ 24 小时vs 单机 100+ 小时)。

9. 从单机 run_grid.py 迁移到分布式

已有 run_grid.py 跑了一部分,想切换到 dist_master.py 续算:

# 1. 停掉正在跑的 run_grid.py
ps aux | grep run_grid
kill <pid>

# 2. 确保 dist_config.yaml 的 grid_config 指向同一个网格配置
#    (如 config_dense.yamlresults 指向同一个结果目录

# 3. 预上传种子(见 3.8 节),否则高金属点 seed_step 无种子可用

# 4. 启动分布式(--force-init 重建 tasks.json已收敛的自动标 done 跳过)
python3 src/dist_master.py dist_config.yaml --force-init

两边用同一个 results/ 目录、同一套 gen_input5.model_name() 命名, init_tasks 会扫 results/<name>/conv.jsonconverged=true 的标为 done完全兼容。

注意:切换后不要再用 run_grid.py 跑同一个 results 目录, 否则两边同时写同一个模型目录会冲突。

10. 故障排查

现象 原因 解决
ssh: connect to host localhost port 22: Connection refused WSL 没跑 sshd sudo service ssh start
Host key verification failed BatchMode 下首次连接不弹确认 ssh-keyscan -H localhost >> ~/.ssh/known_hosts
scp 把 results 全推到 worker 了 scp -r 跟踪符号链接 用 rsync --exclude 或只推 src/templates/seeds
FileNotFoundError: '~/xxx/tlusty.exe' tlusty_root 用了 ~/Python 不展开 ~ dist_config.yaml 改成绝对路径 /home/user/...
worker 收到派发但 run_one 立刻退出 路径错 / 缺库 / 二进制架构不符 手动 SSH 跑一遍 run_one.py 看报错
master 派不出去 SSH 免密没配好 ssh -o BatchMode=yes user@host "echo ok"
worker 算了但 master 没回收 worker_jobs/<name>.log dist_check.py 是否能列出
tasks.json 读到半截 JSON 崩溃 并发写时读方未加锁(已修复) 升级 dist_master.py_read_tasks + tmpfile rename
任务一直 running worker 进程崩了 master 每 2 小时自动重派stale_sec
种子找不到seed_step 失败) master 本地 results/ 里还没有收敛的邻居点 等低金属 wave 先算完产出种子;或先单机跑一批易收敛点
单机模式仍可用 python3 src/run_grid.py config_dense.yaml(完全不受影响)

11. 安全提示

  • 本架构无需任何外部云服务凭证(不再使用七牛/OSS无密钥泄露风险
  • SSH 用密钥免密,不用密码