Prhub

#38404 [Deps] Retire the CUDA 12 lane

原始 PR 作者 mmangkad 合并时间 2026-09-11 07:58 文件变更 38 提交数 4 评论 1 代码增减 +171 / -738

执行摘要

退役 CUDA 12 构建发布通道,为 PyTorch 2.14 铺路

PR body 给出的动机非常直接:「Drops the CUDA 12.9 wheels and images, per the advance notice — prerequisite for the PyTorch 2.14 upgrade. Already-published -cu129 / -cu12 image tags are unaffected. v0.5.19 is the last release with a CUDA 12 lane.」即:其一,PyTorch 2.14 不再为退役 cu12 通道使用的 cu129 索引发布 CUDA 12 wheel(ci_install_dependency.sh 的注释原文:"PyTorch 2.14 publishes no CUDA 12 wheels for the cu129 index the retired cu12 lane used"),继续保留 cu12 通道会直接卡住升级;其二,同时维护 cu129/cu130 两套镜像、wheel 与 CI 分支的边际成本已高于收益;其三,该动作已按内部 Slack 公告(body 中的 advance notice 链接,非公开可访问)提前通知用户。关联 Issue 为空,说明这是一次由维护团队主动发起的通道退役,而非外部缺陷驱动。

值得精读,尤其对负责发布流水线、镜像与依赖管理的工程师:这是一次「删除为主」的 PR,真正的决策密度集中在少数几处。建议重点关注三点:一是 scripts/update_kernel_whl_index.py 把 CUDA 版本白名单替换为 +cuNNN 正则匹配——这是 fail-closed 的稳健性提升,杜绝了未来新 CUDA 版本的 wheel 误入 cu130 索引,值得作为同类脚本的范式;二是 _docker-build-and-publish.yml 中 manifest 固定使用 cu130 digest 后 tag_config.cuda 字段被静默忽略,建议合并后排查所有调用方并显式拒绝(或警告)非 cu130 的 cuda 取值;三是给 check_wheel_cuda_version 补一组针对 +cu129 / +cu130 / 无标记 / +rocm720 的单测。对 CUDA 12 用户,应在 release notes 中明确「v0.5.19 为最后一个 CUDA 12 版本」以及升级到 CUDA 13 的路径,避免他们从新的报错提示里得不到有效指引。

讨论亮点

本 PR 的公开讨论非常稀薄,唯一的 review 是 Fridge003 的 APPROVED 空评论,review_comments 为 0,没有任何行内技术交锋。因此需要从 PR body 的状态槽位里读取「未被讨论但客观存在」的信号:

