main
hcl 435 lines 17 KB
Raw
1 # ci-rocm.hcl - CI-specific configuration for vLLM ROCm Docker builds
2 #
3 # This file lives in the vLLM repo at docker/ci-rocm.hcl so ROCm Docker
4 # build mechanics can evolve with Dockerfile.rocm and docker-bake-rocm.hcl.
5 # Used with: docker buildx bake -f docker/docker-bake-rocm.hcl -f docker/ci-rocm.hcl test-rocm-ci
6 #
7 # Registry cache: Docker Hub (rocm/vllm-ci-cache) is used exclusively.
8 # AMD build agents already have Docker Hub credentials (they push the test
9 # image to rocm/vllm-ci), so no additional credential setup is required.
10 # ROCm CI uses Docker Hub for BuildKit layer cache by default. A separate
11 # compiler cache can be enabled with USE_SCCACHE=1 when AMD provides a shared
12 # S3-compatible cache endpoint.
13
14 # CI metadata
15
16 variable "BUILDKITE_COMMIT" {
17 default = ""
18 }
19
20 variable "BUILDKITE_BUILD_NUMBER" {
21 default = ""
22 }
23
24 variable "BUILDKITE_BUILD_ID" {
25 default = ""
26 }
27
28 variable "PARENT_COMMIT" {
29 default = ""
30 }
31
32 # Merge-base of HEAD with main - provides a more stable cache fallback than
33 # parent commit for long-lived PRs. Mirrors the VLLM_MERGE_BASE_COMMIT
34 # pattern used in the shared ci.hcl file. Auto-computed by ci-bake-rocm.sh
35 # when unset.
36 variable "VLLM_MERGE_BASE_COMMIT" {
37 default = ""
38 }
39
40 # Bridge to vLLM's COMMIT variable for OCI labels
41 variable "COMMIT" {
42 default = BUILDKITE_COMMIT
43 }
44
45 # Image tags (set by CI)
46
47 variable "IMAGE_TAG" {
48 default = ""
49 }
50
51 variable "IMAGE_TAG_LATEST" {
52 default = ""
53 }
54
55 # ROCm-specific GPU architecture targets
56
57 variable "PYTORCH_ROCM_ARCH" {
58 default = "gfx90a;gfx942;gfx950"
59 }
60
61 # Pre-built CI base image (Tier 1). Per-PR builds pull this instead of
62 # rebuilding NIXL/DeepEP/torchcodec from scratch. The ci_base stage in
63 # Dockerfile.rocm inherits from base, so CI_BASE_IMAGE only affects the test
64 # stage and is irrelevant when building --target ci_base itself.
65 variable "CI_BASE_IMAGE" {
66 default = "rocm/vllm-dev:ci_base"
67 }
68
69 # Leave CI_MAX_JOBS empty so the Dockerfile falls back to $(nproc) and uses
70 # the full builder parallelism. Operators can still override this per build.
71 variable "CI_MAX_JOBS" {
72 default = ""
73 }
74
75 # Upstream dependency commit pins -- extracted from Dockerfile.rocm by
76 # ci-bake-rocm.sh at build time. Empty defaults are safe: the cache
77 # functions produce no entries when the variable is empty.
78 variable "NIXL_BRANCH" {
79 default = ""
80 }
81
82 variable "UCX_BRANCH" {
83 default = ""
84 }
85
86 variable "ROCSHMEM_BRANCH" {
87 default = ""
88 }
89
90 variable "DEEPEP_BRANCH" {
91 default = ""
92 }
93
94 variable "NIXL_CACHE_KEY" {
95 default = ""
96 }
97
98 variable "ROCSHMEM_CACHE_KEY" {
99 default = ""
100 }
101
102 variable "DEEPEP_CACHE_KEY" {
103 default = ""
104 }
105
106 # Docker Hub registry cache for AMD builds.
107 #
108 # A separate repo (rocm/vllm-ci-cache) is used for BuildKit layer cache.
109 # Final-image cache exports use mode=min to reduce the volume of data pushed.
110 # Source-scoped csrc cache exports default to mode=max so fresh workers can
111 # recover more of the native build graph when ROCm extension inputs change.
112 # NOTE: mode=min still includes all layers referenced by the final image
113 # manifest, including inherited base layers (~7.25GB ROCm runtime).
114 # Docker Hub auto-creates the repo on first push.
115 #
116 # Final-image cache stays commit-scoped. Branch-to-branch reuse for the test
117 # image comes from importing the parent and merge-base commit cache refs.
118 #
119 # The source-scoped native cache is exported both per-commit and per-branch so
120 # ROCm extension rebuilds are shareable within the same commit reruns and across
121 # consecutive commits on the same branch without depending on a single global
122 # latest tag.
123
124 variable "DOCKERHUB_CACHE_REPO" {
125 default = "rocm/vllm-ci-cache"
126 }
127
128 variable "DOCKERHUB_CACHE_TO" {
129 default = ""
130 }
131
132 variable "ROCM_CACHE_BRANCH_TAG" {
133 default = ""
134 }
135
136 variable "ROCM_CACHE_UPSTREAM_BRANCH_TAG" {
137 default = ""
138 }
139
140 variable "ROCM_CSRC_CACHE_TO_MODE" {
141 default = "max"
142 }
143
144 variable "ROCM_RUST_CACHE_TO_MODE" {
145 default = "max"
146 }
147
148 variable "ROCM_FINAL_CACHE_TO_MODE" {
149 default = "min"
150 }
151
152 # Functions
153
154 function "get_cache_from_rocm" {
155 params = []
156 result = compact([
157 # Exact commit hit - fastest cache on re-runs of the same commit
158 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-${BUILDKITE_COMMIT}" : "",
159 # Parent commit - useful cache for incremental changes
160 PARENT_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-${PARENT_COMMIT}" : "",
161 # Merge-base with main - stable fallback for long-lived or rebased PRs;
162 # maps to a real main-branch commit whose cache layers are likely warm
163 VLLM_MERGE_BASE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-${VLLM_MERGE_BASE_COMMIT}" : "",
164 # Import the source-scoped native build cache as well so builds whose
165 # Python/package layers changed can still reuse compiled ROCm objects.
166 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${BUILDKITE_COMMIT}" : "",
167 PARENT_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${PARENT_COMMIT}" : "",
168 VLLM_MERGE_BASE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${VLLM_MERGE_BASE_COMMIT}" : "",
169 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-branch-${ROCM_CACHE_BRANCH_TAG}" : "",
170 ROCM_CACHE_UPSTREAM_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-branch-${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" : "",
171 # Import the source-scoped Rust frontend cache so non-Rust changes do not
172 # force a fresh cargo release build.
173 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${BUILDKITE_COMMIT}" : "",
174 PARENT_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${PARENT_COMMIT}" : "",
175 VLLM_MERGE_BASE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${VLLM_MERGE_BASE_COMMIT}" : "",
176 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-branch-${ROCM_CACHE_BRANCH_TAG}" : "",
177 ROCM_CACHE_UPSTREAM_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-branch-${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" : "",
178 # Branch-scoped full image cache - fallback when parent-commit cache is evicted
179 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-branch-${ROCM_CACHE_BRANCH_TAG}" : "",
180 ROCM_CACHE_UPSTREAM_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-branch-${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" : "",
181 ])
182 }
183
184 function "get_cache_to_rocm" {
185 params = []
186 result = compact([
187 # Commit-scoped cache for exact re-runs.
188 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-${BUILDKITE_COMMIT},mode=${ROCM_FINAL_CACHE_TO_MODE}" : "",
189 # Branch-scoped cache so later commits on the same branch can reuse the full
190 # image layers when the parent-commit cache is evicted. Unlike the old
191 # rocm-latest tag (which caused duplicate exporter 400s), this is per-branch.
192 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${ROCM_FINAL_CACHE_TO_MODE}" : "",
193 ])
194 }
195
196 function "get_cache_from_rocm_csrc" {
197 params = []
198 result = compact([
199 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${BUILDKITE_COMMIT}" : "",
200 PARENT_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${PARENT_COMMIT}" : "",
201 VLLM_MERGE_BASE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${VLLM_MERGE_BASE_COMMIT}" : "",
202 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-branch-${ROCM_CACHE_BRANCH_TAG}" : "",
203 ROCM_CACHE_UPSTREAM_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-branch-${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" : "",
204 ])
205 }
206
207 function "get_cache_to_rocm_csrc" {
208 params = []
209 result = compact([
210 # Export the exact-commit native cache for same-commit reruns.
211 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-${BUILDKITE_COMMIT},mode=${ROCM_CSRC_CACHE_TO_MODE}" : "",
212 # Export the branch-scoped native cache so later commits on the same branch
213 # can reuse compiled ROCm objects even when the exact parent cache is absent.
214 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:csrc-rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${ROCM_CSRC_CACHE_TO_MODE}" : "",
215 ])
216 }
217
218 function "get_cache_from_rocm_rust" {
219 params = []
220 result = compact([
221 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${BUILDKITE_COMMIT}" : "",
222 PARENT_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${PARENT_COMMIT}" : "",
223 VLLM_MERGE_BASE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${VLLM_MERGE_BASE_COMMIT}" : "",
224 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-branch-${ROCM_CACHE_BRANCH_TAG}" : "",
225 ROCM_CACHE_UPSTREAM_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-branch-${ROCM_CACHE_UPSTREAM_BRANCH_TAG}" : "",
226 ])
227 }
228
229 function "get_cache_to_rocm_rust" {
230 params = []
231 result = compact([
232 # Export exact-commit and branch-scoped Rust caches. A content-addressed
233 # cache ref is appended by ci-bake-rocm.sh when that wrapper is used.
234 BUILDKITE_COMMIT != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-${BUILDKITE_COMMIT},mode=${ROCM_RUST_CACHE_TO_MODE}" : "",
235 ROCM_CACHE_BRANCH_TAG != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rust-rocm-branch-${ROCM_CACHE_BRANCH_TAG},mode=${ROCM_RUST_CACHE_TO_MODE}" : "",
236 ])
237 }
238
239 # Cache functions for upstream dependency stages (NIXL/UCX, ROCShmem, DeepEP).
240 # These stages are pinned to specific upstream commit hashes, so cache keys use
241 # those hashes rather than the Buildkite commit. This means the cache persists
242 # across all vLLM commits as long as the upstream dependency pins don't change.
243
244 function "get_cache_from_rocm_deps" {
245 params = []
246 result = compact([
247 NIXL_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:nixl-rocm-${NIXL_CACHE_KEY}" : (NIXL_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:nixl-rocm-${NIXL_BRANCH}-ucx-${UCX_BRANCH}" : ""),
248 ROCSHMEM_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocshmem-rocm-${ROCSHMEM_CACHE_KEY}" : (ROCSHMEM_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocshmem-rocm-${ROCSHMEM_BRANCH}" : ""),
249 DEEPEP_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:deepep-rocm-${DEEPEP_CACHE_KEY}" : (DEEPEP_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:deepep-rocm-${DEEPEP_BRANCH}-rocshmem-${ROCSHMEM_BRANCH}" : ""),
250 ])
251 }
252
253 function "get_cache_to_rocm_nixl" {
254 params = []
255 result = compact([
256 NIXL_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:nixl-rocm-${NIXL_CACHE_KEY},mode=min" : (NIXL_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:nixl-rocm-${NIXL_BRANCH}-ucx-${UCX_BRANCH},mode=min" : ""),
257 ])
258 }
259
260 function "get_cache_to_rocm_rocshmem" {
261 params = []
262 result = compact([
263 ROCSHMEM_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocshmem-rocm-${ROCSHMEM_CACHE_KEY},mode=min" : (ROCSHMEM_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:rocshmem-rocm-${ROCSHMEM_BRANCH},mode=min" : ""),
264 ])
265 }
266
267 function "get_cache_to_rocm_deepep" {
268 params = []
269 result = compact([
270 DEEPEP_CACHE_KEY != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:deepep-rocm-${DEEPEP_CACHE_KEY},mode=min" : (DEEPEP_BRANCH != "" ? "type=registry,ref=${DOCKERHUB_CACHE_REPO}:deepep-rocm-${DEEPEP_BRANCH}-rocshmem-${ROCSHMEM_BRANCH},mode=min" : ""),
271 ])
272 }
273
274 # CI targets
275
276 target "_ci-rocm" {
277 annotations = [
278 "manifest:vllm.buildkite.build_number=${BUILDKITE_BUILD_NUMBER}",
279 "manifest:vllm.buildkite.build_id=${BUILDKITE_BUILD_ID}",
280 ]
281 args = {
282 ARG_PYTORCH_ROCM_ARCH = PYTORCH_ROCM_ARCH
283 CI_BASE_IMAGE = CI_BASE_IMAGE
284 ROCM_SMOKE_ID = BUILDKITE_BUILD_ID
285 max_jobs = CI_MAX_JOBS
286 }
287 }
288
289 target "test-rocm-ci" {
290 inherits = ["_common-rocm", "_ci-rocm", "_labels"]
291 target = "test"
292 cache-from = get_cache_from_rocm()
293 cache-to = get_cache_to_rocm()
294 tags = compact([
295 IMAGE_TAG,
296 IMAGE_TAG_LATEST,
297 ])
298 output = ["type=registry"]
299 }
300
301 # Validate the test image in the shared BuildKit graph and export only the
302 # success marker. This avoids pulling the multi-GB image into the host daemon.
303 target "smoke-test-rocm-ci" {
304 inherits = ["_common-rocm", "_ci-rocm"]
305 target = "export_test_smoke"
306 cache-from = get_cache_from_rocm()
307 output = ["type=local,dest=./build/rocm-smoke-export"]
308 }
309
310 # Cache-only target for the source-scoped ROCm native build stage.
311 # This persists the csrc-build stage in the registry cache even though the
312 # final test image only consumes it indirectly while packaging the wheel.
313 target "csrc-rocm-ci" {
314 inherits = ["_common-rocm", "_ci-rocm"]
315 target = "csrc-build"
316 cache-from = get_cache_from_rocm_csrc()
317 cache-to = get_cache_to_rocm_csrc()
318 output = ["type=cacheonly"]
319 }
320
321 # Cache-only target for the Rust frontend build stage. Final-image cache
322 # exports use mode=min and do not reliably persist intermediate cargo layers,
323 # so Rust gets its own source-scoped cache target.
324 target "rust-rocm-ci" {
325 inherits = ["_common-rocm", "_ci-rocm"]
326 target = "rust-build"
327 cache-from = get_cache_from_rocm_rust()
328 cache-to = get_cache_to_rocm_rust()
329 output = ["type=cacheonly"]
330 }
331
332 # Keep wheel export on the same CI graph as the test image build so the
333 # shared build_vllm/export_vllm stages resolve identically within one bake
334 # invocation. Without this, export-wheel-rocm uses the plain local target
335 # args while test-rocm-ci uses CI-only args, which can lead to separate
336 # cache lineages and inconsistent export_vllm results.
337 target "export-wheel-rocm" {
338 inherits = ["_common-rocm", "_ci-rocm"]
339 target = "export_vllm"
340 cache-from = get_cache_from_rocm()
341 cache-to = get_cache_to_rocm()
342 output = ["type=local,dest=./wheel-export"]
343 }
344
345 # Artifact-only vLLM build. GPU test jobs consume this artifact on top of
346 # ci_base, avoiding a per-commit multi-GB image push/pull.
347 group "test-rocm-ci-with-artifacts" {
348 targets = ["rust-rocm-ci", "csrc-rocm-ci", "export-wheel-rocm"]
349 }
350
351 # Full test image + wheel export. Kept for fallback/debugging when a pushed,
352 # build-scoped image is useful.
353 group "test-rocm-ci-with-wheel" {
354 targets = [
355 "rust-rocm-ci",
356 "csrc-rocm-ci",
357 "test-rocm-ci",
358 "smoke-test-rocm-ci",
359 "export-wheel-rocm",
360 ]
361 }
362
363 # Primary output tag for the ci_base build. In Buildkite this is a unique,
364 # build-scoped tag; ci-bake-rocm.sh validates it before creating content and
365 # stable aliases.
366 variable "CI_BASE_IMAGE_TAG" {
367 default = "rocm/vllm-dev:ci_base"
368 }
369
370 # Versioned, content-addressed trusted ref. Preview builds import it read-only;
371 # the wrapper creates their source-scoped content alias after validation.
372 variable "CI_BASE_TRUSTED_CONTENT_REF" {
373 default = ""
374 }
375
376 # Cache-only targets for upstream dependency stages. These persist each stage
377 # in the registry cache keyed by its upstream commit hash. When ci_base rebuilds
378 # (e.g., requirements change), these stages are cache hits if their upstream
379 # pins haven't changed -- saving ~35min of compilation.
380 target "nixl-rocm-ci" {
381 inherits = ["_common-rocm", "_ci-rocm"]
382 target = "build_nixl"
383 cache-from = get_cache_from_rocm_deps()
384 cache-to = get_cache_to_rocm_nixl()
385 output = ["type=cacheonly"]
386 }
387
388 target "rocshmem-rocm-ci" {
389 inherits = ["_common-rocm", "_ci-rocm"]
390 target = "build_rocshmem"
391 cache-from = get_cache_from_rocm_deps()
392 cache-to = get_cache_to_rocm_rocshmem()
393 output = ["type=cacheonly"]
394 }
395
396 target "deepep-rocm-ci" {
397 inherits = ["_common-rocm", "_ci-rocm"]
398 target = "build_deepep"
399 cache-from = get_cache_from_rocm_deps()
400 cache-to = get_cache_to_rocm_deepep()
401 output = ["type=cacheonly"]
402 }
403
404 # Builds only the ci_base stage (NIXL, DeepEP, torchcodec, etc.)
405 # Invoked by the ensure-ci-base step when the content hash of ci_base-affecting
406 # files drifts from the remote image label. Per-PR builds then pull the result
407 # as CI_BASE_IMAGE instead of rebuilding those slow layers on every commit.
408 # Uses inline cache metadata on the ci_base image itself instead of exporting a
409 # separate registry cache artifact.
410 target "ci-base-rocm-ci" {
411 inherits = ["_common-rocm"]
412 target = "ci_base"
413 args = {
414 max_jobs = CI_MAX_JOBS
415 }
416 cache-from = concat(
417 compact([
418 CI_BASE_IMAGE_TAG != "" ? "type=registry,ref=${CI_BASE_IMAGE_TAG}" : "",
419 CI_BASE_TRUSTED_CONTENT_REF != "" ? "type=registry,ref=${CI_BASE_TRUSTED_CONTENT_REF}" : "",
420 ]),
421 # Import upstream dependency caches so NIXL/ROCShmem/DeepEP stages
422 # are cache hits even when ci_base itself needs rebuilding.
423 get_cache_from_rocm_deps(),
424 )
425 cache-to = ["type=inline"]
426 tags = compact([CI_BASE_IMAGE_TAG])
427 attest = ["type=provenance,disabled=true"]
428 output = ["type=registry"]
429 }
430
431 # Group for ci_base builds -- exports dependency stage caches alongside the
432 # ci_base image so future rebuilds can reuse them independently.
433 group "ci-base-rocm-ci-with-deps" {
434 targets = ["nixl-rocm-ci", "rocshmem-rocm-ci", "deepep-rocm-ci", "ci-base-rocm-ci"]
435 }