diff --git a/docker/Dockerfile b/docker/Dockerfile index 6e20f6eab4..2ee1d60dbe 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -253,7 +253,7 @@ ENV TORCH_CUDA_ARCH_LIST=${torch_cuda_arch_list} # build stage doesn't need the rust toolchain or the rust source. # This stage reuses the Python environment from base and runs in parallel with # csrc-build/extensions-build. -FROM base AS rust-build +FROM base AS rust-build-cache ARG USE_SCCACHE ARG SCCACHE_ENDPOINT @@ -283,26 +283,28 @@ ENV CARGO_BUILD_JOBS=4 # separate local sccache daemon while sharing the same remote cache backend. ENV SCCACHE_SERVER_PORT=4227 -# Only Rust inputs are present, so suppress the artificial dirty state caused -# by other tracked files being absent from this build stage. -ENV SETUPTOOLS_SCM_PRETEND_METADATA={dirty=false} - -# Build the release artifacts. Cache cargo registry/git, but not target/, -# because stale target metadata can outlive source updates across BuildKit -# cache reuse. +# Build with a stable version so this target snapshot is keyed only by Rust +# inputs. The child stage below relinks the crates that embed the exact version. RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ --mount=type=cache,target=/root/.cargo/git,sharing=locked \ - --mount=type=bind,source=.git,target=.git \ --mount=type=secret,id=aws-credentials,target=/root/.aws/credentials,required=false \ if [ "$USE_SCCACHE" = "1" ]; then \ if [ -n "${SCCACHE_ENDPOINT}" ]; then export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; fi; \ export RUSTC_WRAPPER=sccache; \ sccache --show-stats; \ fi \ - && bash build_rust.sh \ + && SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+docker.cache" bash build_rust.sh \ && if [ "$USE_SCCACHE" = "1" ]; then \ sccache --show-stats; \ fi + +# Relink with the exact Git-derived package version. +FROM rust-build-cache AS rust-build + +RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ + --mount=type=cache,target=/root/.cargo/git,sharing=locked \ + --mount=type=bind,source=.git,target=.git \ + SETUPTOOLS_SCM_PRETEND_METADATA="{dirty=false}" bash build_rust.sh #################### RUST BUILD IMAGE #################### #################### CSRC BUILD IMAGE #################### diff --git a/docker/Dockerfile.cpu b/docker/Dockerfile.cpu index 744336d926..2c9adf4ace 100644 --- a/docker/Dockerfile.cpu +++ b/docker/Dockerfile.cpu @@ -129,7 +129,7 @@ RUN echo 'ulimit -c 0' >> ~/.bashrc # Build the Rust frontend (`vllm-rs`) in a dedicated stage so the wheel build # stage doesn't need the rust toolchain. This stage runs in parallel # with the main vllm-build stage. -FROM base AS rust-build +FROM base AS rust-build-cache ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -y \ @@ -163,25 +163,27 @@ COPY build_rust.sh build_rust.sh # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise). ENV CARGO_BUILD_JOBS=4 -# Only Rust inputs are present, so suppress the artificial dirty state caused -# by other tracked files being absent from this build stage. -ENV SETUPTOOLS_SCM_PRETEND_METADATA={dirty=false} - -# Build the release artifacts. Cache cargo registry/git, but not target/, -# because stale target metadata can outlive source updates across BuildKit -# cache reuse. +# Build with a stable version so this target snapshot is keyed only by Rust +# inputs. The child stage below relinks the crates that embed the exact version. RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ --mount=type=cache,target=/root/.cargo/git,sharing=locked \ - --mount=type=bind,source=.git,target=.git \ --mount=type=secret,id=aws-credentials,target=/root/.aws/credentials,required=false \ if [ "$USE_SCCACHE" = "1" ]; then \ export RUSTC_WRAPPER=sccache; \ if [ -n "${SCCACHE_ENDPOINT}" ]; then export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; fi; \ sccache --show-stats; \ fi && \ - bash build_rust.sh && \ + SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+docker.cache" bash build_rust.sh && \ if [ "$USE_SCCACHE" = "1" ]; then sccache --show-stats; fi +# Relink with the exact Git-derived package version. +FROM rust-build-cache AS rust-build + +RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ + --mount=type=cache,target=/root/.cargo/git,sharing=locked \ + --mount=type=bind,source=.git,target=.git \ + SETUPTOOLS_SCM_PRETEND_METADATA="{dirty=false}" bash build_rust.sh + ######################### SOURCE PREP IMAGE ######################### # Shared prep (source, rust artifacts, build deps) for both vllm-build and # vllm-dev, so the two independent compiles (bdist_wheel vs. setup.py diff --git a/docker/Dockerfile.xpu b/docker/Dockerfile.xpu index 12ebfa36fe..2b65f73288 100644 --- a/docker/Dockerfile.xpu +++ b/docker/Dockerfile.xpu @@ -1,7 +1,7 @@ ######################### RUST BUILD IMAGE ######################### # Build the Rust frontend (`vllm-rs`) in a dedicated stage so the main image # doesn't need the rust toolchain. Runs in parallel with vllm-base. -FROM ubuntu:22.04 AS rust-build +FROM ubuntu:22.04 AS rust-build-cache ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -y \ @@ -25,14 +25,19 @@ COPY build_rust.sh build_rust.sh # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise). ENV CARGO_BUILD_JOBS=4 -# Only Rust inputs are present, so suppress the artificial dirty state caused -# by other tracked files being absent from this build stage. -ENV SETUPTOOLS_SCM_PRETEND_METADATA={dirty=false} +# Build with a stable version so this target snapshot is keyed only by Rust +# inputs. The child stage below relinks the crates that embed the exact version. +RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ + --mount=type=cache,target=/root/.cargo/git,sharing=locked \ + SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+docker.cache" bash build_rust.sh + +# Relink with the exact Git-derived package version. +FROM rust-build-cache AS rust-build RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \ --mount=type=cache,target=/root/.cargo/git,sharing=locked \ --mount=type=bind,source=.git,target=.git \ - bash build_rust.sh + SETUPTOOLS_SCM_PRETEND_METADATA="{dirty=false}" bash build_rust.sh FROM ubuntu:24.04 AS vllm-base diff --git a/docs/assets/contributing/dockerfile-stages-dependency.png b/docs/assets/contributing/dockerfile-stages-dependency.png index d62ab626db..bf3254fa08 100644 Binary files a/docs/assets/contributing/dockerfile-stages-dependency.png and b/docs/assets/contributing/dockerfile-stages-dependency.png differ diff --git a/tests/tools/test_docker_build_metadata_args.py b/tests/tools/test_docker_build_metadata_args.py index 2c8d8608b1..87b357ee16 100644 --- a/tests/tools/test_docker_build_metadata_args.py +++ b/tests/tools/test_docker_build_metadata_args.py @@ -153,6 +153,22 @@ def test_vllm_openai_image_embeds_metadata_contract() -> None: assert expected in dockerfile +def test_rust_build_cache_excludes_git_metadata() -> None: + for name in ("Dockerfile", "Dockerfile.cpu", "Dockerfile.xpu"): + dockerfile = (REPO_ROOT / "docker" / name).read_text() + cached_stage, exact_version_stage = dockerfile.split( + "FROM rust-build-cache AS rust-build", maxsplit=1 + ) + exact_version_stage = exact_version_stage.split("\nFROM ", maxsplit=1)[0] + cached_run = cached_stage.rsplit("RUN ", maxsplit=1)[1] + + assert 'SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+docker.cache"' in cached_run + assert "source=.git,target=.git" not in cached_run + assert "source=.git,target=.git" in exact_version_stage + assert 'SETUPTOOLS_SCM_PRETEND_METADATA="{dirty=false}"' in exact_version_stage + assert "bash build_rust.sh" in exact_version_stage + + def test_rocm_ci_base_bake_embeds_content_hash_label() -> None: bake_file = (REPO_ROOT / "docker" / "docker-bake-rocm.hcl").read_text()