跳到内容

vLLM Wheel 每日构建

vLLM 在 https://wheels.vllm.ai 维护一个按提交记录生成的 Wheel 仓库(通常称为“每日构建”或 "nightly"),自 v0.5.3 版本以来,为 main 分支上的每一次提交提供预构建的 Wheel。本文档解释了每日构建 Wheel 索引机制的工作原理。

CI 中的构建与上传流程

Wheel 构建

在 PR 合并到 main 分支后,Wheel 会在 Release 流水线(.buildkite/release-pipeline.yaml)中构建,包含多种变体:

  • 后端变体cpucuXXX(例如 cu129, cu130)。
  • 架构变体x86_64aarch64

每个构建步骤:

  1. 在 Docker 容器中构建 Wheel。
  2. 重命名 Wheel 文件以使用正确的 manylinux 标签(目前为 manylinux_2_31),以符合 PEP 600 标准。
  3. 将 Wheel 上传至 vllm-wheels S3 存储桶的 /{commit_hash}/ 目录下。

索引生成

上传每个 Wheel 后,.buildkite/scripts/upload-wheels.sh 脚本会执行以下操作:

  1. 列出 S3 提交目录中所有现有的 Wheel
  2. 使用 .buildkite/scripts/generate-nightly-index.py 生成索引
    • 解析 Wheel 文件名以提取元数据(版本、变体、平台标签)。
    • 为 PyPI 兼容性创建 HTML 索引文件(index.html)。
    • 生成机器可读的 metadata.json 文件。
  3. 将索引上传到多个位置(覆盖现有的索引):
    • /{commit_hash}/ - 始终上传,用于特定提交的访问。
    • /nightly/ - 仅用于 main 分支的提交(非 PR)。
    • /{version}/ - 仅用于发布版的 Wheel(版本中不含 dev)。

处理并发构建

索引生成脚本可以通过在生成索引前始终列出提交目录中的所有 Wheel 来处理多个变体的并发构建,从而避免竞态条件。

目录结构

S3 存储桶结构遵循以下模式:

s3://vllm-wheels/
├── {commit_hash}/              # Commit-specific wheels and indices
│   ├── vllm-*.whl              # All wheel files
│   ├── index.html              # Project list (default variant)
│   ├── vllm/
│   │   ├── index.html          # Package index (default variant)
│   │   └── metadata.json       # Metadata (default variant)
│   ├── cu129/                  # Variant subdirectory
│   │   ├── index.html          # Project list (cu129 variant)
│   │   └── vllm/
│   │       ├── index.html      # Package index (cu129 variant)
│   │       └── metadata.json   # Metadata (cu129 variant)
│   ├── cu130/                  # Variant subdirectory
│   ├── cpu/                    # Variant subdirectory
│   └── .../                    # More variant subdirectories
├── nightly/                    # Latest main branch wheels (mirror of latest commit)
└── {version}/                  # Release version indices (e.g., 0.11.2)

所有已构建的 Wheel 都存储在 /{commit_hash}/ 中,而不同的索引则引用它们。这避免了 Wheel 文件的重复。

例如,您可以指定以下 URL 来使用不同的索引:

  • https://wheels.vllm.ai/nightly/cu130 用于获取使用 CUDA 13.0 构建的最新 main 分支 Wheel。
  • https://wheels.vllm.ai/{commit_hash} 用于获取特定提交构建的 Wheel(默认变体)。
  • https://wheels.vllm.ai/0.12.0/cpu 用于获取 0.12.0 发布版 CPU 变体的 Wheel。

请注意,并非每次提交都包含所有变体。可用变体可能会随时间变化,例如将 cu130 更改为 cu131。

变体(Variant)组织

索引按变体组织:

  • 默认变体:没有变体后缀的 Wheel(即使用当前的 VLLM_MAIN_CUDA_VERSION 构建)放置在根目录下。
  • 变体子目录:带有变体后缀(例如 +cu130, .cpu)的 Wheel 组织在子目录中。
  • 默认别名:出于一致性和便利性,默认变体可以有一个别名(例如目前的 cu129)。

变体是从 Wheel 文件名中提取的(详见 文件名约定)。

  • 变体被编码在本地版本标识符中(例如 +cu129dev<N>+g<hash>.cu130)。
  • 示例
    • vllm-0.11.2.dev278+gdbc3d9991-cp38-abi3-manylinux1_x86_64.whl → 默认变体
    • vllm-0.10.2rc2+cu129-cp38-abi3-manylinux2014_aarch64.whlcu129 变体
    • vllm-0.11.1rc8.dev14+gaa384b3c0.cu130-cp38-abi3-manylinux1_x86_64.whlcu130 变体