PR body 的 pr-states 区块显示 Latest PR Test (Base)、Latest PR Test (Extra)、Latest PR Test (AMD ROCm 10) 三个 run 全部为 ❌(Run #34440714447 / #34440714159 / #34440714312)。

兼容承诺:「Already-published -cu129 / -cu12 image tags are unaffected. v0.5.19 is the last release with a CUDA 12 lane.」——这是对 CUDA 12 用户的唯一书面保证,且没有在 review 中被进一步确认或质疑。

代码层面的设计取舍(白名单 → 正则标签匹配)由作者单方面做出,未经过讨论;_docker-build-and-publish.yml 中 tag_config 传入的 cuda 字段被静默忽略、manifest 固定使用 cu130 digest 这一行为变化,也没有在任何评论中被提出。

实现拆解

  1. 移除 CUDA 12 的发布矩阵(workflows 层):release-whl-kernel.yml 删掉 build-cu129-matrix 与 release-cu129 两个 job(-105 行)并从 target 选项中去掉 cu129;release-whl-deepep.yml 删除 build-cu129-matrix、删除整个 target 输入,并把 release-whl / release-pypi 的 needs 与 if 条件从「按 target 分支判断两个矩阵」简化为只依赖 build-cu130-matrix.result == 'success';release-whl-deepgemm.yml 同样删除 eu129 矩阵与 release-cu129。结果是新增 wheel 只产出 CUDA 13 一条通道。

  2. 镜像构建与发布收敛:_docker-build-and-publish.yml 删除 cu129 的 amd64 / arm64 build step、digest-cu129 输出,以及 manifest 阶段 X86_CU129 / ARM64_CU129 的选择分支,tag_config 的 schema 描述从 cu129|cu130 收窄为 cu130;release-docker-dev.yml 同步调整调用参数。

  3. Dockerfile 中的 CUDA 12 兼容 hack 全部删除:docker/Dockerfile 与 docker/Dockerfile.cu134 的 CUINDEX case 只保留 13.0.3 / 13.4.0;删除 sgl-deep-ep==<ver>+cu129 预装、对 pyproject.toml 的 cuda-python>=13.0 → >=12,<13、flashinfer_python[cu13] → [cu12]、nvidia-cutlass-dsl[cu13] 的 sed 改写、torch 2.13 强制重装以及 sgl_deep_gemm-<ver>+cu129 直链安装;Mooncake、MSCCL++、NCCL、nixl 全部固定为 cuda13 变体,SGL_DEEP_GEMM_VERSION build arg 也随之删除。

  4. CI 依赖脚本收敛为 fail-fast:scripts/ci/cuda/ci_install_dependency.sh 在 configure_environment 里加入 case "${CU_STRIP}" in 13*) ;; *) FATAL ...,非 cu13x 直接退出;删除 install_cuda12_deepep_wheel(CUDA 12 的 DeepEP wheel 只存在于 sglang 轮子索引,需按 local version 预装);remove_stale_cuda12_nvidia_wheels 去掉 CU_MAJOR != 13 的早退分支;install_nccl 无条件执行 nvidia-nccl-cu13==2.30.7 覆盖;install_sglang_kernel 不再拼 +cu129、不再回退到 GitHub Release 直链;install_extra_deps 固定 mooncake-transfer-engine-cuda13 / nixl-cu13;内嵌的 verify_imports Python 片段不再接收 $CU_MAJOR 参数,NCCL 版本与运行时校验变成无条件。

  5. 轮子索引生成逻辑重写(本 PR 最值得看的代码改动):scripts/update_kernel_whl_index.py 删除 SUPPORTED_CUDA_VERSIONS = ["129", "130"],改为 CUDA_LOCAL_VERSION_PATTERN = re.compile(r"\+cu(\d+)"),check_wheel_cuda_version 直接比对 wheel 自身携带的 +cuNNN 标记,未带标记的视为默认 CUDA 构建;update_deepgemm_whl_index.py、update_nightly_whl_index.py、update_deepep_whl_index.py 把 SUPPORTED_CUDA_VERSIONS / help 文本收窄为 130。

  6. 运行时提示、文档与测试配套:python/sglang/kernels/aot/python/sgl_kernel/load_utils.py 删除 torch.version.cuda.startswith("12") 的 cu129 安装提示分支,统一为 pip install --upgrade sglang-kernel;python/sglang/kernels/jit/utils/arch.py 的 docstring 由 cu129 示例改为 cu130;docs/docs/get-started/install.mdx(+6/-17)与 MiniMax-M3、Inkling cookbook snippet 的注释同步(cu12 标注为 final Hopper build,不再重建);test/registered/unit/tools/test_docker_build_metadata_args.py 随镜像元数据脚本调整(+7/-7),但没有任何测试覆盖新的 check_wheel_cuda_version 匹配逻辑。

文件 模块 状态 重要度
scripts/update_kernel_whl_index.py 轮子索引 modified 6.5
scripts/ci/cuda/ci_install_dependency.sh 依赖安装 modified 5.89
python/sglang/kernels/aot/python/sgl_kernel/load_utils.py 内核加载 modified 5.6
.github/workflows/_docker-build-and-publish.yml 镜像构建 modified 5.59
docker/Dockerfile 容器镜像 modified 5.51
docker/Dockerfile.cu134 容器镜像 modified 5.07
.github/workflows/release-whl-kernel.yml 发布流水线 modified 5.36
.github/workflows/release-whl-deepep.yml 发布流水线 modified 5.17
test/registered/unit/tools/test_docker_build_metadata_args.py 元数据校验 modified 4.38
docs/docs/get-started/install.mdx 安装文档 modified 3.63

关键符号

check_wheel_cuda_version CUDA_LOCAL_VERSION_PATTERN update_wheel_index configure_environment install_nccl install_sglang_kernel install_extra_deps verify_imports install_cuda12_deepep_wheel(删除) build-cu129-matrix(删除 job) release-cu129(删除 job) _load_architecture_specific_ops

