# MVP V1(Ray + VERL PPO)实验脚本 本目录用于在“宿主机 + Docker 容器”环境下,**用宿主机脚本(`docker exec`)**协调启动 Ray 集群,并通过 **`ray job submit`(在 head 提交)**跑通一次 `verl` 的 PPO 训练闭环(`total_epochs=1`),且数据/模型/日志/ckpt 都持久化到宿主机目录。 ## 1. 运行环境与拓扑 ### 1.1 依赖 - 宿主机:Linux - 必需工具:`docker`、`docker compose`(Compose v2 插件)、`git` - GPU:至少 8 张可用 GPU(索引 `0-7`),Docker 的 NVIDIA runtime 可用 ### 1.2 集群拓扑(3 个容器) - `mvp-ray-head`(Ray Head) - **不挂 GPU**(容器内 `nvidia-smi` 不可用) - `ray start --head --num-cpus=0 --num-gpus=0`:head 只做控制面,不参与计算调度 - 暴露 dashboard:宿主机端口 `8265` - `mvp-ray-worker-0`(Ray Worker) - 4 GPU:`0,1,2,3` - `ray start ... --resources='{"worker_node": 100}'` - `mvp-ray-worker-1`(Ray Worker) - 4 GPU:`4,5,6,7` - `ray start ... --resources='{"worker_node": 100}'` **关键点:driver 不在 head** - 作业通过 head 提交:`ray job submit ...` - 通过 `--entrypoint-resources='{"worker_node": 1}'` 强制 entrypoint/driver 只能调度到 worker(head 没有该资源) ## 2. 持久化目录(宿主机 <-> 容器) 在宿主机项目根目录(运行脚本时的 `${PWD}`)下使用 `./shared` 做持久化根目录,并 bind mount 到容器内 `/mnt/shared`: - 宿主机:`./shared` - 容器:`/mnt/shared` 主要内容: - 数据集:`/mnt/shared/datasets/gsm8k/` - HF 缓存:`/mnt/shared/hf/`(脚本会设置 `HF_HOME`,并尽量幂等跳过重复下载) - 每个 Ray Job 的输出(按 submission id 分目录): - `/mnt/shared/jobs//logs/` - `/mnt/shared/jobs//checkpoints/` ## 3. 整体流程(代码逻辑) 脚本都在 `src/mvp/v1/scripts/`,整体顺序如下: 1) `00_prereq_check.sh` - 检查 `docker/docker compose/git` 2) `05_ensure_verl_repo.sh` - 若项目根目录下没有 `./verl`,自动 `git clone https://github.com/volcengine/verl.git` 3) `01_up.sh` - 创建持久化目录(`./shared/...`) - `docker compose up -d` 启动 3 个容器 4) `10_install_verl_editable.sh` - 在 3 个容器内执行 `pip install -e /workspace/verl`(确保 `python -m verl...` 可用且代码与 `./verl` 同步) 5) `20_start_head.sh` - 在 `mvp-ray-head` 内启动 Ray head(CPU=0、GPU=0) 6) `21_start_workers.sh` - 在两个 worker 内启动 Ray worker 加入集群 - 同时给 worker 打 `worker_node` 自定义资源标签 7) `30_prepare_data_and_model.sh` - 数据集:若 `train.parquet/test.parquet` 已存在则跳过,否则生成 - 模型:使用 HF cache(`HF_HOME=/mnt/shared/hf`),存在则跳过,不存在才下载 8) `40_submit_ppo_epoch1.sh` - 在 head 容器里执行 `ray job submit` - 显式指定 `--submission-id=$SUBMISSION_ID` - 通过 `--entrypoint-resources='{"worker_node": 1}'` 强制 driver 在 worker - 训练参数: - `trainer.total_epochs=1` - `trainer.total_training_steps=29`(GSM8K 该配置下对应 29 steps) - `trainer.save_freq=10`(每 10 step 保存一次 checkpoint,避免磁盘爆炸) - `trainer.default_local_dir=/mnt/shared/jobs/$SUBMISSION_ID/checkpoints` - `hydra.run.dir=/mnt/shared/jobs/$SUBMISSION_ID/logs/hydra` 9) `50_status.sh` - 打印 `ray status` / `ray job list` / `ray job status` / `ray job logs | tail` ## 4. 运行方法 ### 4.1 一键执行 在项目根目录执行: - `./src/mvp/v1/scripts/run_all.sh` ### 4.2 分步执行(推荐) 按顺序执行: - `./src/mvp/v1/scripts/01_up.sh` - `./src/mvp/v1/scripts/10_install_verl_editable.sh` - `./src/mvp/v1/scripts/20_start_head.sh` - `./src/mvp/v1/scripts/21_start_workers.sh` - `./src/mvp/v1/scripts/30_prepare_data_and_model.sh` - `SUBMISSION_ID=ppo_h20_8g_$(date +%Y%m%d_%H%M%S) ./src/mvp/v1/scripts/40_submit_ppo_epoch1.sh` - `./src/mvp/v1/scripts/50_status.sh` ### 4.3 查看与停止 - Dashboard:`http://<宿主机IP>:8265` - 列出作业(在 head 容器内): - `docker exec mvp-ray-head bash -lc "ray job list --address=http://127.0.0.1:8265"` - 停止某个 submission id: - `docker exec mvp-ray-head bash -lc "ray job stop --address=http://127.0.0.1:8265 "` ### 4.4 清理 - 停止并删除容器:`./src/mvp/v1/scripts/02_down.sh` - 清理输出(谨慎,数据量可能很大):删除 `./shared/jobs//` ## 5. 常见坑 - **不传 `--submission-id` 会导致“输出目录难以等于 submission id”**:因为 hydra/ckpt 目录需要在提交前确定。当前脚本会显式传 `--submission-id=$SUBMISSION_ID`,并使用同名目录。 - **checkpoint 太大**:PPO 的 checkpoint 非常占空间。当前脚本默认 `save_freq=10`,如仍过大,可调大 `save_freq` 或减少保存内容/频率。 更多分步操作与验收标准见:`specs/mvp/v1_action.md`