vLLM 每日构建的 Wheels¶
vLLM 在 https://wheels.vllm.ai 维护一个每次提交对应的 wheel 仓库(通常称为 "nightly"),为自 v0.5.3 以来 main 分支上的每次提交提供预构建的 wheels。本文档解释了 nightly wheel 索引机制的工作原理。
CI 上的构建和上传流程¶
Wheel 构建¶
Wheels 在 PR 合并到主分支后,在 Release 流水线(.buildkite/release-pipeline.yaml)中构建,包含多个变体:
- 后端变体:
cpu和cuXXX(例如cu129、cu130)。 - 架构变体:
x86_64和aarch64。
每个构建步骤:
- 在 Docker 容器中构建 wheel。
- 重命名 wheel 文件名,使用正确的 manylinux 标签(当前为
manylinux_2_31)以符合 PEP 600 规范。 - 将 wheel 上传到 S3 存储桶
vllm-wheels下的/{commit_hash}/目录。
索引生成¶
上传每个 wheel 后,.buildkite/scripts/upload-wheels.sh 脚本会:
- 列出提交目录中所有现有的 wheels(从 S3)
- 生成索引,使用
.buildkite/scripts/generate-nightly-index.py:- 解析 wheel 文件名以提取元数据(版本、变体、平台标签)。
- 创建 HTML 索引文件(
index.html)以兼容 PyPI。 - 生成机器可读的
metadata.json文件。
- 上传索引到多个位置(覆盖现有索引):
/{commit_hash}/- 始终上传,用于特定提交的访问。/nightly/- 仅针对main分支上的提交(非 PR)。/{version}/- 仅针对发布版本的 wheels(其版本中不含dev)。
处理并发构建
索引生成脚本通过始终在生成索引前列出提交目录中的所有 wheels,可以处理多个变体并发构建的情况,从而避免竞态条件。
目录结构¶
S3 存储桶结构遵循以下模式:
s3://vllm-wheels/
├── {commit_hash}/ # 特定提交的 wheels 和索引
│ ├── vllm-*.whl # 所有 wheel 文件
│ ├── index.html # 项目列表(默认变体)
│ ├── vllm/
│ │ ├── index.html # 包索引(默认变体)
│ │ └── metadata.json # 元数据(默认变体)
│ ├── cu129/ # 变体子目录
│ │ ├── index.html # 项目列表(cu129 变体)
│ │ └── vllm/
│ │ ├── index.html # 包索引(cu129 变体)
│ │ └── metadata.json # 元数据(cu129 变体)
│ ├── cu130/ # 变体子目录
│ ├── cpu/ # 变体子目录
│ └── .../ # 更多变体子目录
├── nightly/ # 最新的主分支 wheels(最新提交的镜像)
└── {version}/ # 发布版本索引(例如 0.11.2)
所有构建的 wheels 都存储在 /{commit_hash}/ 中,而生成的不同索引会引用它们。这避免了 wheel 文件的重复。
例如,你可以指定以下 URL 来使用不同的索引:
https://wheels.vllm.ai/nightly/cu130用于使用 CUDA 13.0 构建的最新主分支 wheels。https://wheels.vllm.ai/{commit_hash}用于特定提交构建的 wheels(默认变体)。https://wheels.vllm.ai/0.12.0/cpu用于 0.12.0 发布版本的 CPU 变体 wheels。
请注意,并非每次提交都包含所有变体。可用的变体可能会随时间变化,例如从 cu130 变为 cu131。
变体组织¶
索引按变体组织:
- 默认变体:没有变体后缀的 wheels(即使用当前的
VLLM_MAIN_CUDA_VERSION构建)放在根目录。 - 变体子目录:带有变体后缀的 wheels(例如
+cu130、.cpu)组织在子目录中。 - 默认变体的别名:默认变体可以有一个别名(例如
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 标签、平台标签
- 构建标签(如果存在)
- 按变体分组 wheels,然后按包名分组:
- 当前只构建
vllm,但该结构支持未来多个包。
- 当前只构建
- 生成 HTML 索引(符合简单仓库 API):
- 顶级
index.html:列出所有包和变体子目录 - 包级
index.html:列出该包的所有 wheel 文件 - 使用 wheel 文件的相对路径以确保可移植性
- 顶级
- 生成 metadata.json:
- 包含所有 wheel 元数据的机器可读 JSON
- 包括带有 wheel 文件 URL 编码相对路径的
path字段 - 在仅 Python 构建期间,
setup.py使用它来定位兼容的预编译 wheels
AWS 服务的特殊处理¶
Wheels 和索引直接存储在 AWS S3 上,我们使用 AWS CloudFront 作为 S3 存储桶前面的 CDN。
由于 S3 不提供正确的目录列表,为了支持与 PyPI 兼容的简单仓库 API 行为,我们部署了一个 CloudFront 函数,该函数:
- 将任何不以
/结尾且看起来不像文件(即最后一个路径段中不包含点.)的 URL 重定向到带有尾部/的相同 URL - 为任何以
/结尾的 URL 附加/index.html
例如,以下请求将按如下方式处理:
/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 中使用预编译 wheels¶
当使用 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) - 平台标签(架构匹配)
- 包名(
- 从 wheel 下载并提取预编译的二进制文件:
- C++ 扩展模块(
.so文件) - Flash Attention Python 模块
- Triton 内核 Python 文件
- C++ 扩展模块(
- 修补 package_data 以在安装中包含提取的文件
什么是基准提交?
基准提交是通过查找当前分支与上游 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类