索引生成详情

generate-nightly-index.py 脚本执行以下操作:

  1. 使用正则表达式解析 Wheel 文件名以提取
    • 包名称
    • 版本(包含提取出的变体)
    • Python 标签、ABI 标签、平台标签
    • 构建标签(如果存在)
  2. 按变体分组 Wheel,然后按包名称分组
    • 目前仅构建 vllm,但该结构支持未来包含多个包。
  3. 生成 HTML 索引(符合 Simple repository API):
    • 顶层 index.html:列出所有包和变体子目录。
    • 包层级 index.html:列出该包的所有 Wheel 文件。
    • 使用 Wheel 文件的相对路径以实现可移植性。
  4. 生成 metadata.json::
    • 包含所有 Wheel 元数据的机器可读 JSON。
    • 包含 path 字段,即指向 Wheel 文件的 URL 编码后的相对路径。
    • setup.py 在仅 Python 环境的构建过程中使用,用于定位兼容的预编译 Wheel。

AWS 服务的特殊处理

Wheel 和索引直接存储在 AWS S3 上,我们使用 AWS CloudFront 作为 S3 存储桶的前端 CDN。

由于 S3 不提供适当的目录列表功能,为了支持兼容 PyPI 的简单仓库 API 行为,我们部署了一个 CloudFront 函数,该函数执行以下操作:

  • 将任何不以 / 结尾且看起来不像文件(即路径最后一段不包含点 .)的 URL 重定向到带有尾随 / 的相同 URL。
  • /index.html 添加到任何以 / 结尾的 URL。

例如,以下请求将被处理为:

  • /nightly -> /nightly/index.html
  • /nightly/cu130/ -> /nightly/cu130/index.html
  • /nightly/index.html/nightly/vllm.whl -> 保持不变

AWS S3 文件名转义

S3 会根据其 命名规则 在上传时自动转义文件名。对 vllm 的直接影响是文件名中的 + 会被转换为 %2B。我们在索引生成脚本中特别注意在生成 HTML 索引和 JSON 元数据时正确转义文件名,以确保 URL 正确并可直接使用。

setup.py 中预编译 Wheel 的使用

当使用 VLLM_USE_PRECOMPILED=1 安装 vLLM 时,setup.py 脚本会:

  1. 通过 precompiled_wheel_utils.determine_wheel_url() 确定 Wheel 位置
    • 环境变量 VLLM_PRECOMPILED_WHEEL_LOCATION(用户指定的 URL/路径)总是优先,并跳过所有其他步骤。
    • 根据 VLLM_MAIN_CUDA_VERSION 确定变体(可通过环境变量 VLLM_PRECOMPILED_WHEEL_VARIANT 覆盖);默认变体也将作为后备选项进行尝试。
    • 确定该分支的基准提交(稍后解释)(可通过环境变量 VLLM_PRECOMPILED_WHEEL_COMMIT 覆盖)。
  2. https://wheels.vllm.ai/{commit}/vllm/metadata.json(默认变体)或 https://wheels.vllm.ai/{commit}/{variant}/vllm/metadata.json(特定变体)获取元数据
  3. 根据以下内容选择兼容的 Wheel
    • 包名称 (vllm)
    • 平台标签(架构匹配)
  4. 下载并解压预编译的二进制文件
    • C++ 扩展模块 (.so 文件)
    • Flash Attention Python 模块
    • Triton 内核 Python 文件
  5. 修补 package_data 以将解压后的文件包含在安装中

什么是基准提交(base commit)?

基准提交是通过查找当前分支与上游 main 之间的合并基点(merge-base)来确定的,从而确保源代码与预编译二进制文件之间的兼容性。

注意:用户有责任确保在使用预编译 Wheel 之前没有对本地代码(如 C++ 或 CUDA)进行更改。

实现文件

每日构建 Wheel 机制涉及的关键文件:

  • .buildkite/release-pipeline.yaml:构建 Wheel 的 CI 流水线。
  • .buildkite/scripts/upload-wheels.sh:上传 Wheel 并生成索引的脚本。
  • .buildkite/scripts/generate-nightly-index.py:生成符合 PyPI 索引的 Python 脚本。
  • setup.py:包含用于获取和使用预编译 Wheel 的 precompiled_wheel_utils 类。