Prhub

#7627 [misc] feat: uv support aarch64

原始 PR 作者 ETOgaosion 合并时间 2026-08-31 14:13 文件变更 13 提交数 7 评论 0 代码增减 +2278 / -1360

执行摘要

uv 锁与镜像支持 aarch64 双架构,配套流程重写

PR body 只有一句目标:UV support aarch64 with the same image: verlai/verl:uv.cu130.dev1。核心诉求是让同一份 uv.lock、同一个 Docker 镜像在 x86_64 与 aarch64(GH200 / GB200 超级芯片)两类 GPU 主机上都能直接运行,而不是为 aarch64 单独维护一套依赖锁或另一条安装路径。pyproject.toml 的注释进一步说明可行性:torch / torchvision / torchaudio cu130、vllm / sglang 及 wheelhouse 的 apex / flash-attn / transformer-engine 均已有 aarch64 wheel,因此架构可以作为解析维度融入现有通用锁。

值得精读。该 PR 展示了“架构作为解析维度而非 extra 维度”的 uv 多架构设计,以及两个非常具体的坑:nvidia-cusparselt-cu13 的 wheel 内部 tag 不一致导致 uv 反复重装、megatron-core VCS 版本号导致锁文件与实际构建不一致。这些细节对任何维护 uv 单锁多平台的团队都有借鉴价值;Docker 多架构构建与 manifest 合并的实操说明也可直接复用。重点关注 pyproject.toml[tool.uv] 配置注释和 docker/Dockerfile.uv.cu130 的构建说明。

讨论亮点

该 PR 没有任何 review 评论,仅有一条来自 wuxibin89 的空 body APPROVED,因此没有可引用的争议讨论。真正的技术权衡体现在 7 个 commit 的迭代轨迹中:try aarch64try fix frontend buildingtry fix uv raytry fix uv ray with pathfix nv cusparselt,说明 Ray 通过 uv 启动的路径解析与 aarch64 的 cusparselt wheel tag 是主要调试难点;最终方案是让 Ray 的 py_executable 直接指向 uv run,而不是为 Ray 单独维护一套环境。

实现拆解

  1. 扩展锁文件的架构覆盖:在 pyproject.toml[tool.uv].environments 中追加 platform_machine == 'aarch64' marker,使 uv lock 同时解析 x86_64 与 arm64 两个环境的依赖,uv.lock 因此新增约 1600 行 aarch64 解析条目。设计上明确“架构是 resolution 维度,不是 extra 维度”,保持 extra 名称、冲突集合在所有架构上一致,避免拆出 megatron-aarch64 之类的重复 extra。
  2. 修复 aarch64 特有依赖问题override-dependencies 中仅对 aarch64 追加 nvidia-cusparselt-cu13==0.9.1(0.8.0 / 0.8.1 的 wheel 内部 tag 为 manylinux2014_sbsa,uv 认为与文件名不符,每次 uv run 都重装,100+ 并发 Ray worker 会竞争删除 libcusparseLt.so.0 导致 torch import 崩溃);同时在 docker/Dockerfile.uv.cu130 设置 ENV NO_VCS_VERSION=1,避免 megatron-core 的 __version__ 带上 git hash 导致与锁文件 0.18.0 不一致而反复重装。
  3. 环境驱动工具感知双架构manage_envs.py 新增 SUPPORTED_ARCHES = ('x86_64', 'aarch64') 常量,cmd_list 通过 platform.system()platform.machine() 报告当前主机是否被 uv.lock 覆盖,让不被支持的平台在 sync 前就得到明确报错,而不是 uv 内部的 no solution found
  4. Docker 多架构构建支持Dockerfile.uv.cu130 新增 ARG CUDA_BASE_IMAGE 允许替换 base 镜像仓库,并补充基于 buildx 远程 arm64 原生节点、QEMU 模拟、多架构 manifest 合并(imagetools create)的构建说明;docker/README.md 给出完整操作指南与排错细节。
  5. 示例脚本与文档同步:多个 examples/grpo_trainer 脚本(如 run_qwen3_5_2b_openr1_fsdp.shrun_deepseek_v4_flash_megatron.sh)改为 LAUNCH / RAY 数组结构,在 VERL_USE_UV + DEVICE=gpu 分支下用 uv run --frozen --all-packages --extra <engine> --extra <trainer> 启动,并把 Ray 的 runtime_env.py_executable 也指向 uv;docs/start/install.rst 重写为 uv 主线安装文档;tests/special_sanity/check_uv_gpu_only.py 同步更新错误提示与说明。
文件 模块 状态 重要度
pyproject.toml 依赖配置 modified 5.59
manage_envs.py 环境驱动 modified 6.76
docker/Dockerfile.uv.cu130 镜像构建 modified 5.46
uv.lock 锁文件 modified 5.05
docs/start/install.rst 安装文档 modified 4.56
docker/README.md 镜像文档 modified 4.02
examples/grpo_trainer/run_qwen3_5_2b_openr1_fsdp.sh 示例脚本 modified 3.39
tests/special_sanity/check_uv_gpu_only.py 静态检查 modified 3.63

关键符号

cmd_list SUPPORTED_ARCHES _build_parser

关键源码片段

pyproject.toml configuration

核心配置变更:在 [tool.uv].environments 增加 aarch64 marker,按架构 pin nvidia-cusparselt-cu13,并说明‘架构是解析维度而非 extra 维度’的设计原则。