关键源码片段

scripts/update_kernel_whl_index.py core-logic

本 PR 中信息密度最高的代码改动:CUDA 版本过滤从硬编码白名单改为解析 wheel 自身的 +cuNNN 标记,是 fail-closed 的正确性提升,且决定了 wheel 索引里会落入哪些产物。

# scripts/update_kernel_whl_index.py
​
DEFAULT_CUDA_VERSION = "130"
# CUDA wheel 自带的本地版本标记,例如 sglang_kernel-0.4.6.post1+cu130-...whl
CUDA_LOCAL_VERSION_PATTERN = re.compile(r"\+cu(\d+)")
​
​
def check_wheel_cuda_version(path_name, target_cuda_version):
    # 非 CUDA 后端直接跳过:ROCm/MUSA 把后端编码在 local version 里(如 +rocm720),
    # XPU 则使用独立包名 sglang_kernel_xpu-*。
    if re.search(r"\+(rocm|musa)", path_name) or path_name.startswith(
        "sglang_kernel_xpu-"
    ):
        return False
​
    # 关键改动:不再遍历 SUPPORTED_CUDA_VERSIONS 的已知版本列表,而是读取 wheel
    # 自身的 +cuNNN 标记。这样即便某个 wheel 是用本脚本从未见过的 CUDA 版本构建的,
    # 也会被直接拒绝,而不是错误地落进目标索引(旧逻辑对默认版本采用「反查白名单」)。
    match = CUDA_LOCAL_VERSION_PATTERN.search(path_name)
    if match is not None:
        return match.group(1) == target_cuda_version
​
    # 没有标记的 wheel 即默认 CUDA 版本的构建,例如
    # sglang_kernel-0.4.0-cp310-abi3-manylinux2014_x86_64.whl
    # (PyPI 拒绝 local version,因此上传到 PyPI 时会剥离 +cuNNN 标记)。
    return target_cuda_version == DEFAULT_CUDA_VERSION
​
​
def update_wheel_index(cuda_version=DEFAULT_CUDA_VERSION, rocm_version=None):
    index_dir = pathlib.Path(f"sgl-whl/cu{cuda_version}/sglang-kernel")
    index_dir.mkdir(exist_ok=True, parents=True)
    base_url = "https://github.com/sgl-project/whl/releases/download"
​
    # 调用方从此只需传 cu130;check_wheel_cuda_version 负责把 +cu129 等历史
    # 产物挡在索引之外,避免退役通道的 wheel 继续出现在新索引里。
    for path in sorted(pathlib.Path("python/sglang/kernels/aot/dist").glob("*.whl")):
        if not check_wheel_cuda_version(path.name, cuda_version):
            continue
        with open(path, "rb") as f:
            sha256 = hashlib.sha256(f.read()).hexdigest()
        ver = re.findall(
            r"sglang_kernel-([0-9.]+(?:\.post[0-9]+)?)(?:\+cu[0-9]+)?-", path.name
        )[0]
        full_url = f"{base_url}/v{ver}/{path.name}#sha256={sha256}"
        with (index_dir / "index.html").open("a") as f:
            f.write(f'<a href="{full_url}">{path.name}</a><br>\n')
scripts/ci/cuda/ci_install_dependency.sh infrastructure

删改量最大的基础设施文件(+32/-78):引入 CU_VERSION fail-fast 校验,删除 CUDA 12 专用的 DeepEP wheel 安装函数与全部 cu12/cu13 分支,是 CI 侧「只剩一条 CUDA 通道」的执行点。

