Prhub

#51184 [Docker] Cache test dependencies before vLLM install

原始 PR 作者 mgoin 合并时间 2026-08-11 01:34 文件变更 4 提交数 3 评论 6 代码增减 +61 / -6

执行摘要

Docker 测试镜像分层缓存优化,源码变更不再重装依赖

PR body 指出,在缓存命中的 Build image job 中,测试镜像在安装 Git 和开发依赖之前就 ADD 了整个仓库,源码变更会让两个稳定层同时失效:Git 安装 18.9 秒 / 65,523,093 压缩字节,开发依赖 26.0 秒 / 894,989,258 压缩字节,合计约 45 秒执行时间和 916 MiB 重新生成/推送。将 test-deps 分支提前到 per-commit 产物之前,BuildKit 可以复用这两层直到其真实输入(依赖文件)发生变化。blobless checkout 优化被单独跟踪在 vllm-project/ci-infra#455。

这是一次小范围但收益明确的 CI 优化,不必精读;核心设计决策是“把稳定依赖与 per-commit 产物分层”以最大化 BuildKit 层缓存复用,对维护 Docker 镜像的团队有参考价值。建议合并后观察 Buildkite 测试镜像构建时间是否如预期下降,并关注 test 阶段 wheel 安装是否稳定复现。

讨论亮点