# 一个通用 uv.lock 覆盖所有后端:vllm / sglang / fsdp / megatron / cpu。
# 架构是 resolution 维度,不是 extra 维度:
# 在 [tool.uv].environments 中为 x86_64 与 aarch64(GH200 / GB200)各声明一个 marker,
# uv 按 marker 分叉解析,再按主机平台 tag 选取对应 wheel。
# 因此 extra 名称、冲突集合、manage_envs.py 组合在两种架构上完全一致。
[tool.uv]
environments = [
    "python_full_version >= '3.12' and sys_platform == 'linux' and platform_machine == 'x86_64'",
    "python_full_version >= '3.12' and sys_platform == 'linux' and platform_machine == 'aarch64'",
]override-dependencies = [
    # aarch64 专用 pin:nvidia-cusparselt-cu13 0.8.0 / 0.8.1 的 wheel 内部 tag 为
    # manylinux2014_sbsa,与文件名 aarch64 不一致,uv 会每次 uv run 重装,
    # 100+ 个并发 Ray worker 竞争删除 libcusparseLt.so.0 导致 torch import 崩溃
    # (astral-sh/uv#17711)。0.9.1 修正内部 tag 为 manylinux2014_aarch64,
    # 并把库移至 nvidia/cu13/lib/,torch 2.11 的 _preload_cuda_deps 已会搜索该路径。
    # x86_64 保持 0.8.0 不变,避免扰动已验证的配置。
    "nvidia-cusparselt-cu13==0.8.0 ; platform_machine == 'x86_64' and sys_platform == 'linux'",
    "nvidia-cusparselt-cu13==0.9.1 ; platform_machine == 'aarch64' and sys_platform == 'linux'",
]
manage_envs.py dependency-wiring

uv 环境驱动脚本,新增 SUPPORTED_ARCHES 与 cmd_list 主机架构报告,让不支持平台在 sync 前就能得到明确报错;同时更新模块文档说明双架构语义。

# 支持的 CPU 架构。架构不是 extra 维度:两个架构共享同一组 extra 名与冲突集合,
# uv sync 按主机平台 tag 选 wheel。若主机架构未被 uv.lock 覆盖,
# sync 会在 uv 内部报 no solution found,因此 cmd_list 会提前报告 host 是否受支持。
SUPPORTED_ARCHES: tuple[str, ...] = ("x86_64", "aarch64")
​
​
def cmd_list(args: argparse.Namespace) -> int:
    """列出可用 extra、冲突规则、venv 状态,并报告主机是否被 uv.lock 覆盖。"""
    # 同时检查操作系统与 CPU 架构,让 macOS arm64(报告 arm64 而不是 aarch64)
    # 等不受支持平台在 sync 前得到明确报错。
    host = f"{platform.system()} {platform.machine()}"
    locked = sys.platform == "linux" and platform.machine() in SUPPORTED_ARCHES
    print(
        f"\nhost: {host} "
        + (
            "(covered by uv.lock; the extras above resolve here)"
            if locked
            else f"— NOT covered by uv.lock (Linux {' / '.join(SUPPORTED_ARCHES)} only), so `sync` will fail"
        )
    )
    # 继续打印冲突集合与 venv 状态(省略后续输出逻辑)……

评论区精华

无 review 评论 other

PR 没有 review comments,仅有一条 wuxibin89 的空 body APPROVED。技术取舍只能从 commit 历史与代码注释推断:'try fix uv ray'、'try fix uv ray with path'、'fix nv cusparselt' 反映了 Ray 启动路径与 cusparselt wheel tag 是最主要的迭代难点。

结论:最终方案:Ray py_executable 直接指向 uv run;cusparselt 按架构 pin 0.9.1。无未解决问题。 · 已解决

风险与影响

  1. 锁文件回归风险uv.lock 重生成后,x86_64 的解析条目也可能发生重排(+1603 / -1089 中不全是新增 aarch64 条目),若某些间接依赖版本被 uv 重新选择,可能影响现有 CI 组合,需要全量 CI 验证。
  2. NO_VCS_VERSION 全局生效:该 ENV 设置在 Docker 镜像内对所有构建生效,若未来有代码依赖 megatron-core 的 0.18.0+hash 版本字符串(如调试信息、版本检查),会与预期不一致。
  3. 示例脚本默认走 uvVERL_USE_UV 默认值为 1,无 uv 环境且未显式设置 VERL_USE_UV=0 的用户运行示例脚本会失败;文档虽提供了回退开关,但存在用户落地时的摩擦。
  4. 多架构镜像发布复杂度:文档建议的多架构 manifest 合并依赖 buildx imagetools create,若只推送单架构 tag,下游 FROM verlai/verl:uv.cu130 会拉取错误平台镜像;远程 buildx 节点还要求 SSH 免密与 docker 组权限,属于新增的运维面。
  5. aarch64 侧缺少专项测试:sanity 检查只更新了文案,未新增真实 aarch64 环境下的 CI 用例,wheelhouse 是否已发布 arm64 构建也未在 PR 中给出验证结果(存在不确定性)。

对用户而言,GH200 / GB200(aarch64)用户可以直接复用同一份 uv.lock 与 verlai/verl:uv.cu130.dev1 镜像,不再需要等待单独的 aarch64 镜像;x86_64 用户的使用方式不变,但安装文档主流程从 docker 镜像切换为 uv run,需要适应新命令。对系统而言,Docker 镜像发布需要新增多架构 tag 合并步骤,CI 与示例脚本的启动路径整体迁移到 uv run + Ray py_executable。对团队而言,环境维护收敛到单锁文件 + 双架构解析,后续引入 arch-specific 包时需要遵循 pyproject.toml 中定义的 marker 拆分约定。整体影响范围较大,但属于基础设施与文档层面,不涉及训练核心逻辑。

双架构锁文件解析范围扩大 示例默认 uv 启动影响无 uv 用户 NO_VCS_VERSION 全局生效 多架构镜像发布易错 缺少 aarch64 专项测试

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论