# scripts/ci/cuda/ci_install_dependency.sh(退役 CUDA 12 后的形态)
​
configure_environment() {
  # CU_VERSION 决定 PyTorch 索引地址、FlashInfer JIT cache 索引以及 sglang wheel 索引。
  # 现在只剩 CUDA 13 通道:PyTorch 2.14 不再为 cu12 通道所用的 cu129 索引发布
  # CUDA 12 wheel,因此这里用 fail-fast 取代过去的 CU_MAJOR 双分支。
  CU_VERSION="${CU_VERSION:-cu130}"
  CU_STRIP="${CU_VERSION#cu}"
  case "${CU_STRIP}" in
    13*) ;; # 仅放行 cu13x
    *) echo "FATAL: unsupported CU_VERSION=${CU_VERSION}; only CUDA 13 is supported"; exit 1 ;;
  esac
}
​
# install_cuda12_deepep_wheel 已整段删除:该函数原本从 pyproject.toml 抓取 sgl-deep-ep
# 版本,再装上 "sgl-deep-ep==${version}+${CU_VERSION}" 这个只存在于 sglang 轮子索引的
# local version wheel,以便满足 pyproject 的公共版本 pin。CUDA 13 直接用 PyPI wheel。
​
install_nccl() {
  # PyTorch 会锁到 2.29.7,所以本次覆盖必须放在所有解析 Python 依赖的命令
  #(含 lmms-eval)之后执行;原先按 CU_MAJOR 判断的 else 分支已删除。
  $PIP_CMD install "nvidia-nccl-cu13==2.30.7" --force-reinstall --no-deps $PIP_INSTALL_SUFFIX
  mark_step_done "${FUNCNAME[0]}"
}
​
install_sglang_kernel() {
  # 不再拼接 +cu129 本地版本,也不再回退到 GitHub Release 直链下载
  # sgl_deep_gemm-<ver>+cu129-py3-none-manylinux2014_$(uname -m).whl。
  SGL_DEEP_GEMM_VERSION=$(grep -Po -m1 '(?<=sgl-deep-gemm==)[0-9A-Za-z\.\-]+' python/pyproject.toml)
  if installed_wheel_ok sgl-deep-gemm "${SGL_DEEP_GEMM_VERSION}"; then
    echo "sgl-deep-gemm==${SGL_DEEP_GEMM_VERSION} already installed, keeping it"
  else
    $PIP_CMD install "sgl-deep-gemm==${SGL_DEEP_GEMM_VERSION}" --force-reinstall $PIP_INSTALL_SUFFIX
  fi
}
​
install_extra_deps() {
  MOONCAKE_VERSION="0.3.13"
  NIXL_VERSION="1.3.0"
  # 四个变量不再随 CU_MAJOR 切换:CUDA 12 的 mooncake-transfer-engine 与
  # nixl-cu12 / nvidia-cuda-nvrtc-cu12 组合已随通道一起删除。
  MOONCAKE_PKG="mooncake-transfer-engine-cuda13==${MOONCAKE_VERSION}"
  MOONCAKE_STALE_PKG="mooncake-transfer-engine" # 需要强制卸载的旧 CUDA 12 变体
  NIXL_BIN_NAME="nixl-cu13"
  EXTRA_NVIDIA_SPECS="nvidia-cuda-nvrtc"
}

评论区精华

空 body 的 APPROVED 是否覆盖失败中的 PR CI other

