| 1 | # default base image |
| 2 | ARG REMOTE_VLLM="0" |
| 3 | ARG VLLM_REPO="https://github.com/vllm-project/vllm.git" |
| 4 | ARG VLLM_BRANCH="main" |
| 5 | ARG COMMON_WORKDIR=/app |
| 6 | ARG BASE_IMAGE=rocm/vllm-dev:base |
| 7 | ARG CI_BASE_IMAGE=rocm/vllm-dev:ci_base |
| 8 | ARG ROCM_TRITON_KERNELS_COMMIT=669b31acc1dd1b3fd93286afd5db67f65d9f7557 |
| 9 | ARG INSTALL_LMCACHE=false |
| 10 | # NIC backend for MoRI RDMA support. |
| 11 | # By default (all), drivers and userspace libraries for all supported NIC types |
| 12 | # (ainic and bnxt) are installed; MoRI selects the appropriate one at runtime. |
| 13 | # To install drivers for a single NIC type only, set NIC_BACKEND explicitly: |
| 14 | # --build-arg NIC_BACKEND=ainic # AMD AINIC (Pensando) only |
| 15 | # --build-arg NIC_BACKEND=bnxt # Broadcom Thor-2 only |
| 16 | # --build-arg NIC_BACKEND=none # Install nothing. |
| 17 | ARG NIC_BACKEND=all |
| 18 | # AMD AINIC apt repo settings |
| 19 | # Users can specify a custom version compatible with their host drivers. |
| 20 | # The default version has been tested with ionic-dkms=26.03.3.001 |
| 21 | # (libionic1=54.0-187-1), required for MoRI-EP/NIXL on Pollara/AINIC. |
| 22 | ARG AINIC_VERSION=1.117.5-a-77 |
| 23 | ARG UBUNTU_CODENAME=jammy |
| 24 | |
| 25 | # Sccache configuration. Release builds use this today; CI can opt in when a |
| 26 | # shared S3-compatible cache backend is available. |
| 27 | ARG USE_SCCACHE |
| 28 | ARG SCCACHE_DOWNLOAD_URL |
| 29 | ARG SCCACHE_ENDPOINT |
| 30 | ARG SCCACHE_BUCKET_NAME=vllm-build-sccache |
| 31 | ARG SCCACHE_REGION_NAME=us-west-2 |
| 32 | ARG SCCACHE_S3_NO_CREDENTIALS=0 |
| 33 | |
| 34 | FROM ${BASE_IMAGE} AS base |
| 35 | |
| 36 | ARG ARG_PYTORCH_ROCM_ARCH |
| 37 | ENV PYTORCH_ROCM_ARCH=${ARG_PYTORCH_ROCM_ARCH:-${PYTORCH_ROCM_ARCH}} |
| 38 | |
| 39 | # Install build dependencies and utilities |
| 40 | RUN apt-get update -q -y && apt-get install -q -y \ |
| 41 | sqlite3 libsqlite3-dev libfmt-dev libmsgpack-dev libsuitesparse-dev \ |
| 42 | apt-transport-https ca-certificates wget curl \ |
| 43 | libnuma-dev ccache mold |
| 44 | RUN --mount=type=cache,target=/root/.cache/pip \ |
| 45 | python3 -m pip install --upgrade pip |
| 46 | # Note: mold is installed but not set as the system default linker because |
| 47 | # some packages use JIT compilation at runtime with flags mold does not support. |
| 48 | # Build stages opt in via LDFLAGS="-fuse-ld=mold". |
| 49 | # Remove sccache only if not using sccache (it exists in base image from Dockerfile.rocm_base) |
| 50 | ARG USE_SCCACHE |
| 51 | RUN if [ "$USE_SCCACHE" != "1" ]; then \ |
| 52 | apt-get purge -y sccache || true; \ |
| 53 | python3 -m pip uninstall -y sccache || true; \ |
| 54 | rm -f "$(which sccache)" || true; \ |
| 55 | fi |
| 56 | |
| 57 | # Install UV — download first, then run, so a curl failure is not masked by the pipe |
| 58 | RUN curl -LsSf --retry 3 --retry-delay 5 https://astral.sh/uv/install.sh -o /tmp/uv-install.sh \ |
| 59 | && env UV_INSTALL_DIR="/usr/local/bin" sh /tmp/uv-install.sh \ |
| 60 | && rm -f /tmp/uv-install.sh \ |
| 61 | && uv --version |
| 62 | |
| 63 | # This timeout (in seconds) is necessary when installing some dependencies via uv since it's likely to time out |
| 64 | # Reference: https://github.com/astral-sh/uv/pull/1694 |
| 65 | ENV UV_HTTP_TIMEOUT=500 |
| 66 | ENV UV_INDEX_STRATEGY="unsafe-best-match" |
| 67 | # Use copy mode to avoid hardlink failures with Docker cache mounts |
| 68 | ENV UV_LINK_MODE=copy |
| 69 | # ccache directory - persisted across layer rebuilds via cache mounts. |
| 70 | ENV CCACHE_DIR=/root/.cache/ccache |
| 71 | ENV CCACHE_COMPILERCHECK=content |
| 72 | # Empty by default so build steps fall back to $(nproc); CI can override. |
| 73 | ARG max_jobs |
| 74 | ENV MAX_JOBS=${max_jobs} |
| 75 | |
| 76 | # Install sccache if USE_SCCACHE is enabled (for release builds) |
| 77 | ARG USE_SCCACHE |
| 78 | ARG SCCACHE_DOWNLOAD_URL |
| 79 | ARG SCCACHE_ENDPOINT |
| 80 | ARG SCCACHE_BUCKET_NAME |
| 81 | ARG SCCACHE_REGION_NAME |
| 82 | ARG SCCACHE_S3_NO_CREDENTIALS |
| 83 | RUN if [ "$USE_SCCACHE" = "1" ]; then \ |
| 84 | if command -v sccache >/dev/null 2>&1; then \ |
| 85 | echo "sccache already installed, skipping installation"; \ |
| 86 | sccache --version; \ |
| 87 | else \ |
| 88 | echo "Installing sccache..." \ |
| 89 | && SCCACHE_ARCH="x86_64" \ |
| 90 | && SCCACHE_VERSION="v0.8.1" \ |
| 91 | && SCCACHE_DL_URL="${SCCACHE_DOWNLOAD_URL:-https://github.com/mozilla/sccache/releases/download/${SCCACHE_VERSION}/sccache-${SCCACHE_VERSION}-${SCCACHE_ARCH}-unknown-linux-musl.tar.gz}" \ |
| 92 | && curl -L -o /tmp/sccache.tar.gz ${SCCACHE_DL_URL} \ |
| 93 | && tar -xzf /tmp/sccache.tar.gz -C /tmp \ |
| 94 | && mv /tmp/sccache-${SCCACHE_VERSION}-${SCCACHE_ARCH}-unknown-linux-musl/sccache /usr/bin/sccache \ |
| 95 | && chmod +x /usr/bin/sccache \ |
| 96 | && rm -rf /tmp/sccache.tar.gz /tmp/sccache-${SCCACHE_VERSION}-${SCCACHE_ARCH}-unknown-linux-musl \ |
| 97 | && sccache --version; \ |
| 98 | fi; \ |
| 99 | fi |
| 100 | |
| 101 | # Set sccache environment variables only when USE_SCCACHE=1 |
| 102 | # This prevents S3 config from leaking into images when sccache is not used |
| 103 | ARG USE_SCCACHE |
| 104 | ENV SCCACHE_BUCKET=${USE_SCCACHE:+${SCCACHE_BUCKET_NAME}} |
| 105 | ENV SCCACHE_REGION=${USE_SCCACHE:+${SCCACHE_REGION_NAME}} |
| 106 | ENV SCCACHE_S3_NO_CREDENTIALS=${USE_SCCACHE:+${SCCACHE_S3_NO_CREDENTIALS}} |
| 107 | ENV SCCACHE_IDLE_TIMEOUT=${USE_SCCACHE:+0} |
| 108 | |
| 109 | ARG COMMON_WORKDIR |
| 110 | WORKDIR ${COMMON_WORKDIR} |
| 111 | |
| 112 | |
| 113 | # ----------------------- |
| 114 | # vLLM fetch stages |
| 115 | FROM base AS fetch_vllm_0 |
| 116 | ONBUILD COPY ./ vllm/ |
| 117 | FROM base AS fetch_vllm_1 |
| 118 | ARG VLLM_REPO |
| 119 | ARG VLLM_BRANCH |
| 120 | ONBUILD RUN git clone ${VLLM_REPO} \ |
| 121 | && cd vllm \ |
| 122 | && git fetch -v --prune -- origin ${VLLM_BRANCH} \ |
| 123 | && git checkout FETCH_HEAD \ |
| 124 | && if [ ${VLLM_REPO} != "https://github.com/vllm-project/vllm.git" ] ; then \ |
| 125 | git remote add upstream "https://github.com/vllm-project/vllm.git" \ |
| 126 | && git fetch upstream ; fi |
| 127 | FROM fetch_vllm_${REMOTE_VLLM} AS fetch_vllm |
| 128 | |
| 129 | # Resolve the device-independent source version once for every downstream |
| 130 | # artifact. setuptools-scm reads Git metadata for local/remote sources and the |
| 131 | # archive metadata generated for canonical CI contexts. |
| 132 | FROM fetch_vllm AS vllm-version |
| 133 | ARG COMMON_WORKDIR |
| 134 | RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \ |
| 135 | cd ${COMMON_WORKDIR}/vllm \ |
| 136 | && uv pip install --system -r requirements/build/rust.txt \ |
| 137 | && python3 -c \ |
| 138 | "from setuptools_scm import get_version; print(get_version(git_describe_command=['git', 'describe', '--dirty', '--tags', '--long', '--abbrev=40', '--match', 'v[0-9]*']))" \ |
| 139 | > /vllm-version.txt |
| 140 | |
| 141 | # ----------------------- |
| 142 | # Rust toolchain inputs |
| 143 | # |
| 144 | # Keep these separate from Rust source inputs so Rust source-only changes do not |
| 145 | # invalidate the long-lived ci_base image that carries the toolchain. |
| 146 | FROM scratch AS rust_toolchain_input_0 |
| 147 | COPY rust-toolchain.toml /rust-toolchain-input/vllm/rust-toolchain.toml |
| 148 | |
| 149 | FROM base AS rust_toolchain_input_1 |
| 150 | ARG VLLM_REPO |
| 151 | ARG VLLM_BRANCH |
| 152 | RUN git clone --no-checkout --filter=blob:none ${VLLM_REPO} /rust-toolchain-input/vllm \ |
| 153 | && cd /rust-toolchain-input/vllm \ |
| 154 | && git fetch -v --prune -- origin ${VLLM_BRANCH} \ |
| 155 | && git sparse-checkout init --no-cone \ |
| 156 | && git sparse-checkout set \ |
| 157 | rust-toolchain.toml \ |
| 158 | && git checkout FETCH_HEAD |
| 159 | |
| 160 | FROM rust_toolchain_input_${REMOTE_VLLM} AS rust-toolchain-input |
| 161 | |
| 162 | # ----------------------- |
| 163 | # Rust build inputs |
| 164 | # |
| 165 | # REMOTE_VLLM=0: use only the local files that can affect Rust artifacts. |
| 166 | # REMOTE_VLLM=1: fetch only those same files from the requested remote ref. |
| 167 | FROM scratch AS rust_input_0 |
| 168 | COPY requirements/build/rust.txt /rust-input/vllm/requirements/build/rust.txt |
| 169 | COPY rust/Cargo.lock rust/Cargo.toml /rust-input/vllm/rust/ |
| 170 | COPY rust/proto /rust-input/vllm/rust/proto |
| 171 | COPY rust/src /rust-input/vllm/rust/src |
| 172 | COPY rust-toolchain.toml /rust-input/vllm/rust-toolchain.toml |
| 173 | COPY tools/build_rust.py /rust-input/vllm/tools/build_rust.py |
| 174 | COPY tools/build_rust.sh /rust-input/vllm/tools/build_rust.sh |
| 175 | |
| 176 | FROM base AS rust_input_1 |
| 177 | ARG VLLM_REPO |
| 178 | ARG VLLM_BRANCH |
| 179 | RUN git clone --no-checkout --filter=blob:none ${VLLM_REPO} /rust-input/vllm \ |
| 180 | && cd /rust-input/vllm \ |
| 181 | && git fetch -v --prune -- origin ${VLLM_BRANCH} \ |
| 182 | && git sparse-checkout init --no-cone \ |
| 183 | && git sparse-checkout set \ |
| 184 | requirements/build/rust.txt \ |
| 185 | rust/Cargo.lock \ |
| 186 | rust/Cargo.toml \ |
| 187 | rust/proto/** \ |
| 188 | rust/src/** \ |
| 189 | rust-toolchain.toml \ |
| 190 | tools/build_rust.py \ |
| 191 | tools/build_rust.sh \ |
| 192 | && git checkout FETCH_HEAD |
| 193 | |
| 194 | FROM rust_input_${REMOTE_VLLM} AS rust-input |
| 195 | |
| 196 | # ----------------------- |
| 197 | # Rust toolchain |
| 198 | FROM base AS rust-toolchain |
| 199 | |
| 200 | ENV CARGO_HOME=/root/.cargo |
| 201 | ENV RUSTUP_HOME=/root/.rustup |
| 202 | ENV PATH=${CARGO_HOME}/bin:${PATH} |
| 203 | |
| 204 | RUN apt-get update -q -y && apt-get install -q -y --no-install-recommends \ |
| 205 | ca-certificates curl \ |
| 206 | && rm -rf /var/lib/apt/lists/* |
| 207 | |
| 208 | COPY --from=rust-toolchain-input /rust-toolchain-input/vllm/rust-toolchain.toml /tmp/rust-toolchain.toml |
| 209 | RUN TOOLCHAIN="$(grep '^channel' /tmp/rust-toolchain.toml | sed 's/.*= *"\(.*\)"/\1/')" \ |
| 210 | && if ! command -v rustup >/dev/null 2>&1; then \ |
| 211 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ |
| 212 | | sh -s -- -y --default-toolchain none; \ |
| 213 | fi \ |
| 214 | && rustup toolchain install "${TOOLCHAIN}" \ |
| 215 | && rustup default "${TOOLCHAIN}" \ |
| 216 | && rustc --version \ |
| 217 | && cargo --version |
| 218 | |
| 219 | # ----------------------- |
| 220 | # Rust build stage |
| 221 | # Builds the `vllm-rs` frontend in a dedicated stage so the wheel build stages |
| 222 | # don't need the rust toolchain. |
| 223 | FROM rust-toolchain AS rust-build |
| 224 | ARG COMMON_WORKDIR |
| 225 | ARG USE_SCCACHE |
| 226 | |
| 227 | # Cap cargo parallelism to avoid exhausting the AMD CI host's open-file limit |
| 228 | # (rustc spawns enough concurrent processes to hit RLIMIT_NOFILE otherwise). |
| 229 | ENV CARGO_BUILD_JOBS=4 |
| 230 | ENV CARGO_NET_RETRY=10 |
| 231 | ENV RUSTUP_MAX_RETRIES=10 |
| 232 | |
| 233 | WORKDIR ${COMMON_WORKDIR}/vllm |
| 234 | |
| 235 | COPY --from=rust-input /rust-input/vllm/requirements/build/rust.txt requirements/build/rust.txt |
| 236 | |
| 237 | # BuildKit can run this stage in parallel with ROCm native builds. Keep Rust on |
| 238 | # a separate local sccache daemon while sharing the same remote cache backend. |
| 239 | ENV SCCACHE_SERVER_PORT=4227 |
| 240 | |
| 241 | RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \ |
| 242 | uv pip install --system -r requirements/build/rust.txt |
| 243 | |
| 244 | COPY --from=rust-input /rust-input/vllm/rust rust |
| 245 | COPY --from=rust-input /rust-input/vllm/rust-toolchain.toml rust-toolchain.toml |
| 246 | COPY --from=rust-input /rust-input/vllm/tools/build_rust.py tools/build_rust.py |
| 247 | COPY --from=rust-input /rust-input/vllm/tools/build_rust.sh tools/build_rust.sh |
| 248 | COPY --from=vllm-version /vllm-version.txt /tmp/vllm-version.txt |
| 249 | |
| 250 | # Build the release binary. Cargo's registry/git caches can be written by |
| 251 | # concurrent BuildKit jobs on shared workers, so lock those cache mounts while |
| 252 | # keeping the cache benefit. Do not cache target/, because stale target metadata |
| 253 | # can outlive source updates across BuildKit cache reuse. |
| 254 | RUN --mount=type=cache,id=vllm-rocm-cargo-registry,target=/root/.cargo/registry,sharing=locked \ |
| 255 | --mount=type=cache,id=vllm-rocm-cargo-git,target=/root/.cargo/git,sharing=locked \ |
| 256 | if [ "$USE_SCCACHE" = "1" ]; then \ |
| 257 | export RUSTC_WRAPPER=sccache \ |
| 258 | && sccache --show-stats; \ |
| 259 | fi \ |
| 260 | && export SETUPTOOLS_SCM_PRETEND_VERSION="$(cat /tmp/vllm-version.txt)" \ |
| 261 | && bash tools/build_rust.sh \ |
| 262 | && test -x vllm/vllm-rs \ |
| 263 | && if [ "$USE_SCCACHE" = "1" ]; then \ |
| 264 | sccache --show-stats; \ |
| 265 | fi |
| 266 | |
| 267 | # ----------------------- |
| 268 | # Shared vLLM build dependencies |
| 269 | # |
| 270 | # Install requirements once, before bringing source-specific inputs into either |
| 271 | # consumer. Native compilation and wheel packaging then share this expensive |
| 272 | # layer while retaining their independent source cache boundaries. |
| 273 | FROM base AS build_vllm_dependencies |
| 274 | ARG COMMON_WORKDIR |
| 275 | ENV VLLM_TARGET_DEVICE=rocm |
| 276 | WORKDIR ${COMMON_WORKDIR}/vllm |
| 277 | |
| 278 | COPY --from=fetch_vllm \ |
| 279 | ${COMMON_WORKDIR}/vllm/requirements/common.txt \ |
| 280 | ${COMMON_WORKDIR}/vllm/requirements/rocm.txt \ |
| 281 | requirements/ |
| 282 | |
| 283 | RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \ |
| 284 | uv pip install --system -r requirements/rocm.txt |
| 285 | |
| 286 | # Fetch only the pinned ROCm Triton kernels package. Keeping this before the |
| 287 | # vLLM source COPYs makes the download reusable when native sources change. |
| 288 | FROM base AS rocm-triton-kernels |
| 289 | ARG ROCM_TRITON_KERNELS_COMMIT |
| 290 | ADD https://codeload.github.com/ROCm/triton/tar.gz/${ROCM_TRITON_KERNELS_COMMIT} \ |
| 291 | /tmp/rocm-triton.tar.gz |
| 292 | RUN mkdir -p /opt/rocm-triton-kernels \ |
| 293 | && tar --extract --gzip --file /tmp/rocm-triton.tar.gz \ |
| 294 | --directory /opt/rocm-triton-kernels --strip-components=4 \ |
| 295 | --no-same-owner \ |
| 296 | "triton-${ROCM_TRITON_KERNELS_COMMIT}/python/triton_kernels/triton_kernels" \ |
| 297 | && test -f /opt/rocm-triton-kernels/__init__.py \ |
| 298 | && rm /tmp/rocm-triton.tar.gz |
| 299 | |
| 300 | # ----------------------- |
| 301 | # vLLM native build stages |
| 302 | # |
| 303 | # csrc-build intentionally copies only files that affect ROCm native extension |
| 304 | # compilation. That keeps unrelated CI/test/docs edits from invalidating the |
| 305 | # expensive HIP/C++ build layer. |
| 306 | FROM build_vllm_dependencies AS csrc-build |
| 307 | ARG ROCM_TRITON_KERNELS_COMMIT |
| 308 | COPY --from=rocm-triton-kernels \ |
| 309 | /opt/rocm-triton-kernels /opt/rocm-triton-kernels |
| 310 | ENV TRITON_KERNELS_SRC_DIR=/opt/rocm-triton-kernels |
| 311 | |
| 312 | # pyproject.toml is bind-mounted so it is not persisted in the csrc-build image, |
| 313 | # but it still participates in the BuildKit cache key for this RUN step. |
| 314 | COPY setup.py CMakeLists.txt ./ |
| 315 | COPY tools/build_rust.py tools/build_rust.py |
| 316 | COPY cmake cmake/ |
| 317 | COPY csrc csrc/ |
| 318 | COPY vllm/envs.py vllm/envs.py |
| 319 | COPY vllm/__init__.py vllm/__init__.py |
| 320 | |
| 321 | ENV SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+rocm.csrc.build" |
| 322 | |
| 323 | RUN --mount=type=bind,source=pyproject.toml,target=${COMMON_WORKDIR}/vllm/pyproject.toml \ |
| 324 | --mount=type=cache,id=vllm-rocm-ccache,target=/root/.cache/ccache \ |
| 325 | grep -Fq "set(TRITON_KERNELS_TAG \"${ROCM_TRITON_KERNELS_COMMIT}\")" \ |
| 326 | cmake/external_projects/triton_kernels.cmake \ |
| 327 | && export CCACHE_BASEDIR="$PWD" \ |
| 328 | && echo "=== ccache stats before ROCm native build ===" \ |
| 329 | && (ccache --show-stats || true) \ |
| 330 | && (ccache --zero-stats || true) \ |
| 331 | && EFFECTIVE_MAX_JOBS="${MAX_JOBS:-$(nproc)}" \ |
| 332 | && echo "Building ROCm native extension wheel with MAX_JOBS=${EFFECTIVE_MAX_JOBS}" \ |
| 333 | && PYTHONDONTWRITEBYTECODE=1 LDFLAGS="-fuse-ld=mold" MAX_JOBS="${EFFECTIVE_MAX_JOBS}" python3 setup.py bdist_wheel --dist-dir=dist \ |
| 334 | && test -d dist \ |
| 335 | && ls dist/*.whl >/dev/null \ |
| 336 | && echo "=== ccache stats after ROCm native build ===" \ |
| 337 | && (ccache --show-stats || true) |
| 338 | |
| 339 | # Build the full vLLM ROCm wheel by reusing the native extension wheel from |
| 340 | # csrc-build. This stage still rebuilds for Python/package changes, but skips |
| 341 | # the expensive dependency installation and HIP/C++ compilation when their |
| 342 | # respective inputs are unchanged. |
| 343 | FROM build_vllm_dependencies AS build_vllm |
| 344 | COPY --from=fetch_vllm ${COMMON_WORKDIR}/vllm ${COMMON_WORKDIR}/vllm |
| 345 | COPY --from=csrc-build ${COMMON_WORKDIR}/vllm/dist /precompiled-wheels |
| 346 | COPY --from=vllm-version /vllm-version.txt /tmp/vllm-version.txt |
| 347 | |
| 348 | # Drop the pre-built Rust artifacts into the source tree. setup.py detects |
| 349 | # them and ships them as-is, skipping the local Rust build. |
| 350 | COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs |
| 351 | COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/_rust_*.so ${COMMON_WORKDIR}/vllm/vllm/ |
| 352 | |
| 353 | RUN cd "${COMMON_WORKDIR}/vllm" \ |
| 354 | && export SETUPTOOLS_SCM_PRETEND_VERSION="$(cat /tmp/vllm-version.txt)" \ |
| 355 | && export VLLM_USE_PRECOMPILED=1 \ |
| 356 | && export VLLM_PRECOMPILED_WHEEL_LOCATION="$(ls /precompiled-wheels/*.whl)" \ |
| 357 | && export VLLM_DOCKER_BUILD_CONTEXT=1 \ |
| 358 | && echo "Packaging vLLM ROCm wheel using precompiled extensions from ${VLLM_PRECOMPILED_WHEEL_LOCATION}" \ |
| 359 | && PYTHONDONTWRITEBYTECODE=1 python3 setup.py bdist_wheel --dist-dir=dist \ |
| 360 | && test -d dist \ |
| 361 | && ls dist/*.whl >/dev/null |
| 362 | FROM scratch AS export_vllm |
| 363 | ARG COMMON_WORKDIR |
| 364 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/dist/*.whl / |
| 365 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/requirements /requirements |
| 366 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/benchmarks /benchmarks |
| 367 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/tests /tests |
| 368 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/examples /examples |
| 369 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/tools /tools |
| 370 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/Dockerfile /docker/Dockerfile |
| 371 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/Dockerfile.cpu /docker/Dockerfile.cpu |
| 372 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/Dockerfile.rocm /docker/ |
| 373 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/Dockerfile.rocm_base /docker/Dockerfile.rocm_base |
| 374 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/ci-rocm.hcl /docker/ci-rocm.hcl |
| 375 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/docker-bake.hcl /docker/docker-bake.hcl |
| 376 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/docker/docker-bake-rocm.hcl /docker/docker-bake-rocm.hcl |
| 377 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/.buildkite /.buildkite |
| 378 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/pyproject.toml /pyproject.toml |
| 379 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/rust /rust |
| 380 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/rust-toolchain.toml /rust-toolchain.toml |
| 381 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm/vllm/v1 /vllm_v1 |
| 382 | |
| 383 | # NIXL/UCX build stages |
| 384 | FROM base AS build_nixl |
| 385 | ARG NIXL_BRANCH="231d56753047c989062a5cb2ac703a1ad761c7d2" |
| 386 | ARG NIXL_REPO="https://github.com/ai-dynamo/nixl.git" |
| 387 | ARG UCX_BRANCH="96e58a16039f6d7d213bc967b8069238742c5194" |
| 388 | ARG UCX_REPO="https://github.com/openucx/ucx.git" |
| 389 | ENV ROCM_PATH=/opt/rocm |
| 390 | ENV UCX_HOME=/usr/local/ucx |
| 391 | ENV NIXL_HOME=/usr/local/nixl |
| 392 | |
| 393 | # NIXL build system dependencies and RDMA support |
| 394 | RUN apt-get -y update && apt-get -y install autoconf libtool pkg-config \ |
| 395 | libgrpc-dev \ |
| 396 | libgrpc++-dev \ |
| 397 | libprotobuf-dev \ |
| 398 | protobuf-compiler-grpc \ |
| 399 | libcpprest-dev \ |
| 400 | libaio-dev \ |
| 401 | librdmacm1 \ |
| 402 | librdmacm-dev \ |
| 403 | libibverbs1 \ |
| 404 | libibverbs-dev \ |
| 405 | ibverbs-utils \ |
| 406 | rdmacm-utils \ |
| 407 | ibverbs-providers \ |
| 408 | && rm -rf /var/lib/apt/lists/* |
| 409 | |
| 410 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 411 | uv pip install --system meson meson-python pyyaml types-PyYAML \ |
| 412 | auditwheel build patchelf pytest tomlkit "setuptools>=80.9.0" |
| 413 | |
| 414 | RUN --mount=type=cache,target=/root/.cache/ccache \ |
| 415 | cd /usr/local/src && \ |
| 416 | git clone ${UCX_REPO} && \ |
| 417 | cd ucx && \ |
| 418 | git checkout ${UCX_BRANCH} && \ |
| 419 | ./autogen.sh && \ |
| 420 | mkdir build && cd build && \ |
| 421 | CC="ccache gcc" CXX="ccache g++" \ |
| 422 | ../configure \ |
| 423 | --prefix=/usr/local/ucx \ |
| 424 | --enable-shared \ |
| 425 | --disable-static \ |
| 426 | --disable-doxygen-doc \ |
| 427 | --enable-optimizations \ |
| 428 | --enable-devel-headers \ |
| 429 | --with-rocm=${ROCM_PATH} \ |
| 430 | --with-verbs \ |
| 431 | --with-dm \ |
| 432 | --enable-mt && \ |
| 433 | make -j$(nproc) && \ |
| 434 | make install |
| 435 | |
| 436 | ENV PATH=/usr/local/ucx/bin:$PATH |
| 437 | ENV LD_LIBRARY_PATH=${UCX_HOME}/lib:${LD_LIBRARY_PATH} |
| 438 | |
| 439 | RUN --mount=type=cache,target=/root/.cache/ccache \ |
| 440 | git clone ${NIXL_REPO} /opt/nixl && \ |
| 441 | cd /opt/nixl && \ |
| 442 | git checkout ${NIXL_BRANCH} && \ |
| 443 | CC="ccache gcc" CXX="ccache g++" \ |
| 444 | meson setup build --prefix=${NIXL_HOME} \ |
| 445 | -Ducx_path=${UCX_HOME} \ |
| 446 | -Dwheel_variant=rocm \ |
| 447 | -Dbuild_tests=false \ |
| 448 | -Dbuild_examples=false && \ |
| 449 | cd build && \ |
| 450 | ninja -j$(nproc) && \ |
| 451 | ninja install && \ |
| 452 | echo "${NIXL_HOME}/lib/$(uname -m)-linux-gnu" \ |
| 453 | > /etc/ld.so.conf.d/nixl.conf && \ |
| 454 | echo "${NIXL_HOME}/lib/$(uname -m)-linux-gnu/plugins" \ |
| 455 | >> /etc/ld.so.conf.d/nixl.conf && \ |
| 456 | ldconfig |
| 457 | |
| 458 | # Generate the ROCm NIXL wheel. Upstream's generic wheel helper detects CUDA, |
| 459 | # so configure the ROCm wheel variant directly through Meson. |
| 460 | # Exclude libcore and libpull from auditwheel: transitive dependencies |
| 461 | # that are not shipped in the wheel and vary across base images. |
| 462 | RUN cd /opt/nixl && \ |
| 463 | ./contrib/tomlutil.py --wheel-name nixl-rocm pyproject.toml && \ |
| 464 | CC="ccache gcc" CXX="ccache g++" \ |
| 465 | uv build --wheel --no-build-isolation --out-dir /tmp/nixl_wheels \ |
| 466 | --python ${PYTHON_VERSION} \ |
| 467 | -Csetup-args=-Ducx_path=${UCX_HOME} \ |
| 468 | -Csetup-args=-Dwheel_variant=rocm \ |
| 469 | -Csetup-args=-Dbuild_tests=false \ |
| 470 | -Csetup-args=-Dbuild_examples=false && \ |
| 471 | mkdir -p /tmp/nixl_wheels/repaired /app/install && \ |
| 472 | auditwheel repair \ |
| 473 | --exclude 'libamdhip64*' \ |
| 474 | --exclude 'libcore*' \ |
| 475 | --exclude 'libpull*' \ |
| 476 | /tmp/nixl_wheels/nixl_rocm*.whl \ |
| 477 | --plat manylinux_2_34_$(uname -m) \ |
| 478 | --wheel-dir /tmp/nixl_wheels/repaired && \ |
| 479 | ./contrib/wheel_add_ucx_plugins.py \ |
| 480 | --ucx-plugins-dir ${UCX_HOME}/lib/ucx \ |
| 481 | --nixl-plugins-dir ${NIXL_HOME}/lib/$(uname -m)-linux-gnu/plugins \ |
| 482 | /tmp/nixl_wheels/repaired/*.whl && \ |
| 483 | cp /tmp/nixl_wheels/repaired/*.whl /app/install |
| 484 | |
| 485 | # Fetch and verify pinned LMCache source. |
| 486 | FROM base AS lmcache_source |
| 487 | ARG LMCACHE_REPO="https://github.com/LMCache/LMCache.git" |
| 488 | ARG LMCACHE_REF="140819c9d57a975dbc5678a6459a218e544cb58b" |
| 489 | ARG LMCACHE_VERSION="0.5.3" |
| 490 | |
| 491 | # Remove NVIDIA-only dependencies and accept the release image's NumPy version. |
| 492 | RUN printf '%s\n' "${LMCACHE_REF}" | grep -Eq '^[0-9a-f]{40}$' \ |
| 493 | && git clone --depth=1 --branch "v${LMCACHE_VERSION}" \ |
| 494 | "${LMCACHE_REPO}" /app/lmcache \ |
| 495 | && test "$(git -C /app/lmcache rev-parse HEAD)" = "${LMCACHE_REF}" \ |
| 496 | && test "$(grep -Ec '^(cufile-python|nvtx)([<=>[:space:]]|$)' \ |
| 497 | /app/lmcache/requirements/common.txt)" -eq 2 \ |
| 498 | && test "$(grep -Fxc 'numpy<=2.2.6' \ |
| 499 | /app/lmcache/requirements/common.txt)" -eq 1 \ |
| 500 | && sed -i -E \ |
| 501 | -e '/^(cufile-python|nvtx)([<=>[:space:]]|$)/d' \ |
| 502 | -e 's/^numpy<=2\.2\.6$/numpy<=2.3.5/' \ |
| 503 | /app/lmcache/requirements/common.txt \ |
| 504 | && rm -rf /app/lmcache/.git |
| 505 | |
| 506 | FROM base AS build_lmcache |
| 507 | COPY --from=lmcache_source \ |
| 508 | /app/lmcache/requirements/build.txt /tmp/lmcache-build-requirements.txt |
| 509 | RUN --mount=type=cache,id=vllm-rocm-uv,target=/root/.cache/uv \ |
| 510 | uv pip install --system -r /tmp/lmcache-build-requirements.txt |
| 511 | |
| 512 | ARG LMCACHE_VERSION="0.5.3" |
| 513 | ARG LMCACHE_ROCM_ARCH="gfx942;gfx950" |
| 514 | ARG USE_SCCACHE |
| 515 | |
| 516 | COPY --from=lmcache_source /app/lmcache /app/lmcache |
| 517 | WORKDIR /app/lmcache |
| 518 | |
| 519 | RUN --mount=type=cache,id=vllm-rocm-ccache,target=/root/.cache/ccache \ |
| 520 | set -eu; \ |
| 521 | printf '%s\n' "${LMCACHE_ROCM_ARCH}" \ |
| 522 | | grep -Eq '^gfx[0-9a-z]+(;gfx[0-9a-z]+)*$'; \ |
| 523 | offload_flags="$(printf '%s' "${LMCACHE_ROCM_ARCH}" \ |
| 524 | | sed 's/;/ --offload-arch=/g; s/^/--offload-arch=/')"; \ |
| 525 | if [ "${USE_SCCACHE:-0}" = "1" ]; then \ |
| 526 | export HIP_CLANG_PATH=/opt/sccache-wrappers; \ |
| 527 | fi; \ |
| 528 | mkdir -p /app/install; \ |
| 529 | SETUPTOOLS_SCM_PRETEND_VERSION="${LMCACHE_VERSION}" \ |
| 530 | PYTORCH_ROCM_ARCH="${LMCACHE_ROCM_ARCH}" \ |
| 531 | HIPCC_COMPILE_FLAGS_APPEND="${offload_flags}" \ |
| 532 | BUILD_WITH_HIP=1 CXX=hipcc \ |
| 533 | python3 setup.py bdist_wheel --dist-dir=/app/install; \ |
| 534 | test "$(find /app/install -maxdepth 1 -name '*.whl' | wc -l)" -eq 1 |
| 535 | |
| 536 | RUN --mount=type=tmpfs,target=/tmp/lmcache-wheel \ |
| 537 | set -eu; \ |
| 538 | python3 -m zipfile -e /app/install/*.whl /tmp/lmcache-wheel; \ |
| 539 | c_ops="$(find /tmp/lmcache-wheel -type f -name 'c_ops*.so')"; \ |
| 540 | test "$(find /tmp/lmcache-wheel -type f -name 'c_ops*.so' | wc -l)" -eq 1; \ |
| 541 | expected="$(printf '%s' "${LMCACHE_ROCM_ARCH}" | tr ';' '\n' | sort -u)"; \ |
| 542 | actual="$(/opt/rocm/llvm/bin/llvm-objdump --offloading "${c_ops}" \ |
| 543 | | grep -oE 'gfx[0-9a-z]+' | sort -u)"; \ |
| 544 | if [ "${actual}" != "${expected}" ]; then \ |
| 545 | printf 'LMCache code objects mismatch\nexpected:\n%s\nactual:\n%s\n' \ |
| 546 | "${expected}" "${actual}" >&2; \ |
| 547 | exit 1; \ |
| 548 | fi |
| 549 | |
| 550 | # ROCShmem build stage - split from DeepEP so changing DEEPEP_BRANCH does not |
| 551 | # invalidate the slow ROCShmem build. |
| 552 | FROM base AS build_rocshmem |
| 553 | ARG ROCSHMEM_BRANCH="f0acb0c6" |
| 554 | ARG ROCSHMEM_REPO="https://github.com/ROCm/rocm-systems.git" |
| 555 | # DeepEP only supports gfx942 and gfx950; build ROCShmem for the same set so |
| 556 | # it can be linked against DeepEP without arch mismatches. |
| 557 | ARG DEEPEP_ROCM_ARCH="gfx942;gfx950" |
| 558 | ENV ROCM_PATH=/opt/rocm |
| 559 | ENV ROCSHMEM_DIR=/opt/rocshmem |
| 560 | |
| 561 | RUN --mount=type=cache,target=/root/.cache/ccache \ |
| 562 | git clone --no-checkout --filter=blob:none ${ROCSHMEM_REPO} \ |
| 563 | && cd rocm-systems \ |
| 564 | && git sparse-checkout set --cone projects/rocshmem \ |
| 565 | && git checkout ${ROCSHMEM_BRANCH} \ |
| 566 | && mkdir -p projects/rocshmem/build \ |
| 567 | && cd projects/rocshmem/build \ |
| 568 | && CC="ccache gcc" CXX="ccache g++" INSTALL_PREFIX=${ROCSHMEM_DIR} \ |
| 569 | bash ../scripts/build_configs/all_backends \ |
| 570 | -DROCM_PATH=${ROCM_PATH} \ |
| 571 | -DGPU_TARGETS="${DEEPEP_ROCM_ARCH}" \ |
| 572 | -DUSE_EXTERNAL_MPI=OFF |
| 573 | |
| 574 | # DeepEP build stage - depends on ROCShmem, builds the HIP kernel wheel. |
| 575 | FROM build_rocshmem AS build_deepep |
| 576 | ARG DEEPEP_BRANCH="0f63d3e9" |
| 577 | ARG DEEPEP_REPO="https://github.com/ROCm/DeepEP.git" |
| 578 | ARG DEEPEP_NIC="cx7" |
| 579 | |
| 580 | # Build DeepEP wheel. DeepEP looks for rocshmem at ROCSHMEM_DIR. |
| 581 | # DeepEP only supports gfx942 and gfx950, so avoid gfx90a in the default list. |
| 582 | # We pass --rocm-gfx942-fp8-fnuz-max 224 to align the DeepEP dispatch FP8 |
| 583 | # quantization bound with vLLM's own clamp |
| 584 | # (vllm/model_executor/layers/quantization/utils/fp8_utils.py): 224 for E4M3FNUZ |
| 585 | RUN --mount=type=cache,target=/root/.cache/ccache \ |
| 586 | export PYTORCH_ROCM_ARCH="gfx942;gfx950" \ |
| 587 | && git clone ${DEEPEP_REPO} \ |
| 588 | && cd DeepEP \ |
| 589 | && git checkout ${DEEPEP_BRANCH} \ |
| 590 | && LDFLAGS="-fuse-ld=mold" MAX_JOBS="${MAX_JOBS:-$(nproc)}" python3 setup.py --variant rocm --rocm-explicit-ctx --nic ${DEEPEP_NIC} --rocm-gfx942-fp8-fnuz-max 224 bdist_wheel --dist-dir=/app/deep_install |
| 591 | |
| 592 | # MoRI runtime dependencies live in Dockerfile.rocm so NIC backend changes do |
| 593 | # not force users to rebuild the long-lived Dockerfile.rocm_base image. |
| 594 | FROM base AS mori_base |
| 595 | ARG NIC_BACKEND |
| 596 | ARG AINIC_VERSION |
| 597 | ARG UBUNTU_CODENAME |
| 598 | RUN /bin/bash -lc 'set -euo pipefail; \ |
| 599 | \ |
| 600 | install_ainic() { \ |
| 601 | apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gnupg apt-transport-https; \ |
| 602 | rm -rf /var/lib/apt/lists/*; \ |
| 603 | mkdir -p /etc/apt/keyrings; \ |
| 604 | curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | gpg --dearmor > /etc/apt/keyrings/amdainic.gpg; \ |
| 605 | echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/amdainic.gpg] https://repo.radeon.com/amdainic/pensando/ubuntu/${AINIC_VERSION} ${UBUNTU_CODENAME} main" \ |
| 606 | > /etc/apt/sources.list.d/amdainic.list; \ |
| 607 | apt-get update && apt-get install -y --no-install-recommends \ |
| 608 | libionic-dev \ |
| 609 | ionic-common \ |
| 610 | ; \ |
| 611 | rm -rf /var/lib/apt/lists/*; \ |
| 612 | }; \ |
| 613 | \ |
| 614 | # NOTE: requires FW 235.2.86.0 and kernel drivers on the host: \ |
| 615 | # bnxt-en-dkms=1.10.3.235.2.86.0 bnxt-re-dkms=235.2.86.0 (from packages.broadcom.com PPA) \ |
| 616 | install_bnxt() { \ |
| 617 | install -m 0755 -d /etc/apt/keyrings; \ |
| 618 | curl -fsSL https://packages.broadcom.com/artifactory/api/security/keypair/PackagesKey/public \ |
| 619 | -o /etc/apt/keyrings/broadcom-nic.asc; \ |
| 620 | chmod a+r /etc/apt/keyrings/broadcom-nic.asc; \ |
| 621 | echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/broadcom-nic.asc] https://packages.broadcom.com/artifactory/ethernet-nic-debian-public jammy main" \ |
| 622 | > /etc/apt/sources.list.d/broadcom-nic.list; \ |
| 623 | apt-get update && apt-get install -y --no-install-recommends \ |
| 624 | bnxt-rocelib=235.2.86.0 \ |
| 625 | ; \ |
| 626 | cp -a /usr/local/lib/x86_64-linux-gnu/libbnxt_re* /usr/local/lib/; \ |
| 627 | ldconfig; \ |
| 628 | rm -rf /var/lib/apt/lists/*; \ |
| 629 | }; \ |
| 630 | \ |
| 631 | echo "[MORI] Install MoRI proxy deps"; \ |
| 632 | pip install --quiet --ignore-installed blinker && \ |
| 633 | pip install --quiet quart msgpack aiohttp pyzmq; \ |
| 634 | echo "[MORI] NIC_BACKEND=${NIC_BACKEND}"; \ |
| 635 | \ |
| 636 | # NIC backend deps — mori auto-detects NIC at runtime (MORI_DEVICE_NIC env var override). \ |
| 637 | # Only vendor packages are installed here for dlopen; no compile-time flags needed. \ |
| 638 | case "${NIC_BACKEND}" in \ |
| 639 | none) ;; \ |
| 640 | all) install_ainic; install_bnxt ;; \ |
| 641 | ainic) install_ainic ;; \ |
| 642 | bnxt) install_bnxt ;; \ |
| 643 | *) echo "ERROR: unknown NIC_BACKEND=${NIC_BACKEND}. Use one of: none, ainic, bnxt, all"; exit 2 ;; \ |
| 644 | esac' |
| 645 | |
| 646 | # ----------------------- |
| 647 | # vLLM wheel release build stage (for building distributable wheels) |
| 648 | # This stage pins dependencies to custom ROCm wheel versions and handles version detection |
| 649 | FROM fetch_vllm AS build_vllm_wheel_release |
| 650 | |
| 651 | ARG COMMON_WORKDIR |
| 652 | ARG ROCM_TRITON_KERNELS_COMMIT |
| 653 | COPY --from=vllm-version /vllm-version.txt /tmp/vllm-version.txt |
| 654 | COPY --from=rocm-triton-kernels \ |
| 655 | /opt/rocm-triton-kernels /opt/rocm-triton-kernels |
| 656 | ENV TRITON_KERNELS_SRC_DIR=/opt/rocm-triton-kernels |
| 657 | |
| 658 | # Drop the pre-built Rust artifacts into the source tree. setup.py detects |
| 659 | # them and ships them as-is, skipping the local Rust build. |
| 660 | COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/vllm-rs ${COMMON_WORKDIR}/vllm/vllm/vllm-rs |
| 661 | COPY --from=rust-build ${COMMON_WORKDIR}/vllm/vllm/_rust_*.so ${COMMON_WORKDIR}/vllm/vllm/ |
| 662 | |
| 663 | # Create /install directory for custom wheels |
| 664 | RUN mkdir -p /install |
| 665 | |
| 666 | # Copy custom ROCm wheels from docker/context if they exist |
| 667 | # COPY ensures Docker cache is invalidated when wheels change |
| 668 | # .keep file ensures directory always exists for COPY to work |
| 669 | COPY docker/context/base-wheels/ /tmp/base-wheels/ |
| 670 | # This is how we know if we are building for a wheel release or not. |
| 671 | # If there are not wheels found there, we are not building for a wheel release. |
| 672 | # So we exit with an error. To skip this stage. |
| 673 | RUN if [ -n "$(ls /tmp/base-wheels/*.whl 2>/dev/null)" ]; then \ |
| 674 | echo "Found custom wheels - copying to /install"; \ |
| 675 | cp /tmp/base-wheels/*.whl /install/ && \ |
| 676 | echo "Copied custom wheels:"; \ |
| 677 | ls -lh /install/; \ |
| 678 | else \ |
| 679 | echo "ERROR: No custom wheels found in docker/context/base-wheels/"; \ |
| 680 | echo "Wheel releases require pre-built ROCm wheels."; \ |
| 681 | exit 1; \ |
| 682 | fi |
| 683 | |
| 684 | # GIT_REPO_CHECK: Verify repo is clean and tags are available (for release builds) |
| 685 | # This matches CUDA's Dockerfile behavior for proper version detection via setuptools_scm |
| 686 | ARG GIT_REPO_CHECK=0 |
| 687 | RUN if [ "$GIT_REPO_CHECK" != "0" ]; then \ |
| 688 | echo "Running repository checks..."; \ |
| 689 | cd vllm && bash tools/check_repo.sh; \ |
| 690 | fi |
| 691 | |
| 692 | # Install the packaging helpers before modifying requirements/rocm.txt. |
| 693 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 694 | grep -Fq "set(TRITON_KERNELS_TAG \"${ROCM_TRITON_KERNELS_COMMIT}\")" \ |
| 695 | vllm/cmake/external_projects/triton_kernels.cmake \ |
| 696 | && cd vllm \ |
| 697 | && uv pip install --system setuptools_scm regex |
| 698 | |
| 699 | # Fail if git-based package dependencies are found in requirements files |
| 700 | # (uv doesn't handle git+ URLs well, and packages should be distributed on PyPI) |
| 701 | # Extra notes: pip install is able to handle git+ URLs, but uv doesn't. |
| 702 | RUN echo "Checking for git-based packages in requirements files..." \ |
| 703 | && echo "Checking common.txt for git-based packages:" \ |
| 704 | && if grep -q 'git+' ${COMMON_WORKDIR}/vllm/requirements/common.txt; then \ |
| 705 | echo "ERROR: Git-based packages found in common.txt:"; \ |
| 706 | grep 'git+' ${COMMON_WORKDIR}/vllm/requirements/common.txt; \ |
| 707 | echo "Please publish these packages to PyPI instead of using git dependencies."; \ |
| 708 | exit 1; \ |
| 709 | else \ |
| 710 | echo " ✓ No git-based packages found in common.txt"; \ |
| 711 | fi \ |
| 712 | && echo "Checking rocm.txt for git-based packages:" \ |
| 713 | && if grep -q 'git+' ${COMMON_WORKDIR}/vllm/requirements/rocm.txt; then \ |
| 714 | echo "ERROR: Git-based packages found in rocm.txt:"; \ |
| 715 | grep 'git+' ${COMMON_WORKDIR}/vllm/requirements/rocm.txt; \ |
| 716 | echo "Please publish these packages to PyPI instead of using git dependencies."; \ |
| 717 | exit 1; \ |
| 718 | else \ |
| 719 | echo " ✓ No git-based packages found in rocm.txt"; \ |
| 720 | fi \ |
| 721 | && echo "All requirements files are clean - no git-based packages found" |
| 722 | |
| 723 | # Pin vLLM dependencies to exact versions of custom ROCm wheels |
| 724 | # This ensures 'pip install vllm' automatically installs correct torch/triton/torchvision/amdsmi |
| 725 | COPY tools/vllm-rocm/pin_rocm_dependencies.py /tmp/pin_rocm_dependencies.py |
| 726 | RUN echo "Pinning vLLM dependencies to custom wheel versions..." \ |
| 727 | && python3 /tmp/pin_rocm_dependencies.py /install ${COMMON_WORKDIR}/vllm/requirements/rocm.txt |
| 728 | |
| 729 | # Install dependencies using custom wheels from /install |
| 730 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 731 | cd vllm \ |
| 732 | && echo "Building vLLM with custom wheels from /install" \ |
| 733 | && uv pip install --system --find-links /install -r requirements/rocm.txt |
| 734 | |
| 735 | # Build wheel using the shared source version to avoid dirty state from modified requirements/rocm.txt |
| 736 | # (setup.py auto-detects ccache/sccache in PATH) |
| 737 | RUN --mount=type=cache,id=vllm-rocm-ccache,target=/root/.cache/ccache \ |
| 738 | cd vllm \ |
| 739 | && export CCACHE_BASEDIR="$PWD" \ |
| 740 | && export SETUPTOOLS_SCM_PRETEND_VERSION="$(cat /tmp/vllm-version.txt)" \ |
| 741 | && echo "Building wheel with version: ${SETUPTOOLS_SCM_PRETEND_VERSION}" \ |
| 742 | && MAX_JOBS="${MAX_JOBS:-$(nproc)}" python3 setup.py bdist_wheel --dist-dir=dist |
| 743 | |
| 744 | FROM scratch AS export_vllm_wheel_release |
| 745 | ARG COMMON_WORKDIR |
| 746 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/dist/*.whl / |
| 747 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/requirements /requirements |
| 748 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/benchmarks /benchmarks |
| 749 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/tests /tests |
| 750 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/examples /examples |
| 751 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/tools /tools |
| 752 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/Dockerfile /docker/Dockerfile |
| 753 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/Dockerfile.cpu /docker/Dockerfile.cpu |
| 754 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/Dockerfile.rocm /docker/ |
| 755 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/Dockerfile.rocm_base /docker/Dockerfile.rocm_base |
| 756 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/ci-rocm.hcl /docker/ci-rocm.hcl |
| 757 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/docker-bake.hcl /docker/docker-bake.hcl |
| 758 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/docker/docker-bake-rocm.hcl /docker/docker-bake-rocm.hcl |
| 759 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/.buildkite /.buildkite |
| 760 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/pyproject.toml /pyproject.toml |
| 761 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/rust /rust |
| 762 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/rust-toolchain.toml /rust-toolchain.toml |
| 763 | COPY --from=build_vllm_wheel_release ${COMMON_WORKDIR}/vllm/vllm/v1 /vllm_v1 |
| 764 | |
| 765 | # ----------------------- |
| 766 | # CI base image (Tier 1) - stable, rarely changing CI dependencies. |
| 767 | # Per-PR test builds pull this as CI_BASE_IMAGE so the test stage only layers |
| 768 | # in the vLLM artifacts for the current commit. |
| 769 | FROM mori_base AS ci_base |
| 770 | ARG COMMON_WORKDIR |
| 771 | |
| 772 | ENV CARGO_HOME=/root/.cargo |
| 773 | ENV RUSTUP_HOME=/root/.rustup |
| 774 | ENV PATH=${CARGO_HOME}/bin:${PATH} |
| 775 | |
| 776 | # Bake the Rust toolchain into ci_base so Rust build/test steps do not |
| 777 | # need to fetch it on each per-commit image build. |
| 778 | COPY --from=rust-toolchain /root/.cargo /root/.cargo |
| 779 | COPY --from=rust-toolchain /root/.rustup /root/.rustup |
| 780 | |
| 781 | # Update rdma-core to support latest rocshmem. |
| 782 | ARG DEEPEP_NIC |
| 783 | RUN if [ "${DEEPEP_NIC}" = "cx7" ] || [ "${DEEPEP_NIC}" = "io" ]; then \ |
| 784 | git clone --branch v62.0 --depth 1 https://github.com/linux-rdma/rdma-core.git /tmp/rdma-core && \ |
| 785 | cd /tmp/rdma-core && \ |
| 786 | mkdir -p build && cd build && \ |
| 787 | cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr -DNO_MAN_PAGES=1 .. && \ |
| 788 | ninja && ninja install && ldconfig && rm -rf /tmp/rdma-core; \ |
| 789 | fi |
| 790 | |
| 791 | # Install NIXL + DeepEP wheels. |
| 792 | RUN --mount=type=bind,from=build_nixl,src=/app/install,target=/nixl_install \ |
| 793 | --mount=type=bind,from=build_deepep,src=/app/deep_install,target=/deep_install \ |
| 794 | uv pip install --system /nixl_install/*.whl /deep_install/*.whl |
| 795 | |
| 796 | # Copy ROCShmem runtime libraries. |
| 797 | COPY --from=build_rocshmem /opt/rocshmem /opt/rocshmem |
| 798 | |
| 799 | # RDMA userspace libraries plus FFmpeg dev libs needed by torchcodec. |
| 800 | RUN timeout --kill-after=30s 5m apt-get update -q -y --error-on=any \ |
| 801 | -o Acquire::Retries=3 \ |
| 802 | -o Acquire::http::Timeout=30 \ |
| 803 | -o Acquire::https::Timeout=30 \ |
| 804 | && apt-get install -q -y --no-install-recommends \ |
| 805 | -o Acquire::Retries=3 \ |
| 806 | -o Acquire::http::Timeout=30 \ |
| 807 | -o Acquire::https::Timeout=30 \ |
| 808 | librdmacm1 \ |
| 809 | libibverbs1 \ |
| 810 | ibverbs-providers \ |
| 811 | ibverbs-utils \ |
| 812 | util-linux \ |
| 813 | unzip \ |
| 814 | libsodium23 \ |
| 815 | pkg-config ffmpeg libavcodec-dev libavformat-dev libavutil-dev \ |
| 816 | libswscale-dev libavdevice-dev libavfilter-dev libswresample-dev \ |
| 817 | && rm -rf /var/lib/apt/lists/* |
| 818 | |
| 819 | # Install torchcodec from source for ROCm/torch ABI compatibility. |
| 820 | COPY tools/install_torchcodec_rocm.sh /tmp/install_torchcodec.sh |
| 821 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 822 | --mount=type=cache,target=/root/.cache/pip \ |
| 823 | --mount=type=cache,target=/root/.cache/torchcodec-wheels \ |
| 824 | bash /tmp/install_torchcodec.sh \ |
| 825 | && rm /tmp/install_torchcodec.sh \ |
| 826 | && apt-get clean && rm -rf /var/lib/apt/lists/* |
| 827 | |
| 828 | # Pre-install shared ROCm runtime dependencies. |
| 829 | COPY requirements/common.txt requirements/rocm.txt /tmp/ci-base-requirements/ |
| 830 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 831 | uv pip install --system -r /tmp/ci-base-requirements/rocm.txt \ |
| 832 | && rm -rf /tmp/ci-base-requirements |
| 833 | |
| 834 | # Enable fast and less brittle model downloads in tests. |
| 835 | ENV HF_XET_HIGH_PERFORMANCE=1 |
| 836 | ENV HF_HUB_DOWNLOAD_TIMEOUT=60 |
| 837 | |
| 838 | # Keep torch.cuda.is_available() fork-safe (see vllm/env_override.py). |
| 839 | ENV PYTORCH_NVML_BASED_CUDA_CHECK=1 |
| 840 | |
| 841 | # Pre-install vLLM test dependencies. |
| 842 | COPY requirements/test/rocm.txt /tmp/rocm-test-reqs.txt |
| 843 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 844 | uv pip install --system -r /tmp/rocm-test-reqs.txt |
| 845 | |
| 846 | # Rebuild fastsafetensors from source so its C++ extension is compiled with |
| 847 | # USE_ROCM and can detect libamdhip64.so at runtime. |
| 848 | RUN --mount=type=cache,target=/root/.cache/pip \ |
| 849 | FASTSAFETENSORS_REQ="$(grep -E '^fastsafetensors(==| @ )' /tmp/rocm-test-reqs.txt | head -1)" \ |
| 850 | && test -n "${FASTSAFETENSORS_REQ}" \ |
| 851 | && python3 -m pip install --force-reinstall --no-deps \ |
| 852 | --no-binary fastsafetensors "${FASTSAFETENSORS_REQ}" \ |
| 853 | && rm /tmp/rocm-test-reqs.txt |
| 854 | |
| 855 | # Set MIOPEN ENVS to resolve performance regressions in MIOpen 3D convolution kernel. |
| 856 | # See: https://github.com/pytorch/pytorch/issues/169857 |
| 857 | ENV MIOPEN_DEBUG_CONV_DIRECT=0 |
| 858 | ENV MIOPEN_DEBUG_CONV_GEMM=0 |
| 859 | |
| 860 | # Use legacy IPC mode for HSA to avoid GPU memory pinning issues with UCX rocm_ipc. |
| 861 | # See: https://github.com/ROCm/rocm-libraries/issues/6266 |
| 862 | ENV HSA_ENABLE_IPC_MODE_LEGACY=1 |
| 863 | ENV UCX_RMA_PPLN_ENABLE=y |
| 864 | ENV UCX_ROCM_COPY_SIGPOOL_MAX_ELEMS=inf |
| 865 | |
| 866 | # ROCm profiler limits workaround. |
| 867 | RUN echo "ROCTRACER_MAX_EVENTS=10000000" > ${COMMON_WORKDIR}/libkineto.conf |
| 868 | ENV KINETO_CONFIG="${COMMON_WORKDIR}/libkineto.conf" |
| 869 | |
| 870 | # Install vllm_test_utils in ci_base for ci_base + wheel parity. |
| 871 | COPY tests/vllm_test_utils /tmp/vllm_test_utils |
| 872 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 873 | uv pip install --system /tmp/vllm_test_utils \ |
| 874 | && rm -rf /tmp/vllm_test_utils |
| 875 | |
| 876 | RUN --mount=type=bind,from=build_lmcache,src=/app/install,target=/lmcache_install \ |
| 877 | --mount=type=bind,source=requirements/test/rocm.txt,target=/tmp/lmcache-constraints.txt \ |
| 878 | --mount=type=cache,target=/root/.cache/uv \ |
| 879 | uv pip install --system --constraint /tmp/lmcache-constraints.txt \ |
| 880 | /lmcache_install/*.whl \ |
| 881 | && python3 -c "import lmcache, lmcache.c_ops; print('lmcache', lmcache.__version__)" |
| 882 | |
| 883 | # ----------------------- |
| 884 | # Test vLLM image (Tier 2) - vLLM-only layer on top of ci_base. |
| 885 | FROM ${CI_BASE_IMAGE} AS test |
| 886 | ARG COMMON_WORKDIR |
| 887 | |
| 888 | # Install the vLLM wheel (--no-deps: all deps already in ci_base). |
| 889 | RUN --mount=type=bind,from=export_vllm,src=/,target=/install \ |
| 890 | --mount=type=cache,target=/root/.cache/uv \ |
| 891 | cd /install \ |
| 892 | && uv pip install --system --no-deps *.whl |
| 893 | |
| 894 | # Store the vLLM wheel in the image for python-only install tests. |
| 895 | COPY --from=export_vllm /*.whl /opt/vllm-wheels/ |
| 896 | |
| 897 | WORKDIR /vllm-workspace |
| 898 | COPY --from=build_vllm ${COMMON_WORKDIR}/vllm /vllm-workspace |
| 899 | |
| 900 | # Copy in the v1 package (for python-only install test group). |
| 901 | COPY --from=export_vllm /vllm_v1 /usr/local/lib/python${PYTHON_VERSION}/dist-packages/vllm/v1 |
| 902 | |
| 903 | # Hide source under src/ so it won't shadow the installed package in tests. |
| 904 | RUN mkdir src && mv vllm src/vllm |
| 905 | |
| 906 | # Catch GPU<->CPU syncs in execute_model/sample_tokens during tests. |
| 907 | ENV VLLM_GPU_SYNC_CHECK=error |
| 908 | |
| 909 | # Run the structural smoke test inside BuildKit, where the test image layers |
| 910 | # are already available. Export only a build-scoped success marker. |
| 911 | FROM test AS test_smoke |
| 912 | ARG ROCM_SMOKE_ID |
| 913 | RUN --network=none \ |
| 914 | bash /vllm-workspace/.buildkite/scripts/rocm/smoke-test-image.sh --inside \ |
| 915 | && printf '%s\n' "${ROCM_SMOKE_ID:-local}" > /vllm-smoke-ok |
| 916 | |
| 917 | FROM scratch AS export_test_smoke |
| 918 | COPY --from=test_smoke /vllm-smoke-ok / |
| 919 | |
| 920 | # ----------------------- |
| 921 | # Final vLLM image |
| 922 | FROM mori_base AS final_common |
| 923 | ENV UV_COMPILE_BYTECODE=1 |
| 924 | |
| 925 | RUN python3 -m pip install --upgrade pip && rm -rf /var/lib/apt/lists/* |
| 926 | |
| 927 | # Clean up sccache from release image (not needed at runtime) |
| 928 | # This removes the binary and wrappers that may have been installed during build |
| 929 | RUN rm -f /usr/bin/sccache || true \ |
| 930 | && rm -rf /opt/sccache-wrappers || true |
| 931 | |
| 932 | # Unset sccache environment variables for the release image |
| 933 | # This prevents S3 bucket config from leaking into production images |
| 934 | ENV SCCACHE_BUCKET= |
| 935 | ENV SCCACHE_REGION= |
| 936 | ENV SCCACHE_ENDPOINT= |
| 937 | ENV SCCACHE_S3_NO_CREDENTIALS= |
| 938 | ENV SCCACHE_IDLE_TIMEOUT= |
| 939 | |
| 940 | # Error related to odd state for numpy 1.20.3 where there is no METADATA etc, but an extra LICENSES_bundled.txt. |
| 941 | # Manually remove it so that later steps of numpy upgrade can continue |
| 942 | RUN case "$(which python3)" in \ |
| 943 | *"/opt/conda/envs/py_3.9"*) \ |
| 944 | rm -rf /opt/conda/envs/py_3.9/lib/python3.9/site-packages/numpy-1.20.3.dist-info/;; \ |
| 945 | *) ;; esac |
| 946 | |
| 947 | RUN --mount=type=cache,target=/root/.cache/uv \ |
| 948 | uv pip install --system --upgrade huggingface-hub |
| 949 | |
| 950 | # Install vLLM using uv (inherited from base stage) |
| 951 | # Note: No -U flag to avoid upgrading PyTorch ROCm to CUDA version |
| 952 | # Keep OpenTelemetry within LMCache's cap. |
| 953 | RUN --mount=type=bind,from=export_vllm,src=/,target=/install \ |
| 954 | --mount=type=cache,target=/root/.cache/uv \ |
| 955 | cd /install \ |
| 956 | && echo "opentelemetry-api<=1.40.0" > /tmp/otel-constraints.txt \ |
| 957 | && uv pip install --system -c /tmp/otel-constraints.txt -r requirements/rocm.txt \ |
| 958 | && pip uninstall -y vllm \ |
| 959 | && uv pip install --system -c /tmp/otel-constraints.txt *.whl \ |
| 960 | && rm /tmp/otel-constraints.txt |
| 961 | |
| 962 | # Install NIXL ROCm wheel |
| 963 | RUN --mount=type=bind,from=build_nixl,src=/app/install,target=/nixl_install \ |
| 964 | uv pip install --system /nixl_install/*.whl |
| 965 | |
| 966 | ARG COMMON_WORKDIR |
| 967 | ARG BASE_IMAGE |
| 968 | ARG NIC_BACKEND |
| 969 | ARG AINIC_VERSION |
| 970 | |
| 971 | # Copy over the benchmark scripts as well |
| 972 | COPY --from=export_vllm /benchmarks ${COMMON_WORKDIR}/vllm/benchmarks |
| 973 | COPY --from=export_vllm /examples ${COMMON_WORKDIR}/vllm/examples |
| 974 | COPY --from=export_vllm /docker ${COMMON_WORKDIR}/vllm/docker |
| 975 | |
| 976 | # Use legacy IPC mode for HSA to avoid GPU memory pinning issues with UCX rocm_ipc |
| 977 | # See: https://github.com/ROCm/rocm-libraries/issues/6266 |
| 978 | ENV HSA_ENABLE_IPC_MODE_LEGACY=1 |
| 979 | ENV UCX_RMA_PPLN_ENABLE=y |
| 980 | ENV UCX_ROCM_COPY_SIGPOOL_MAX_ELEMS=inf |
| 981 | |
| 982 | ENV TOKENIZERS_PARALLELISM=false |
| 983 | |
| 984 | # ENV that can improve safe tensor loading, and end-to-end time |
| 985 | ENV SAFETENSORS_FAST_GPU=1 |
| 986 | |
| 987 | # Performance environment variable. |
| 988 | ENV HIP_FORCE_DEV_KERNARG=1 |
| 989 | |
| 990 | # Keep torch.cuda.is_available() fork-safe (see vllm/env_override.py). |
| 991 | ENV PYTORCH_NVML_BASED_CUDA_CHECK=1 |
| 992 | |
| 993 | # Workaround for ROCm profiler limits |
| 994 | RUN echo "ROCTRACER_MAX_EVENTS=10000000" > ${COMMON_WORKDIR}/libkineto.conf |
| 995 | ENV KINETO_CONFIG="${COMMON_WORKDIR}/libkineto.conf" |
| 996 | RUN echo "VLLM_BASE_IMAGE=${BASE_IMAGE}" >> ${COMMON_WORKDIR}/versions.txt \ |
| 997 | && echo "MORI_NIC_BACKEND=${NIC_BACKEND}" >> ${COMMON_WORKDIR}/versions.txt \ |
| 998 | && echo "AINIC_VERSION=${AINIC_VERSION}" >> ${COMMON_WORKDIR}/versions.txt |
| 999 | |
| 1000 | CMD ["/bin/bash"] |
| 1001 | |
| 1002 | FROM final_common AS final_lmcache_false |
| 1003 | |
| 1004 | FROM final_common AS final_lmcache_true |
| 1005 | RUN --mount=type=bind,from=build_lmcache,src=/app/install,target=/lmcache_install \ |
| 1006 | --mount=type=cache,target=/root/.cache/uv \ |
| 1007 | uv pip install --system /lmcache_install/*.whl \ |
| 1008 | && test "$(python3 -c 'import numpy; print(numpy.__version__)')" = "2.3.5" \ |
| 1009 | && uv pip check --system \ |
| 1010 | && python3 -c "import lmcache, lmcache.c_ops; print('lmcache', lmcache.__version__)" |
| 1011 | |
| 1012 | FROM final_lmcache_${INSTALL_LMCACHE} AS final |
| 1013 | |
| 1014 | #Set entrypoint for vllm-openai official images |
| 1015 | FROM final AS vllm-openai |
| 1016 | ENTRYPOINT ["vllm", "serve"] |