# MVP V1 远程实验行动文档（待确认后执行）

## 1. 任务复述（我理解的需求）

你希望我在远程机器 `argus@h1` 上，进入目录 `/home2/argus/infra/mvp`，把 MVP V1 的“原本流程”**手动完整跑一遍并验证**。要求：

1) 在宿主机上编写脚本，脚本通过 `docker exec` 在容器内执行命令，负责协调启动顺序（先 head、后 worker）。  
2) 集群拓扑改为：
   - 1 个 Ray Head：**没有 GPU**，并且 Head 的 Ray 资源 `CPU=0`（防止 Ray 把训练任务调度到 head）。
   - 2 个 Ray Worker：各自 **4 GPU**（总 8 GPU）。
3) PPO 训练需要“轻量化”，把 `total_epochs` 改为 `1`。  
4) 先在本地仓库 `src/mvp/v1/` 写好脚本与 compose 文件；再拷贝到远程目录执行与验证。  
5) 在你确认这份行动文档没问题之前，我**不执行**远程操作。

## 2. 本地已准备的文件（在本仓库内）

- `src/mvp/v1/docker-compose.yaml`：3 容器（head + 2 worker），head 不使用 nvidia runtime；worker0/1 各限制 4 GPU。
- `src/mvp/v1/scripts/`：宿主机脚本（内部全部用 `docker exec`）
  - `01_up.sh`：起容器
  - `20_start_head.sh`：启动 Ray head（`--num-cpus=0 --num-gpus=0`）
  - `21_start_workers.sh`：启动 Ray worker 加入集群
  - `30_prepare_data_and_model.sh`：准备 GSM8K 数据与预下载模型
  - `40_submit_ppo_epoch1.sh`：提交 PPO（`trainer.total_epochs=1`，并设置 `nnodes=2, n_gpus_per_node=4`）
  - `run_all.sh`：按顺序一键执行

## 3. 远程环境前置条件（需要你确认/保证）

在 `argus@h1` 上：

- Docker 可用，且有 `docker compose` 插件（Compose v2）。
- NVIDIA runtime 可用（worker 容器需要 `runtime: nvidia`），宿主机有至少 8 张 GPU。
- 不强制要求提前准备 `./verl`：脚本会在宿主机侧检查 `${PWD}/verl`，如果不存在会自动执行：
  - `git clone https://github.com/volcengine/verl.git`

此外本实验默认写入持久化目录：`/home2/argus/infra/mvp/shared`（会自动创建）。

## 4. 拷贝到远程（我执行前会再次征求你确认）

从本地（本机）同步到远程：

1) 同步脚本与 compose：
   - `rsync -av ./src/mvp/v1/ argus@h1:/home2/argus/infra/mvp/src/mvp/v1/`
   - `rsync -av ./specs/mvp/v1_action.md argus@h1:/home2/argus/infra/mvp/specs/mvp/v1_action.md`
2) `verl/` 默认不需要同步（远程会 clone）。如果你更希望固定版本/避免网络波动，也可以手动同步：
   - `rsync -av --delete ./verl/ argus@h1:/home2/argus/infra/mvp/verl/`

## 5. 远程执行步骤（在宿主机上）

在远程机器执行：

1) 进入目录：
   - `cd /home2/argus/infra/mvp`
2) 确保脚本可执行（首次同步后需要做一次）：
   - `chmod +x ./src/mvp/v1/scripts/*.sh`
3) 启动容器：
   - `./src/mvp/v1/scripts/01_up.sh`
4) 安装 editable 版 `verl`（保证 `python -m verl...` 可用）：
   - `./src/mvp/v1/scripts/10_install_verl_editable.sh`
5) 启动 Ray Head（禁止调度到 head）：
   - `./src/mvp/v1/scripts/20_start_head.sh`
6) 启动两个 Ray Worker 加入集群：
   - `./src/mvp/v1/scripts/21_start_workers.sh`
7) 准备数据 + 预下载模型（落到 `./shared`）：
   - `./src/mvp/v1/scripts/30_prepare_data_and_model.sh`
