ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深度解析 vLLM Nightly Wheel 构建与分发机制:从 CI 流水线、PyPI 兼容索引到预编译安装加速

深度解析 vLLM Nightly Wheel 构建与分发机制:从 CI 流水线、PyPI 兼容索引到预编译安装加速 深度解析 vLLM Nightly Wheel 构建与分发机制从 CI 流水线、PyPI 兼容索引到预编译安装加速【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllmvLLM 在其官方 CIBuildkite中维护了一套面向main分支每次提交的每提交一个 wheel仓库常被称为nightly wheels并通过 CI nightly_builds 文档 记录其完整设计。本文以该文档为主线结合仓库内的构建流水线、上传脚本、索引生成器与setup.py中的消费逻辑逐层拆解wheel 如何在 CI 中构建并上传、索引如何组织、用户在安装时如何按 commit / CUDA 变体挑选并复用预编译产物读完你可以理解这套机制的 URL 语义、S3 目录布局、变体命名规则以及如何用VLLM_USE_PRECOMPILED等环境变量跳过本地编译、直接下载与源码匹配的预编译 wheel。Nightly Wheel 机制概览vLLM 在https://wheels.vllm.ai托管一个 per-commit逐提交wheel 仓库从v0.5.3起main分支上的每一个提交都会通过 Release 流水线产出并发布对应提交的预编译 wheel。与pip install vllm从 PyPI 拉取的正式发布版本不同nightly wheel 面向的是希望提前尝鲜主分支最新功能的用户需要在 CI / 复现环境中锁定某个具体 commit的开发者希望通过Python-only 构建 预编译原生产物来大幅缩短本地安装时间的用户。机制全貌可以分成两半来看前半段是生产侧由 CI 流水线构建、上传 wheel 并生成索引后半段是消费侧由setup.py在VLLM_USE_PRECOMPILED1时读取索引元数据、挑选并解压合适的 wheel。CI 侧wheel 的构建与上传Release 流水线与NIGHTLY1门控Nightly wheel 的构建发生在 release-pipeline.yaml 定义的Release 流水线中。PR 合入main分支后触发该流水线流水线的第一批步骤总是构建常规wheel——默认的CUDA 13.0版本同时覆盖x86_64与aarch64两种架构。流水线在每个构建步骤末尾通过统一命令收尾bash .buildkite/scripts/upload-nightly-wheels.sh关键点在于流水线中还有若干受环境变量NIGHTLY控制的block门控步骤例如key: block-build-additional-wheels的步骤带有if: build.env(NIGHTLY) ! 1条件。这意味着人工触发的手工 Release默认会在这些门控处停下等待操作人员手动Unblock后才会继续构建附加 wheel 变体设置了NIGHTLY1的自动调度构建会跳过人工门控自动继续把附加变体一并构建出来。被这套门控管理起来的附加变体包括后端变体cpu见流水线中Build wheel - x86_64 - CPU/Build wheel - aarch64 - CPU等步骤以及cuXXX如cu129、cu130即不同 CUDA 版本的后端架构变体x86_64与aarch64流水线为两套架构各自排队、独立构建见cpu_queue_release与arm64_cpu_queue_release除此之外流水线还覆盖了 macOSarm64 CPU、XPU 与独立的 ROCm / ROCk wheel 与镜像发布路径。不同后端变体在流水线中通过不同的 Dockerfile 与构建参数区分例如 CUDA 变体使用-f docker/Dockerfile .并传入--build-arg CUDA_VERSION与--build-arg torch_cuda_arch_list而 CPU 变体走-f docker/Dockerfile.cpu .并传入VLLM_CPU_X86true或 aarch64 时的VLLM_BUILD_ACLON。GPU 架构列表也在流水线顶部以环境变量统一声明如CUDA_ARCH_X86、CUDA_ARCH_AARCH64便于集中维护。每个构建步骤做三件事以 upload-nightly-wheels.sh 为观察点每个 wheel 构建步骤实际执行如下三步在 Docker 容器中构建 wheel构建产物统一落盘到artifacts/dist/*.whl脚本要求该目录中恰有一个 wheel 文件否则报错退出重命名为符合 PEP 600 的 manylinux 文件名Linux wheel 通过apply_manylinux_tag来自.buildkite/scripts/lib/manylinux.sh检测并套上合适的 platform tag——当前仓库的 Release 构建目标是manylinux_2_28。macOS wheel 因自带合法 tag会跳过这一重打标步骤脚本通过VLLM_WHEEL_PLATFORM环境变量区分平台上传到 S3wheel 被aws s3 cp到s3://vllm-wheels/{commit_hash}/目录。上传脚本只负责搬运 wheel 本体索引生成由单独的步骤完成脚本注释明确写着 Index generation is handled by a separate step。索引生成步骤一次上传处处引用在 generate-and-upload-nightly-index.sh 中完成索引的生成与分发。它先通过aws s3 ls与aws s3api list-objects-v2列出 commit 目录下所有已存在的 wheel再调用索引生成器见下一节生成索引随后把索引上传到多个位置/{commit_hash}/—— 无条件上传保证 commit 级别 URL 始终可用/nightly/—— 仅在满足UPDATE_NIGHTLY_INDEX1、分支为main且不是 PR 构建BUILDKITE_PULL_REQUESTfalse时覆盖上传从而让/nightly/永远指向最新的 main 提交/{version}/—— 脚本会从 commit 目录下载一个 vLLM wheel解出METADATA中的Version:字段得到版本号只有当版本号中不含dev即正式发布 wheel时才重新以version 索引--version pure_version --wheel-dir commit生成并上传到/{pure_version}/。并发安全提示多个变体很可能并行构建、先后完成上传。索引生成前总会重新列出当前 commit 目录里的全部 wheel因此后到者重跑生成时会把先到者的 wheel 一并纳入避免相互覆盖丢文件、产生索引与 wheel 不一致的竞态。索引生成器让 S3 表现成一个 PyPI Simple Repository从文件名反解元数据generate-nightly-index.py 是整个索引体系的计算核心。它先用正则解析每个 wheel 文件名抽取包名、版本号、Python tag、ABI tag、platform tag 与可选的 build tagwheel 文件名规范为{package_name}-{version}(-{build_tag})?-{python_tag}-{abi_tag}-{platform_tag}.whl再从版本号中剥离出变体variant若版本中含有dev则取dev之后、最后一个.后面的片段作为变体例如vllm-0.11.1rc8.dev14gaa384b3c0.cu130-...中剥离出.cu130否则若版本中含只有后缀以rocm、cu、cpu、xpu开头时才会被识别为变体如cu129其余后缀如 git 哈希不会被误判为变体。变体分组与 PEP 503 规范化解析后所有 wheel 先按变体分组无变体的归入default再在每个变体内按包名分组。当前仓库只会产出vllm包但数据结构天然支持未来多包共存代码里包名会按 PEP 503 做规范化-_.连续符统一转成单个-并转小写。对于 ROCm 构建脚本还会把从vllmwheel 上检测到的rocmXXX变体继承给同目录下没有变体后缀的其他 wheel保证整组索引自洽。输出两种文件对每个 (变体, 包) 组合生成器输出两类文件index.html兼容 PyPI Simple repository API 的 HTML 页面顶层index.html罗列所有包与变体子目录包级index.html用相对路径相对链接保证索引可以被搬到/commit/、/nightly/、/version/任意位置复用指向真正的 wheel 文件metadata.json机器可读的 wheel 元数据 JSON 数组每个元素包含解析出的全部 tag 字段外加一个path字段URL 编码后的相对路径供setup.py定位兼容的预编译 wheel详见后文。生成的 HTML 头部还会写入pypi:repository-version声明与生成时间 / 注释信息。S3 目录结构与 URL 语义所有 wheel 本体只存放在/{commit_hash}/下多个索引位置都通过相对引用指向它们从而避免 wheel 文件被重复存储。整体结构如下s3://vllm-wheels/ ├── {commit_hash}/ # Commit 专属的 wheel 与索引 │ ├── vllm-*.whl # 所有 wheel 文件 │ ├── index.html # 项目列表默认变体 │ ├── vllm/ │ │ ├── index.html # 包索引默认变体 │ │ └── metadata.json # 元数据默认变体 │ ├── cu129/ # 变体子目录 │ │ ├── index.html # 项目列表cu129 变体 │ │ └── vllm/ │ │ ├── index.html # 包索引cu129 变体 │ │ └── metadata.json # 元数据cu129 变体 │ ├── cu130/ # 变体子目录 │ ├── cpu/ # 变体子目录 │ └── .../ # 更多变体子目录 ├── nightly/ # main 分支最新提交的镜像最新 commit 的引用 └── {version}/ # Release 版本索引如 0.11.2因此作为使用者你可以用不同的 URL 前缀表达不同的语义。例如实际可用变体以仓库实时内容为准https://wheels.vllm.ai/nightly/cu130用 CUDA 13.0 构建的 main 分支最新 wheelhttps://wheels.vllm.ai/{commit_hash}某个具体提交的 wheel默认变体https://wheels.vllm.ai/0.12.0/cpu某个发布版本的 CPU 变体 wheel。也就是说把该前缀当作pip的--extra-index-url或--index-url即可完成安装。注意并非每个 commit 都拥有全部变体仓库也提示变体集合会随时间演进例如默认 CUDA 版本从 cu130 演进到 cu131 后旧的 cuXXX 目录会逐渐不再更新。此外ROCm 与 XPU 等路径使用了各自的rocm/、xpu/前缀脚本中可见rocm/{commit}、rocm/nightly/、rocm/{version}的映射关系。变体Variant组织规则索引按变体组织遵循三条约定默认变体不带任何变体后缀的 wheel即按当前主推 CUDA 版本构建放在index_base_dirroot层级属于vllm的索引直接出现在vllm/下变体子目录带变体后缀如cu130、.cpu的 wheel 被组织进以变体命名的子目录如cu130/、cpu/默认变体的别名alias为了历史一致性与使用便利默认变体还可拥有一个别名子目录。当前仓库的默认变体别名是cu130——该值在 generate-and-upload-nightly-index.sh 中以DEFAULT_VARIANT_ALIAScu130声明其注释标明要与vllm/envs.py中的VLLM_MAIN_CUDA_VERSION对齐。生成器会以--alias-to-default参数接收该别名把无后缀默认变体的索引复制一份到别名目录若别名与既有变体同名则直接报错避免歧义。从命名可直观判断 wheel 归属哪个变体变体编码在 wheel 版本号的 local version 段里vllm-0.11.2.dev278gdbc3d9991-cp38-abi3-manylinux1_x86_64.whl→ 默认变体无变体后缀vllm-0.10.2rc2cu129-cp38-abi3-manylinux2014_aarch64.whl→cu129变体vllm-0.11.1rc8.dev14gaa384b3c0.cu130-cp38-abi3-manylinux1_x86_64.whl→cu130变体。CloudFront 与 AWS 存储的特殊处理wheel 与索引直接存放在 AWS S3 桶vllm-wheels中前面再架一层 AWS CloudFront 作为 CDN。S3 本身并不提供目录列表能力为了让访问行为贴合 PyPI Simple repository APIvLLM 部署了一个CloudFront Function做两层 URL 重写任何不以/结尾、且末段不像文件末段路径中不含.的 URL重定向到带/的同一 URL任何以/结尾的 URL追加/index.html。例如/nightly→/nightly/index.html/nightly/cu130/→/nightly/cu130/index.html/nightly/index.html或/nightly/vllm.whl→ 原样不动另一个 AWS 行为是对象键转义S3 上传时会对文件名按命名规则自动转义直接后果是文件名中的会被转成%2B。因此索引生成脚本在生成 HTML 链接与 JSONpath时会使用 Python 的urllib.parse.quote(..., safe:%/)做严格一致的转义既保留/与已编码的%2B不被二次编码又保证被正确编码最终 URL 可直接用于下载。安装侧setup.py中的预编译 wheel 使用nightly 索引不只是给pip install用的也是 vLLMPython-only 构建免去本地 C/CUDA 编译的物料来源。当用户设置VLLM_USE_PRECOMPILED1安装 vLLM 时setup.py 中的precompiled_wheel_utils工具类类定义位于setup.py约 L505 起会依次执行下面几步。1. 确定 wheel 位置determine_wheel_url()对应实现见setup.py的determine_wheel_url()约 L888 起其优先级逻辑如下用户显式指定优先若设置了环境变量VLLM_PRECOMPILED_WHEEL_LOCATION可以是本地路径或远程 URL则直接使用它并跳过后续全部自动探测步骤ROCm 系统走determine_wheel_url_rocm()按环境匹配的 ROCm 变体解析 wheelCUDA 变体选择若显式设置VLLM_PRECOMPILED_WHEEL_VARIANT则直接采用否则若显式设置了VLLM_MAIN_CUDA_VERSION定义于vllm/envs.py就以它选变体都未设置时从当前 PyTorch /nvidia-smi探测 CUDA 版本探测失败时退回默认的VLLM_MAIN_CUDA_VERSION确定 base commit默认取当前分支与上游main的 merge-base对应的提交详见下文base commit说明可用VLLM_PRECOMPILED_WHEEL_COMMIT覆盖必须是 40 位 commit 哈希否则报错并回退到自动计算。2. 拉取元数据并挑选兼容 wheel确定 commit 与变体后setup.py从https://wheels.vllm.ai/{commit}/{variant}/vllm/metadata.json拉取元数据再按如下条件筛选 wheel包名为vllmplatform_tag与本地架构platform.machine()返回的x86_64/aarch64等匹配。metadata.json中每个条目形如{ package_name: vllm, version: 0.11.2.dev278gdbc3d9991, build_tag: null, python_tag: cp38, abi_tag: abi3, platform_tag: manylinux1_x86_64, variant: null, filename: vllm-0.11.2.dev278gdbc3d9991-cp38-abi3-manylinux1_x86_64.whl, path: ../vllm-0.11.2.dev278%2Bgdbc3d9991-cp38-abi3-manylinux1_x86_64.whl }其中path会被urljoin到仓库根 URL 上得到完整下载地址setup.py 中的注释还专门提醒根/默认变体不会被当作回退因为其 CUDA 兼容性无法验证。3. 下载并解压预编译产物拿到 URL 后extract_precompiled_and_patch_package()下载 wheel 并用 zipfile 精确抽取以下内容均可从setup.py中看到白名单原生扩展模块vllm/_C.abi3.so等一批.abi3.so含 ROCm 的_rocm_C.abi3.soRust 前端二进制vllm/vllm-rs另有针对 Rust 扩展成员的正则白名单Flash Attention Python 模块与Triton / FlashMLA / DeepGEMM / FlashKDA / FMHA-SM100 等第三方内核的 Python 文件其中__init__.py、flash_attn_interface.py等受源码管理的文件会被跳过、防止覆盖本地源文件。4. 修补package_data并收尾抽取完成后工具类把解出的文件纳入package_data确保它们随 Python-only 安装一起落地整个安装便不再需要在本机编译任何原生代码。base commit 是什么为兼容源码与预编译二进制setup.py通过执行git merge-base upstream-main 当前分支求得当前分支相对上游main的分叉点提交代码见setup.py约 L1150。只有在该 merge-base 上有预编译 wheel且本分支相对该提交没有改动原生代码C/CUDA/Rust 等时预编译产物才能与本地 Python 源码安全混装。文档原话提醒确保在改用预编译 wheel 之前没有引入原生代码变更是用户自己的责任。涉及的关键实现文件角色文件作用CI 流水线.buildkite/release-pipeline.yaml定义 Release 流水线构建 CUDA/CPU/XPU/ROCm/macOS 多变体 wheel、镜像并按NIGHTLY门控附加构建wheel 上传脚本.buildkite/scripts/upload-nightly-wheels.sh定位artifacts/dist/*.whl、套用 manylinux tag、上传到s3://vllm-wheels/{commit}/索引生成 上传脚本.buildkite/scripts/generate-and-upload-nightly-index.sh汇总 commit 目录全部 wheel生成索引并分发到/{commit}/、/nightly/、/{version}/索引计算核心.buildkite/scripts/generate-nightly-index.py解析文件名、按变体/包分组、产出 PyPI 兼容 HTML 与metadata.json含 URL 编码处理消费侧逻辑setup.pyprecompiled_wheel_utils约 L505 起VLLM_USE_PRECOMPILED1时探测变体/commit、拉取元数据、下载解压预编译产物并修补package_data默认 CUDA 版本定义vllm/envs.pyVLLM_MAIN_CUDA_VERSION决定默认变体与别名cu130的对齐基准nightly 镜像发布补充.buildkite/scripts/push-nightly-builds.sh、push-nightly-builds-rocm.sh、cleanup-nightly-builds.sh将 nightly 多架构镜像推送到 DockerHub并清理旧 nightly只保留最近 14 个小结整套机制的精妙之处在于wheel 存一处、索引处处引用S3 桶vllm-wheels中每个提交的 wheel 只存放一份/{commit_hash}/、/nightly/、/{version}/三种索引通过相对路径引用同一批文件配合 CloudFront Function 的目录重写让纯对象存储对外呈现标准 PyPI Simple repository 语义变体则由 wheel 文件名的 local version 段cu130、.cpu、.rocm等编码generate-nightly-index.py负责解析、分组、生成 HTML 与机器可读 JSON消费端setup.py再按用户显式指定 → CUDA 变体探测 → base commit 定位 → 元数据匹配的顺序找到并解压最合适的预编译产物。理解了这三层你既能看懂wheels.vllm.ai上任意 URL 背后指向的是哪个提交、哪种 CUDA 后端也能在需要复现某个历史提交性能、或想避开数十分钟本地编译时精准地利用这套预编译通道。【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表