Prhub

#7313 [ray] fix: remove conflicting PYTHONPATH forwarding

原始 PR 作者 yyDing1 合并时间 2026-08-10 14:03 文件变更 1 提交数 1 评论 0 代码增减 +0 / -7

执行摘要

移除 Ray runtime env 中 PYTHONPATH 转发,修复作业提交冲突

PR body 明确指出:When a job submitted through ray job submit --runtime-env already defines PYTHONPATH, verl adds the same key again to ray.init(runtime_env=...). Ray rejects the merge and training fails before initialization. 因此需要撤销 #7101 引入的自动转发,避免重复键冲突导致训练无法启动。

值得快速浏览,不必精读。核心收获是 Ray runtime_env 合并的约束:同名键会在 Job-level 与 programmatic 之间冲突。如果团队经常使用 Ray Job 提交训练,建议在文档中明确 PYTHONPATH 必须通过 runtime-env 提供;若存在 out-of-band ray start 场景,可在未来通过更明确的配置开关替代自动转发。

讨论亮点

本 PR 没有任何 review 评论(comments_count=0、review_comments_count=0),由作者 yyDing1 直接提交、wuxibin89 合并。虽然没有公开讨论,核心设计权衡在于:修复了 Ray Job 合并冲突,但收紧了使用约定——自定义 PYTHONPATH 必须由 Ray Job runtime-env 显式提供;当初 #7101 为了支持 out-of-band ray start 场景而引入的自动转发被完全撤销。

实现拆解

  1. 定位变更入口:verl/trainer/constants_ppo.py 中的 get_ppo_ray_runtime_env(config=None) 是构造 PPO Ray 运行时环境的核心函数,它在调用时复制 PPO_RAY_RUNTIME_ENV["env_vars"],过滤已存在的变量,并强制注入确定性相关变量。
  2. 删除 PYTHONPATH 转发:base 版本在函数末尾检测 os.environ.get("PYTHONPATH"),若非空则写入 runtime_env["env_vars"]["PYTHONPATH"];head 版本删除这段逻辑(含 3 行注释),函数在注入四个确定性变量后直接返回 runtime_env。
  3. 保留既有行为:过滤逻辑、Megatron/MindSpeed 的 CUDA_DEVICE_MAX_CONNECTIONS 设置、以及 PYTHONHASHSEED 等变量的注入均保持不变,未影响其他调用方。
  4. 测试与配套:本次没有新增或修改测试,也没有配置文件或部署脚本变更;PR 仅通过 pre-commit 对 constants_ppo.py 的检查。潜在回归风险在于依赖自动转发的非 Ray Job 场景。
文件 模块 状态 重要度
verl/trainer/constants_ppo.py 训练入口 modified 5.4

关键符号

get_ppo_ray_runtime_env

关键源码片段

verl/trainer/constants_ppo.py core-logic

唯一变更文件,get_ppo_ray_runtime_env() 是 PPO Ray runtime env 的唯一构造入口,删除 PYTHONPATH 转发直接影响所有 Ray 后端 PPO 训练的启动行为。

def get_ppo_ray_runtime_env(config=None):
    """
    返回 PPO 训练使用的 Ray runtime environment。    该函数避免重复注入当前进程已经存在的环境变量,
    并始终在调用时刻(而非 import 时刻)注入关键变量。    Args:
        config: 可选训练配置。当引擎策略为 Megatron 且 GPU 是 Hopper/Ampere 时,
            会额外设置 CUDA_DEVICE_MAX_CONNECTIONS=1。
    """
    # 从 Ray Job 配置中读取 working_dir,避免重复设置
    working_dir = (
        json.loads(os.environ.get(RAY_JOB_CONFIG_JSON_ENV_VAR, "{}"))
        .get("runtime_env", {})
        .get("working_dir", None)
    )
​
    runtime_env = {
        "env_vars": PPO_RAY_RUNTIME_ENV["env_vars"].copy(),
        **({"working_dir": None} if working_dir is None else {}),
    }
​
    # 仅 Megatron 在 Hopper/Ampere 上需要该设置,MindSpeed 也需要
    if _is_hopper_or_ampere and _uses_megatron(config):
        runtime_env["env_vars"]["CUDA_DEVICE_MAX_CONNECTIONS"] = "1"
    if _uses_mindspeed(config):
        runtime_env["env_vars"]["CUDA_DEVICE_MAX_CONNECTIONS"] = "1"
​
    # 当前进程已设置过的变量不再重复注入,避免覆盖已有值
    for key in list(runtime_env["env_vars"].keys()):
        if os.environ.get(key) is not None:
            runtime_env["env_vars"].pop(key, None)
​
    # 这四个变量始终在调用时注入(而不是 import 时)
    for key in ("PYTHONHASHSEED", "VERL_FULL_DETERMINISM", "VLLM_BATCH_INVARIANT", "VERL_RL_INSIGHT_ENABLE"):
        runtime_env["env_vars"][key] = os.environ.get(key, "0")
​
    # 注意:PR #7313 之前这里会把 driver 的 PYTHONPATH 转发给 Ray worker,
    # 但如果任务由 `ray job submit --runtime-env` 提交且已定义 PYTHONPATH,
    # 重复注入同一键会让 Ray 拒绝合并 runtime_env,训练在初始化前失败。
    # 因此该转发已被移除,PYTHONPATH 由 Ray Job runtime env 统一负责传播。
    return runtime_env

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

兼容性风险:直接 ray.init()ray start 集群上,driver 的 PYTHONPATH 不再自动传给 worker,依赖 PYTHONPATH 暴露的非 site-packages 包(如 CI 镜像中的 /workspace/Megatron-LM)可能 ImportError。缺少测试覆盖:无对应单元测试,后续若重新加入转发或调整 runtime_env 结构,无法自动发现冲突。正确性风险:修复依赖用户已在 Ray Job runtime-env 中正确设置 PYTHONPATH,否则行为会从原来的启动崩溃变为静默导入失败。

用户侧:通过 ray job submit --runtime-env 提交且已定义 PYTHONPATH 的用户从启动崩溃变为正常训练;但未正确配置 runtime-env 的用户可能遇到新的导入失败。系统侧:get_ppo_ray_runtime_env 是所有 PPO Ray 训练的公用函数,影响所有 Ray 后端 PPO 配置的启动环节,但对默认(无 PYTHONPATH 需求)训练无影响。团队侧:撤销了 #7101 的兼容性设计,后续涉及 PYTHONPATH 传播的需求需在设计层面统一。

Ray 启动路径变更 缺少测试覆盖 依赖外部 runtime_env 传递 PYTHONPATH

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论