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)中构建,包含多种变体:
- 后端变体:
cpu和cuXXX(例如cu129,cu130)。 - 架构变体:
x86_64和aarch64。
每个构建步骤:
- 在 Docker 容器中构建 Wheel。
- 重命名 Wheel 文件以使用正确的 manylinux 标签(目前为
manylinux_2_31),以符合 PEP 600 标准。 - 将 Wheel 上传至
vllm-wheelsS3 存储桶的/{commit_hash}/目录下。
索引生成¶
上传每个 Wheel 后,.buildkite/scripts/upload-wheels.sh 脚本会执行以下操作:
- 列出 S3 提交目录中所有现有的 Wheel。
- 使用
.buildkite/scripts/generate-nightly-index.py生成索引:- 解析 Wheel 文件名以提取元数据(版本、变体、平台标签)。
- 为 PyPI 兼容性创建 HTML 索引文件(
index.html)。 - 生成机器可读的
metadata.json文件。
- 将索引上传到多个位置(覆盖现有的索引):
/{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 文件名中提取的(详见 文件名约定)。
- 变体被编码在本地版本标识符中(例如
+cu129或dev<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.whl→cu129变体vllm-0.11.1rc8.dev14+gaa384b3c0.cu130-cp38-abi3-manylinux1_x86_64.whl→cu130变体
索引生成详情¶
generate-nightly-index.py 脚本执行以下操作:
- 使用正则表达式解析 Wheel 文件名以提取:
- 包名称
- 版本(包含提取出的变体)
- Python 标签、ABI 标签、平台标签
- 构建标签(如果存在)
- 按变体分组 Wheel,然后按包名称分组。
- 目前仅构建
vllm,但该结构支持未来包含多个包。
- 目前仅构建
- 生成 HTML 索引(符合 Simple repository API):
- 顶层
index.html:列出所有包和变体子目录。 - 包层级
index.html:列出该包的所有 Wheel 文件。 - 使用 Wheel 文件的相对路径以实现可移植性。
- 顶层
- 生成 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 脚本会:
- 通过
precompiled_wheel_utils.determine_wheel_url()确定 Wheel 位置:- 环境变量
VLLM_PRECOMPILED_WHEEL_LOCATION(用户指定的 URL/路径)总是优先,并跳过所有其他步骤。 - 根据
VLLM_MAIN_CUDA_VERSION确定变体(可通过环境变量VLLM_PRECOMPILED_WHEEL_VARIANT覆盖);默认变体也将作为后备选项进行尝试。 - 确定该分支的基准提交(稍后解释)(可通过环境变量
VLLM_PRECOMPILED_WHEEL_COMMIT覆盖)。
- 环境变量
- 从
https://wheels.vllm.ai/{commit}/vllm/metadata.json(默认变体)或https://wheels.vllm.ai/{commit}/{variant}/vllm/metadata.json(特定变体)获取元数据。 - 根据以下内容选择兼容的 Wheel:
- 包名称 (
vllm) - 平台标签(架构匹配)
- 包名称 (
- 下载并解压预编译的二进制文件:
- C++ 扩展模块 (
.so文件) - Flash Attention Python 模块
- Triton 内核 Python 文件
- C++ 扩展模块 (
- 修补
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类。