[CI/Build] Fix flaky failures in CPU CI image building (#55317)
Signed-off-by: jiang1.li <jiang1.li@intel.com>
Li, Jiang committed
Sep 4, 2026 at 20:27 UTC
8340fe1bb99c187ebec08ad03bbe0d06b45d1d40
4 files changed
+67
-13
.buildkite/scripts/hardware_ci/run-cpu-test.sh
+33
-4
@@ -15,12 +15,15 @@ TEST_COMMAND=$2
15
# Disk hygiene knobs. Reclaim space only once the Docker root filesystem crosses
16
# DISK_USAGE_THRESHOLD percent, and cap the shared BuildKit cache at
17
# BUILDKIT_CACHE_MAX so subsequent builds keep reusing the hottest layers.
18
-DISK_USAGE_THRESHOLD=${DISK_USAGE_THRESHOLD:-70}
19
-BUILDKIT_CACHE_MAX=${BUILDKIT_CACHE_MAX:-80GB}
18
+DISK_USAGE_THRESHOLD=${DISK_USAGE_THRESHOLD:-80}
19
+BUILDKIT_CACHE_MAX=${BUILDKIT_CACHE_MAX:-200GB}
20
21
# Reclaim disk only when the host is under pressure. We trim (not purge) the
22
# shared BuildKit cache so cross-job/cross-agent reuse stays intact, and only
23
# touch dangling images; other agents' uniquely tagged images are left alone.
24
+# Cache mounts (exec.cachemount: uv/cargo/apt) are excluded so they survive
25
+# pruning -- they're what let installs reuse packages instead of hitting the
26
+# network, and are otherwise reclaimable like any other build cache record.
27
prune_if_disk_pressure() {
28
local docker_root disk_usage
29
docker_root=$(docker info -f '{{.DockerRootDir}}' 2>/dev/null || true)
@@ -31,7 +34,7 @@ prune_if_disk_pressure() {
34
if [ "${disk_usage:-0}" -gt "$DISK_USAGE_THRESHOLD" ]; then
35
echo "--- :broom: Disk usage ${disk_usage}% exceeds ${DISK_USAGE_THRESHOLD}%, reclaiming space"
36
docker image prune -f || true
34
- docker builder prune -f --keep-storage="$BUILDKIT_CACHE_MAX" || true
37
+ docker builder prune -f --keep-storage="$BUILDKIT_CACHE_MAX" --filter type!=exec.cachemount || true
38
else
39
echo "Disk usage ${disk_usage:-unknown}% within ${DISK_USAGE_THRESHOLD}% threshold; skipping prune"
40
fi
@@ -50,9 +53,35 @@ trap cleanup EXIT
53
# Free space up front so a nearly-full host doesn't fail the build.
54
prune_if_disk_pressure
55
56
+# Opportunistically warm the local base image cache; `docker build` below
57
+# omits `--pull` so it already prefers a cached image over the network. A
58
+# single attempt only -- the retry loop below covers a miss here too.
59
+echo "--- :docker: Pre-fetching base image"
60
+docker pull ubuntu:22.04 || true
61
+
62
# building the docker image
63
echo "--- :docker: Building Docker image"
55
-docker build --progress plain --tag "$IMAGE_NAME" --target vllm-test -f docker/Dockerfile.cpu .
64
+BUILD_RETRY_PATTERN='dial tcp|i/o timeout|failed to authorize|TLS handshake timeout|connection reset|Could not resolve host|Temporary failure in name resolution|dns error: failed to lookup address information|client error \(Connect\)|error sending request for url|Failed to fetch:'
65
+BUILD_MAX_ATTEMPTS=4 # 1 initial + 3 retries
66
+BUILD_RETRY_WAITS=(10 20 40) # seconds to wait before retry 1/2/3
67
+build_log="$(mktemp)"
68
+attempt=1
69
+while true; do
70
+ if docker build --progress plain --tag "$IMAGE_NAME" --target vllm-test \
71
+ --build-arg USE_SCCACHE=1 --build-arg SCCACHE_LOCAL_ONLY=1 \
72
+ -f docker/Dockerfile.cpu . 2>&1 | tee "$build_log"; then
73
+ break
74
+ fi
75
+ if [ "$attempt" -ge "$BUILD_MAX_ATTEMPTS" ] || ! grep -qE "$BUILD_RETRY_PATTERN" "$build_log"; then
76
+ rm -f "$build_log"
77
+ exit 1
78
+ fi
79
+ wait_s="${BUILD_RETRY_WAITS[$((attempt - 1))]}"
80
+ echo "--- :docker: Transient network error during build (attempt $attempt/$BUILD_MAX_ATTEMPTS), retrying in ${wait_s}s"
81
+ sleep "$wait_s"
82
+ attempt=$((attempt + 1))
83
+done
84
+rm -f "$build_log"
85
86
# Run the image, setting --shm-size=4g for tensor parallel.
87
docker run --rm --cpuset-cpus="$CORE_RANGE" --cpuset-mems="$NUMA_NODE" -v ~/.cache/huggingface:/root/.cache/huggingface --privileged=true -e HF_TOKEN -e VLLM_CPU_KVCACHE_SPACE=16 -e VLLM_CPU_CI_ENV=1 -e VLLM_CPU_SIM_MULTI_NUMA=1 -e VLLM_CPU_ATTN_SPLIT_KV=0 --shm-size=4g "$IMAGE_NAME" \
csrc/cpu/sgl-kernels/mla_cache.cpp
+3
-3
@@ -59,9 +59,9 @@ void concat_and_cache_mla_kernel_impl(
59
60
// kv_c_normed : [num_tokens, kv_lora_rank]
61
// k_pe : [num_tokens, qk_rope_head_dim] or [num_tokens, 1,
62
-// qk_rope_head_dim] kv_cache : [num_blocks, block_size, kv_lora_rank +
63
-// qk_rope_head_dim] slot_mapping: [num_tokens] int32/int64, absolute physical
64
-// row index
62
+// qk_rope_head_dim]
63
+// kv_cache : [num_blocks, block_size, kv_lora_rank + qk_rope_head_dim]
64
+// slot_mapping: [num_tokens] int32/int64, absolute physical row index
65
// (block_id * block_size + block_offset); negative entries are
66
// skipped (padded tokens), matching `concat_and_cache_mla`.
67
void concat_and_cache_mla_cpu(const at::Tensor& kv_c_normed,
docker/Dockerfile.cpu
+27
-3
@@ -165,6 +165,9 @@ ARG USE_SCCACHE
165
ARG SCCACHE_ENDPOINT
166
ARG SCCACHE_BUCKET_NAME=vllm-build-sccache
167
ARG SCCACHE_REGION_NAME=us-west-2
168
+# When set, sccache skips the S3 backend and uses a local disk cache instead
169
+# (for builds without S3 credentials, e.g. a developer's local `docker build`).
170
+ARG SCCACHE_LOCAL_ONLY=0
171
172
ENV SCCACHE_BUCKET=${USE_SCCACHE:+${SCCACHE_BUCKET_NAME}}
173
ENV SCCACHE_REGION=${USE_SCCACHE:+${SCCACHE_REGION_NAME}}
@@ -186,10 +189,16 @@ ENV CARGO_BUILD_JOBS=4
189
# inputs. The child stage below relinks the crates that embed the exact version.
190
RUN --mount=type=cache,target=/root/.cargo/registry,sharing=locked \
191
--mount=type=cache,target=/root/.cargo/git,sharing=locked \
192
+ --mount=type=cache,target=/root/.cache/sccache-local,sharing=locked \
193
--mount=type=secret,id=aws-credentials,target=/root/.aws/credentials,required=false \
194
if [ "$USE_SCCACHE" = "1" ]; then \
195
export RUSTC_WRAPPER=sccache; \
192
- if [ -n "${SCCACHE_ENDPOINT}" ]; then export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; fi; \
196
+ if [ "$SCCACHE_LOCAL_ONLY" = "1" ]; then \
197
+ unset SCCACHE_BUCKET SCCACHE_REGION; \
198
+ export SCCACHE_DIR=/root/.cache/sccache-local SCCACHE_CACHE_SIZE=20G; \
199
+ elif [ -n "${SCCACHE_ENDPOINT}" ]; then \
200
+ export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; \
201
+ fi; \
202
sccache --show-stats; \
203
fi && \
204
SETUPTOOLS_SCM_PRETEND_VERSION="0.0.0+docker.cache" bash build_rust.sh && \
@@ -272,13 +281,20 @@ FROM vllm-src AS vllm-build
281
282
ARG USE_SCCACHE
283
ARG SCCACHE_ENDPOINT
284
+ARG SCCACHE_LOCAL_ONLY=0
285
286
RUN --mount=type=cache,target=/root/.cache/uv \
287
--mount=type=cache,target=/root/.cache/ccache \
288
+ --mount=type=cache,target=/root/.cache/sccache-local,sharing=locked \
289
--mount=type=cache,target=/vllm-workspace/.deps,sharing=locked \
290
--mount=type=secret,id=aws-credentials,target=/root/.aws/credentials,required=false \
291
if [ "$USE_SCCACHE" = "1" ]; then \
281
- if [ -n "${SCCACHE_ENDPOINT}" ]; then export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; fi; \
292
+ if [ "$SCCACHE_LOCAL_ONLY" = "1" ]; then \
293
+ unset SCCACHE_BUCKET SCCACHE_REGION; \
294
+ export SCCACHE_DIR=/root/.cache/sccache-local SCCACHE_CACHE_SIZE=20G; \
295
+ elif [ -n "${SCCACHE_ENDPOINT}" ]; then \
296
+ export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; \
297
+ fi; \
298
sccache --show-stats; \
299
fi && \
300
VLLM_TARGET_DEVICE=cpu python3 setup.py bdist_wheel --dist-dir=dist --py-limited-api=cp38 && \
@@ -296,6 +312,7 @@ ARG VLLM_CPU_X86=0
312
313
ARG USE_SCCACHE
314
ARG SCCACHE_ENDPOINT
315
+ARG SCCACHE_LOCAL_ONLY=0
316
317
WORKDIR /vllm-workspace
318
@@ -303,15 +320,22 @@ RUN mkdir dist
320
321
RUN --mount=type=cache,target=/root/.cache/uv \
322
--mount=type=cache,target=/root/.cache/ccache \
323
+ --mount=type=cache,target=/root/.cache/sccache-local,sharing=locked \
324
--mount=type=cache,target=/vllm-workspace/.deps,sharing=locked \
325
--mount=type=cache,target=/root/.triton \
326
--mount=type=secret,id=aws-credentials,target=/root/.aws/credentials,required=false \
327
+ set -ex; \
328
if [ "$TARGETARCH" = "amd64" ] || [ "$VLLM_CPU_X86" != "0" ]; then \
329
git clone --recurse-submodules --shallow-submodules --filter=blob:none "https://github.com/triton-lang/triton-cpu.git"; \
330
cd triton-cpu; \
331
git checkout "270e696d"; \
332
if [ "$USE_SCCACHE" = "1" ]; then \
314
- if [ -n "${SCCACHE_ENDPOINT}" ]; then export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; fi; \
333
+ if [ "$SCCACHE_LOCAL_ONLY" = "1" ]; then \
334
+ unset SCCACHE_BUCKET SCCACHE_REGION; \
335
+ export SCCACHE_DIR=/root/.cache/sccache-local SCCACHE_CACHE_SIZE=20G; \
336
+ elif [ -n "${SCCACHE_ENDPOINT}" ]; then \
337
+ export SCCACHE_ENDPOINT="${SCCACHE_ENDPOINT}"; \
338
+ fi; \
339
export TRITON_APPEND_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache"; \
340
sccache --show-stats; \
341
fi; \
tests/models/language/pooling/test_max_tokens_per_doc.py
+4
-3
@@ -14,8 +14,6 @@ import requests
14
from tests.utils import VLLM_PATH, RemoteOpenAIServer
15
from vllm.entrypoints.pooling.scoring.protocol import RerankResponse
16
17
-os.environ["VLLM_LOGGING_LEVEL"] = "WARNING"
18
-
17
TEMPLATE_DIR = str(VLLM_PATH / "examples/pooling/score/template")
18
ExpectedPromptTokens = int | tuple[int, ...]
19
@@ -128,7 +126,10 @@ def assert_prompt_tokens(actual: int, expected: ExpectedPromptTokens) -> None:
126
@pytest.fixture(scope="module", params=RERANK_CONFIGS, ids=lambda c: c.model)
127
def server(request):
128
config: TestConfig = request.param
131
- with RemoteOpenAIServer(config.model, config.args) as remote_server:
129
+ env_dict = {"VLLM_LOGGING_LEVEL": "WARNING"}
130
+ with RemoteOpenAIServer(
131
+ config.model, config.args, env_dict=env_dict
132
+ ) as remote_server:
133
yield config, remote_server
134
135