8) 提交 PPO（`total_epochs=1`，必须用 `ray job submit` 在 head 提交；通过 `--entrypoint-resources` 强制 driver 调度到 worker）：
   - `./src/mvp/v1/scripts/40_submit_ppo_epoch1.sh`
9) 观察状态：
   - `./src/mvp/v1/scripts/50_status.sh`
   - 打开 Ray Dashboard：`http://<h1宿主机IP>:8265`

也可以一键跑：
- `./src/mvp/v1/scripts/run_all.sh`

## 6. 验收与验证点（执行时我会逐项检查）

1) Head 节点无 GPU：在 head 容器内 `nvidia-smi` 应不可用或无设备（worker 内可见 4 张）。  
2) Head 的 Ray 逻辑资源为 `CPU=0, GPU=0`：head 不应承载训练任务调度资源（通过 `ray start --num-cpus=0 --num-gpus=0`）。  
3) 集群节点数量正确：`ray status` 中应看到 1 head + 2 worker。  
4) PPO driver 不在 head：`ray job list` 里该 `submission_id` 的 `driver_info.node_ip_address` 应该是 worker 的 IP（`172.19.0.3/172.19.0.4`），不能是 head（`172.19.0.2`）。  
5) PPO 训练只跑 1 个 epoch：提交参数包含 `trainer.total_epochs=1`。  
6) checkpoint 落盘：`/mnt/shared/jobs/<job_id>/checkpoints/` 有产物（脚本通过 `trainer.default_local_dir` 强制指向该目录；不设置 `trainer.default_hdfs_dir`）。  
7) 数据与缓存落盘：`/home2/argus/infra/mvp/shared/` 下出现 datasets/hf/jobs 等目录。

补充（磁盘保护）：
- checkpoint 不要每步保存（会非常占空间）；当前脚本默认 `trainer.save_freq=10`（每 10 step 保存一次）。

## 10. 目录命名约定（submission id）

- 脚本默认会显式指定 `ray job submit --submission-id=$SUBMISSION_ID`，并使用同一个值作为输出目录名：
  - 输出目录：`/mnt/shared/jobs/$SUBMISSION_ID/`
- 你可以在提交时自定义 ID（推荐这样便于检索）：
  - `SUBMISSION_ID=my_run_20251219_001 ./src/mvp/v1/scripts/40_submit_ppo_epoch1.sh`

## 7. 风险点与兜底

- 如果 `runtime: nvidia` 在该环境不生效：需要改成 compose 的 `gpus:` 写法（我会按远程 docker 版本调整）。
- 如果 Ray Jobs 的 driver 必须在 head 启动（Ray 机制如此）：这不影响“训练任务不调度到 head”，但 head 仍会有一个 job driver 进程。
- 如果 `verl` 在镜像内已安装但版本不匹配：脚本会优先 `pip install -e /workspace/verl` 以保证行为一致。

## 8. 你需要确认的 3 个问题（你已确认，我按此执行）

1) `verl/`：脚本会在远程自动 `git clone https://github.com/volcengine/verl.git`（如你希望固定版本，可改成同步或 checkout tag/commit）。  
2) GPU：`0-7` 可用（worker0 用 `0-3`，worker1 用 `4-7`）。  
3) PPO：用满 8 GPU（`nnodes=2, n_gpus_per_node=4`）。

## 9. 你新增的关键要求（我已纳入脚本）

- 数据与模型必须落在 `/mnt/shared`（由宿主机 `./shared` bind mount 提供），并且具备**幂等**：
  - 数据：如果 `train.parquet/test.parquet` 已存在则跳过下载。
  - 模型：优先检测本地 cache（`HF_HOME=/mnt/shared/hf`）；存在则跳过，否则才下载。
- 提交 job 时显式注入 `HF_HOME/HUGGINGFACE_HUB_CACHE/TRANSFORMERS_CACHE`，确保训练使用持久化缓存与数据路径。