唯一 review 是 Fridge003 的 APPROVED,review body 为空,`review_comments` 为 0,没有任何行内意见。而 PR body 内嵌的 CI 状态槽位显示 Latest PR Test (Base)、Latest PR Test (Extra)、Latest PR Test (AMD ROCm 10) 三个 run 均为 ❌(#34440714447 / #34440714159 / #34440714312)。

结论:维护者以「已提前公告 + 通道退役属既定计划」为前提直接放行,CI 失败未在 PR 中被讨论或复跑说明,因此这些红灯究竟是被删除 job 造成的预期噪音还是真实回归,无法从本 PR 记录中判断。 · unresolved

已发布 cu129 / cu12 镜像与 wheel 的兼容承诺 documentation

PR body 明确写「Already-published `-cu129` / `-cu12` image tags are unaffected. v0.5.19 is the last release with a CUDA 12 lane.」;`docs/src/snippets/configs/MiniMaxAI/minimax-m3.jsx` 的注释同步改为 cu12 是 final Hopper/CUDA-12 build、(that lane is retired, so it no longer rebuilds),h200 仍指向 dev-cu12-minimax-m3。

结论:不删除历史产物、只停止重建,旧 tag 保持可拉取,用户短期不会遇到镜像拉取失败;但「最后一个 CUDA 12 版本」的结论只在 PR body 中书面给出,未落到 release notes 或安装文档的显式声明里。 · 已解决

索引匹配策略从版本白名单改为 wheel 标签解析 设计

`scripts/update_kernel_whl_index.py` 删除 SUPPORTED_CUDA_VERSIONS,新增 CUDA_LOCAL_VERSION_PATTERN = re.compile(r"\+cu(\d+)"),注释说明动机是让「用本脚本从未见过的 CUDA 版本构建的 wheel 被拒绝,而不是落进目标索引」。这一取舍由作者独立做出,review 中没有相关讨论。

结论:属于 fail-closed 的正确性提升,同时移除了未来新增 CUDA 版本时需要同步更新的白名单维护负担;代价是行为改变(未知版本 wheel 从可能通过变为必然拒绝)缺乏单测覆盖。 · 已解决

风险与影响

  1. tag_config 的静默语义漂移:_docker-build-and-publish.yml 的 manifest job 删除了 CUDA=$(...jq -r '.cuda') 分支,改为无条件使用 X86_CU130 / ARM64_CU130。若仍有调用方沿用 {"cuda":"cu129","tags":[...]} 的旧配置,不会报错,而是把这些 tag 名打到 CUDA 13 镜像上,形成「标签说 cu12、内容是 cu13」的错配。当前仓库内 release-docker-dev.yml 已同步,但这是 reusable workflow,仓库外或历史调用方存在残留风险。

  2. 新的索引匹配逻辑缺少测试:check_wheel_cuda_version 从「白名单里反查」变成「读取 wheel 自身 +cuNNN」,是 fail-closed 的正向改进,但没有任何单测覆盖(仓库有 test/registered/unit/tools/ 目录,却只改了 docker 元数据测试)。边界情形(无标记 wheel 归入默认 130、+rocm720 / sglang_kernel_xpu- 排除、未知 cu 版本被拒绝)只能靠发布流程事后验证。

  3. 报错提示降级:load_utils.py 删掉了 CUDA 12 专属的 --index-url https://docs.sglang.ai/whl/cu129/ 提示后,处于 CUDA 12 环境的用户加载 common_ops 失败时只会被引导执行 pip install --upgrade sglang-kernel,而该 wheel 在 CUDA 12 驱动上无法工作,用户会陷入「按提示操作仍失败且无下一步线索」的状态。

  4. 发布 job 条件收窄:release-whl-deepep.yml 的 release-whl / release-pypi 从 always() && ... 的显式组合判断改为纯 needs.build-cu130-matrix.result == 'success'。语义上更清晰,但当上游 job 被 skip(例如 if 判定仓库不匹配)时,下游 release job 的触发行为与过去不同,需要确认手动重跑 / 部分发布场景未受影响。

  5. CUDA 12 用户的硬断点:cu129 wheel 与镜像不再重建,v0.5.19 成为最后一个含 CUDA 12 通道的版本;由于 PyTorch 2.14 本身也不再发布 CUDA 12 wheel,这条路径事实上不可逆。风险主要在用户侧感知与支持成本,而非代码正确性。

用户侧:仅支持 CUDA 12 驱动栈的机器(例如停留在 12.x 的 Hopper 集群)无法再获得新的 sglang wheel 与镜像,只能继续使用既有 tar 出的镜像 tag 或 ≤v0.5.19 的版本;docs/docs/get-started/install.mdx 的改动使这一断点在安装文档上可见。已经发布的 -cu129 / -cu12 tag 保持可拉取,短期不构成服务中断。系统与 CI 侧:发布矩阵从两条 CUDA 通道收敛为一条,_docker-build-and-publish.yml 少了两个镜像构建 step(AMD64/ARM64 各一个 cu129),Dockerfile 少了整段 CUDA 12 改写与重装逻辑,构建时间与失败面同时下降;ci_install_dependency.sh 增加 fail-fast 后,任何仍传 CU_VERSION=cu129 的 runner 会立刻以 FATAL 退出而不是带着错误依赖继续跑。团队侧:为 PyTorch 2.14 升级扫清前置障碍,此后 CUDA 相关改动只需维护 13.x 一条路径,长期维护成本下降;代价是发布流程与镜像脚本的大面积删除缺乏自动化验证,回归主要依赖下一次真实 release 演练。

发布链路大范围删除 CUDA 12 用户兼容性断点 PR CI 状态为失败 索引逻辑缺少测试覆盖 tag_config.cuda 字段静默失效 错误提示指引退化

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论