本 PR 几乎没有技术讨论:claude[bot] 因 PR 来自 fork 而禁用自动 review;唯一的人类批准来自 Harry-Chen,仅回复 "Thanks!"。mergify 曾提示存在 merge conflict,作者 rebase(66b69d3、f82ef3b)后重新触发 Buildkite CI(#82548、#82558)并解决。第三个 commit 补上 requirements/common.txt 的 COPY,说明依赖文件拷贝顺序是容易被遗漏的点。

实现拆解

  1. 拆分运行时基座:将 docker/Dockerfile 中原来的 FROM ${FINAL_BASE_IMAGE} AS vllm-base 改为 FROM ${FINAL_BASE_IMAGE} AS vllm-runtime-base,保留原有系统依赖安装;随后新增 FROM vllm-runtime-base AS vllm-base,把 vLLM 与 EP kernel wheel 的安装收在 vllm-base 内,生产镜像构建路径不变。
  2. 新增 test-deps 稳定层FROM vllm-runtime-base AS test-deps,先 COPY requirements/common.txt、requirements/lint.txt、requirements/test/cuda.in、requirements/test/cuda.txt,再安装 Git 与 requirements/dev.txt(开发依赖),这些层只随依赖文件变化而失效。
  3. 重写 test 阶段FROM test-deps AS test,先通过 bind mount 从 build 阶段安装 vLLM wheel(含 torch nightly 分支)与 EP kernel wheel,设置 LD_LIBRARY_PATH,最后 ADD . /vllm-workspace/ 加入源码并安装 tests/vllm_test_utils。
  4. 配套工具与文档:tools/pre_commit/update-dockerfile-graph.sh 与 docs/contributing/dockerfile/dockerfile.md 在 dockerfilegraph 命令中增加 --concentrate,重新生成 docs/assets/contributing/dockerfile-stages-dependency.png,只改可视化、不改依赖关系。
  5. 验证:本地执行 pytest tests/tools/test_docker_build_metadata_args.py(7 passed)、docker buildx bake --print test、docker buildx build --call=check --target test 均通过;完整 CUDA 镜像构建未在本地运行,交由 Buildkite CI 验证。
文件 模块 状态 重要度
docker/Dockerfile 镜像构建 modified 4.8
tools/pre_commit/update-dockerfile-graph.sh 图表生成 modified 2.0
docs/contributing/dockerfile/dockerfile.md 贡献指南 modified 1.96
docs/assets/contributing/dockerfile-stages-dependency.png 文档配图 modified 1.53

关键符号

vllm-runtime-base test-deps vllm-base test

关键源码片段

docker/Dockerfile infrastructure

核心变更:重排测试镜像 stage,新增 vllm-runtime-base、test-deps 并将 test 阶段改为先装 wheel 后 ADD 源码,实现稳定依赖层缓存复用。

# vllm-runtime-base 阶段只安装运行时系统依赖,作为稳定基座,
# 后续 vllm-base 与 test-deps 都从它派生,避免重复安装。
FROM ${FINAL_BASE_IMAGE} AS vllm-runtime-base
# (此处保留原有系统依赖安装步骤,未在本 PR 中改动)# vllm-base 阶段在稳定基座上继续安装 vLLM 与 EP kernel wheel,
# 生产镜像构建路径的行为保持不变。
FROM vllm-runtime-base AS vllm-base
# (wheel 安装逻辑保持原有实现)# test-deps 阶段先把 requirements 文件 COPY 进镜像,再安装 Git 与开发依赖,
# 这样只有依赖文件变化时这些层才失效,源码变更不会触发重装。
FROM vllm-runtime-base AS test-deps
COPY requirements/common.txt requirements/common.txt
COPY requirements/lint.txt requirements/lint.txt
COPY requirements/test/cuda.in requirements/test/cuda.in
COPY requirements/test/cuda.txt requirements/test/cuda.txt
RUN apt-get update -y \
    && apt-get install -y git ... \
    && --mount=type=cache,target=/opt/uv/cache \
       uv pip install --system -r requirements/dev.txt ...
​
# test 阶段先安装 per-commit 的 vLLM/EP wheel,再把仓库源码 ADD 进来,
# 让最易变的源码处于最上层,最大化 BuildKit 层缓存复用。
FROM test-deps AS test
COPY --from=build /workspace/dist/wheel.sha256 /tmp/vllm-wheel.sha256
RUN --mount=type=bind,from=build,src=/workspace/dist,target=/vllm-workspace/dist \
    --mount=type=cache,target=/opt/uv/cache \
    uv pip install --system dist/*.whl --verbose # 安装 vLLM wheel
ADD . /vllm-workspace/ # 源码最后加入,避免污染稳定层
RUN --mount=type=cache,target=/opt/uv/cache \
    uv pip install --system -e tests/vllm_test_utils

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 构建顺序变化:test 阶段现在从 test-deps 派生,wheel 安装依赖 build 阶段的 bind mount(wheel.sha256、torch_lib_versions.txt),若 CI 中该产物路径变化或缺失会直接失败;PR 未本地跑完整 CUDA 镜像,需 Buildkite 全量验证。
  2. stage 重命名兼容性:原 vllm-base 保留但拆出新 vllm-runtime-base,任何直接引用旧 stage 的外部脚本不受影响;但新基座名称需要团队知晓,避免后续维护时误用。
  3. 依赖文件完整性:test-deps 需要同时 COPY 多份 requirements 文件,且 cuda.in 与 cuda.txt 有生成关系;第三次 commit 补上 requirements/common.txt 说明该处容易遗漏。
  4. 缓存语义变化:源码变更不再触发依赖重装,但若开发依赖需要跟随源码变化(如 tests/vllm_test_utils),该包已在 ADD 之后安装,逻辑正确;反过来若误把源码相关文件放入依赖层,会引入缓存污染风险。

影响集中在 CI/Docker 镜像构建:对 source-only commit,预计每次构建减少约 45 秒执行时间和 916 MiB 层生成/推送量,缓存命中场景收益显著;生产 vllm-base 镜像路径与开发者镜像保持不变,对最终用户无影响。需要团队维护 Dockerfile 的人员理解新的 stage 划分(vllm-runtime-base / test-deps / test),避免后续改动破坏分层缓存策略。

镜像构建流程变更 未完成全量镜像构建验证 阶段重命名兼容性 依赖层缓存策略

关联 Issue

#455 [CI] Use blobless checkout for image builds

完整报告

参与讨论