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

310 lines
13 KiB
Markdown
Raw Permalink 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.

# 分布式网格计算部署指南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 单向 SSH**worker 在 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
```bash
# pyyaml 可选(有则用,无则用内置极简解析器,零 pip 也能跑)
pip3 install pyyaml 2>/dev/null || echo "用内置解析器,无需 pip"
```
### 3.2 每台 worker异地机器
只需:
- **python3**(系统自带)
- **openssh-server**(让 master 能 SSH 进来)
- **tlusty 树**(二进制 + 原子数据)
```bash
sudo apt install python3 openssh-server
```
> **worker 不需要 curl、不需要 pip、不需要 SSH 回 master。**
### 3.3 SSH 免密配置master → worker单向即可
```bash
# 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
```bash
# 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`
> ```bash
> sudo service ssh start 2>/dev/null
> ```
> 或者每次手动 `sudo service ssh start`。
**worker 不需要 SSH 回 master。**
### 3.4 分发 TLUSTY 树到每台 worker首次约 2GB
在 master 上对每台 worker
```bash
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
```bash
# 只推必要子目录,不要推整个树(避免跟踪符号链接)
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每次代码更新
```bash
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 时:
```bash
scp -r src/ templates/ seeds/ config_dense.yaml $TARGET:$REMOTE_ROOT/cno_grid/
```
### 3.6 验证 worker 部署完整性
```bash
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 查找种子的位置,**无需任何额外操作**。
确认种子可用:
```bash
RESULTS=<你的 results 实际路径> # 如 /mnt/e/fmq/grid
echo "收敛种子数: $(find $RESULTS -maxdepth 2 -name '*.7' | wc -l)"
```
> 注意:旧的 `.seeds_remote/` 目录(七牛时代的本地缓存)已不再使用,
> 可以删除。master 现在直接扫 `results/`。
## 4. 配置 dist_config.yaml
```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
```bash
cd /home/dckj/program/tlusty/tl208-s54/cno_grid
python3 src/dist_master.py dist_config.yaml --dry-run
# 输出:待计算 N 点,总并发 X 核,预计 ~Y 小时 + 部署检查清单
```
### 5.2 全量跑
```bash
nohup python3 src/dist_master.py dist_config.yaml > results/dist_run.log 2>&1 &
```
### 5.3 监控
```bash
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` 续算
```bash
# 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.json` `converged=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 用密钥免密不用密码