Skip to content

ci: add vLLM/SGLang NIXL disaggregation sanity testing - #1777

Merged
NirWolfer merged 1 commit into
ai-dynamo:mainfrom
NirWolfer:vllm-sglang-tests
Jul 14, 2026
Merged

NirWolfer merged 1 commit into
ai-dynamo:mainfrom
NirWolfer:vllm-sglang-tests

Conversation

@NirWolfer

@NirWolfer NirWolfer commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Add vLLM / SGLang NIXL disaggregation sanity to the build-wheel pipeline

What

Adds a per-PR GPU sanity test that proves a freshly-built NIXL wheel performs a real prefill → decode KV transfer inside the vLLM and SGLang runtime images. It is folded into the existing nixl-ci-build-wheel job and runs on x86_64 / the mizu SLURM cluster, reusing the wheels that job already builds (no second wheel build).

Why

Until now the wheels were only smoke-tested for import/version. This validates that the wheel actually works end-to-end in the disaggregated inference path that consumes NIXL.
HPCINFRA-4419

How it works

Everything runs sequentially in the single podman container (ci-demo runs each runs_on_dockers entry as a parallel branch with no cross-branch ordering, so a split would race):

  1. Build wheels (existing, both arches).
  2. Build per-PR images (x86_64): extract the wheels and layer them onto the weights-base images; tag by commit SHA and push.
  3. Install scctl (dedicated x86_64-only step, scoped to the sanity).
  4. Allocate a mizu GPU node, run the vLLM and SGLang sanity, then release the allocation.

All sanity steps are gated to x86_64 via containerSelector.

Components

  • Weights-base imagescontrib/Dockerfile.{vllm,sglang}-base = pinned upstream image (docker.io/...) + baked-in test model (Qwen/Qwen3-1.7B) + iproute2. Built by the ci-demo runs_on_dockers mechanic (category: tool), rebuilt only when the Dockerfile changes or CI_IMAGE_TAG is bumped. sglang-base also installs sglang-router and bakes the GSM8K test set, so nothing is fetched at test time.
  • Per-PR images — reuse the wheels just built; the sanity script + .ci/scripts/common.sh are baked in and the image is tagged by commit SHA so the SLURM run pulls the exact image.
  • .gitlab/test_vllm_sglang_sanity.sh:
    • vLLM — launches a 1-prefill / 1-decode pair through the baked toy_proxy_server and asserts a completion comes back.
    • SGLang — routes through sglang-router and asserts gsm8k accuracy ≥ GSM8K_MIN_ACCURACY (default 0.55); a broken transfer collapses accuracy toward 0.
    • All host-bound ports (HTTP, vLLM NIXL side-channel, SGLang bootstrap) are allocated via get_next_tcp_port, so concurrent jobs sharing a node (8 GPUs, host networking) don't collide.

Other changes

  • cidemo-init.sh tracks the new base Dockerfiles.
  • CI_IMAGE_TAG bumped across the matrices so the bases rebuild.

Notes for reviewers

  • The sanity model and accuracy threshold are env-overridable (SANITY_MODEL_ID, GSM8K_MIN_ACCURACY, GSM8K_NUM_QUESTIONS).

Summary by CodeRabbit

  • New Features
    • Added end-to-end CI sanity checks for both vLLM and SGLang (prefill→decode validation with GSM8K scoring).
    • Introduced per-PR vLLM/SGLang images with preloaded model artifacts and GSM8K test data for faster, more reliable runs.
    • Added dedicated pinned “base” images for vLLM and SGLang with baked-in models.
  • Bug Fixes
    • Improved test readiness gating to verify a real completion request (not just health).
    • Standardized the proxy script install location used during image builds.

@github-actions

Copy link
Copy Markdown

👋 Hi NirWolfer! Thank you for contributing to ai-dynamo/nixl.

Your PR reviewers will review your contribution then trigger the CI to test your changes.

🚀

@NirWolfer
NirWolfer force-pushed the vllm-sglang-tests branch from 1ab463a to 4a09a8a Compare June 22, 2026 08:59
@NirWolfer
NirWolfer force-pushed the vllm-sglang-tests branch from 5737720 to 2208da3 Compare June 23, 2026 15:52
@NirWolfer
NirWolfer force-pushed the vllm-sglang-tests branch from 2208da3 to ffd64ec Compare June 24, 2026 07:48
@NirWolfer
NirWolfer force-pushed the vllm-sglang-tests branch from ffd64ec to cbde7bf Compare June 24, 2026 16:24
@NirWolfer
NirWolfer marked this pull request as ready for review June 24, 2026 17:42
@NirWolfer

Copy link
Copy Markdown
Contributor Author

/build

@svc-nixl

svc-nixl commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

👀 Investigating nixl-ci-dl-gpu build 1360

@svc-nixl

svc-nixl commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

🤖 CI Triage Agentnixl-ci-dl-gpu · commit ca294b98

This confirms the nixlbench branch in my log passed all its DRAM tests and skipped VRAM ("Worker without GPU, skipping VRAM tests") because HAS_GPU=false. That branch exited 0.

I've now thoroughly reviewed the one log I could fetch. The captured branch — including the full C++ gtest suite, the shell test binaries, test_azure.sh, and all nixlbench runs — completed successfully end to end. The pipeline's FAILURE comes from parallel task failed with msg: ... script returned exit code 1, referring to a sibling parallel branch (a second SLURM job, 1483396, was cancelled at cleanup) whose console output is not present in the log text returned to me.

I must not speculate about a branch whose log I don't have. Here is what the evidence supports:

Summary: A parallel branch of the nixl-ci-dl-gpu #1360 build failed with exit code 1; the branch captured in the fetched console log passed all its stages successfully.

Root cause: Not determinable from the available log. The branch present in the fetched console (SLURM job 1483397, the "master"/DL nixlbench + C++ tests branch) ran to completion with all tests passing — every gtest case passed, test_azure.sh printed "Done.", and all nixlbench POSIX/UCX DRAM runs reported results and exited cleanly. The pipeline-level hudson.AbortException: parallel task failed with msg: ... script returned exit code 1 originates from a different parallel branch (a second SLURM job, 1483396, was scanceled during cleanup) whose console output was not included in the returned log. The only anomaly in the captured branch was benign: the container reported HAS_GPU=false with cuDeviceGetCount(&num_devices) failed: initialization error and UCX CUDA support was not found despite nvidia-smi topo -m showing 4 GPUs — but that branch still passed by degrading to DRAM-only tests, so it is not the failing task.

Implicated commit: unknown (the failing branch's log was not retrievable; the CUDA-init anomaly, if it proves fatal in the other branch, would point to a base-image/CUDA-driver mismatch rather than a specific NIXL source commit).

File: unknown — the failing branch's step/log is not in the fetched output.

Suggested fix: Retrieve the console log for the failing parallel branch specifically (the sibling SLURM job 1483396 / the non-"master" branch of build #1360) — e.g. open the branch's "Restart from stage" / branch-specific log in Blue Ocean, or the flowNode/branch log URL — since the top-level log only interleaved the passing branch. Separately, investigate the cuDeviceGetCount ... initialization error / HAS_GPU=false GPU-init anomaly on gb-nvl-059-compute07: CUDA cannot initialize inside the container even though the GPUs are visible, which is consistent with a CUDA driver/toolkit or base-image mismatch (compare with the CUDA base-image PRs #1869 / #1893). If the other branch runs GPU/VRAM tests, that init failure would make it fail hard rather than skip.

Related: #1869 (Switch CI base image to pytorch + CUDA 13.3), #1893 (CI: pin CUDA-ambiguous LLM base image defaults) — both concern CUDA base-image init issues that could explain the GPU-init anomaly seen in the captured log.

@NirWolfer
NirWolfer requested a review from ovidiusm July 7, 2026 12:11
ovidiusm pushed a commit that referenced this pull request Jul 13, 2026
…deps (#1870)

## What?
Add a wheel_base Docker build target covering all dependency compilation
(hwloc, OpenSSL, Abseil, gRPC, AWS/Azure SDKs, Rust, DOCA, libfabric,
UCX, etc.). The wheel stage starts FROM wheel_base and only runs the
NIXL build and wheel creation steps.

In CI the wheel_base image is built and pushed to Artifactory by ci-demo
when Dockerfile.manylinux changes (via the new runs_on_dockers entry).
The build-wheel pipeline then passes --wheel-base-image to
build-container.sh which pulls the cached image and runs --target wheel,
skipping the expensive dep compilation on every PR.

Local docker builds are unaffected: wheel_base ARG defaults to the local
stage name so docker build ./contrib/Dockerfile.manylinux builds the
full image as before.

Also removes the now-redundant manylinux matrix axis (was single-valued
2_28, hardcoded in the image name and wheel-base arg), and wires
cidemo-init.sh to enforce a CI_IMAGE_TAG bump in build-wheel-matrix.yaml
whenever Dockerfile.manylinux changes.

## Why?
Lower nixl-ci-build-wheel pipeline times, and to justify adding #1777
later on which adds time to this pipeline


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Wheel builds now use a single cached wheel-base (manylinux_2_28) and
support selecting a prebuilt dependencies image, alongside optional
Torch version selection.
* The wheel build matrix is simplified to vary only by Python version
and architecture for more consistent builds.
* **Bug Fixes**
* CI validation now more reliably enforces required image tag updates
for both standard CI changes and wheel Dockerfile changes, with clearer
guidance when updates are missing.
* **Documentation**
* Updated CI docs to reflect the new cached wheel-base flow and how to
refresh it via the wheel cache image tag.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Nir Wolfson <nwolfer@nvidia.com>
Signed-off-by: NirWolfer <nwolfer@nvidia.com>
Gate every PR with a 1-prefill/1-decode NIXL KV-transfer sanity for vLLM and
SGLang, folded into the existing nixl-ci-build-wheel job so wheels aren't built
twice for it. Runs on the gb200nvl72_ci dlcluster SLURM partition (Grace-Blackwell
= aarch64) over SSH.

Structure: x86_64 wheels build in the upstream manylinux runner (wheel-only). Each
framework sanity runs as its own aarch64 branch (build_helper_vllm /
build_helper_sglang): build the aarch64 wheels (reusing the cached wheel_base),
layer them onto a pinned vLLM/SGLang base image (.ci/dockerfiles/Dockerfile.{vllm,
sglang}-base, built as category: tool by ci-demo), then bring up prefill+decode
wired through NIXL and assert a completion comes back through the transfer. SGLang
additionally asserts a gsm8k accuracy floor on Qwen/Qwen3-8B. The aarch64 wheels
are built once per framework branch so the two flows stay separate, readable
branches in the Jenkins UI (nested step parallelism renders as an unlabeled blob,
JENKINS-54010).

The test model weights are pulled at runtime (not baked) to keep image pulls fast;
GPU memory fraction and TCP port range are pinned so servers start on shared nodes
and SGLang's derived ports stay within range. ci-overview documents the new gate.

Signed-off-by: NirWolfer <nwolfer@nvidia.com>
@NirWolfer

Copy link
Copy Markdown
Contributor Author

/build

@svc-nixl

Copy link
Copy Markdown
Collaborator

🤖 CI Triage Agentnixl-ci-dl-gpu-ep · commit 8aa1f4d7

TL;DR: The "Allocate DL EP Environment" stage failed because a Slurm salloc on the gb200nvl72_cx8 partition sat queued for the full 1-hour --immediate=3600 window and timed out ("Unable to allocate resources: Connection timed out") — a cluster capacity/infra problem, not a code bug. Re-run the build once GB200 nodes are free; no source change is needed.

Full analysis

Summary: Stage 245 ("Allocate DL EP Environment", master-UCX variant) failed at the Slurm resource-allocation step; the NIXL build, EP compile, and Docker image all succeeded beforehand.

Root cause: salloc -N 1 -p gb200nvl72_cx8 --immediate=3600 --time=01:30:00 ... was issued at 09:29:40 and returned salloc: job 1550183 queued and waiting for resources / error: Unable to allocate resources: Connection timed out at 10:29:48 — exactly ~60 minutes (the --immediate=3600 limit) later. No GB200 node became available in that window, so Slurm gave up and the script exited 1. The ~1-hour silent gap is Slurm legitimately waiting in queue for scarce GPU capacity (not a hung process); the sibling allocation for the ucx-v1.22.x path (stage 183) succeeded in 98s, confirming this is capacity contention specific to this allocation, not PR #1777's code.

Implicated commit: none — the failure is in CI infrastructure/cluster scheduling, not in commit 8aa1f4d (PR #1777). All build/compile/install steps passed.

File: n/a (CI Slurm allocation step: salloc ... -p gb200nvl72_cx8 --immediate=3600)

Suggested fix: Retry the build when the gb200nvl72_cx8 partition has free nodes — this is a transient capacity timeout, not a regression. To reduce recurrence, consider: (1) raising --immediate / adding a retry-with-backoff loop around salloc in the allocation stage so a transient queue wait doesn't fail the whole pipeline, or (2) surfacing a clearer "cluster busy, retrying" message. Do not treat this as a bug in the PR.

Related: none

@NirWolfer
NirWolfer merged commit b594eb0 into ai-dynamo:main Jul 14, 2026
18 checks passed
@NirWolfer
NirWolfer deleted the vllm-sglang-tests branch July 14, 2026 13:37
NirWolfer added a commit to NirWolfer/nixl that referenced this pull request Jul 20, 2026
…deps (ai-dynamo#1870)

## What?
Add a wheel_base Docker build target covering all dependency compilation
(hwloc, OpenSSL, Abseil, gRPC, AWS/Azure SDKs, Rust, DOCA, libfabric,
UCX, etc.). The wheel stage starts FROM wheel_base and only runs the
NIXL build and wheel creation steps.

In CI the wheel_base image is built and pushed to Artifactory by ci-demo
when Dockerfile.manylinux changes (via the new runs_on_dockers entry).
The build-wheel pipeline then passes --wheel-base-image to
build-container.sh which pulls the cached image and runs --target wheel,
skipping the expensive dep compilation on every PR.

Local docker builds are unaffected: wheel_base ARG defaults to the local
stage name so docker build ./contrib/Dockerfile.manylinux builds the
full image as before.

Also removes the now-redundant manylinux matrix axis (was single-valued
2_28, hardcoded in the image name and wheel-base arg), and wires
cidemo-init.sh to enforce a CI_IMAGE_TAG bump in build-wheel-matrix.yaml
whenever Dockerfile.manylinux changes.

## Why?
Lower nixl-ci-build-wheel pipeline times, and to justify adding ai-dynamo#1777
later on which adds time to this pipeline


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Wheel builds now use a single cached wheel-base (manylinux_2_28) and
support selecting a prebuilt dependencies image, alongside optional
Torch version selection.
* The wheel build matrix is simplified to vary only by Python version
and architecture for more consistent builds.
* **Bug Fixes**
* CI validation now more reliably enforces required image tag updates
for both standard CI changes and wheel Dockerfile changes, with clearer
guidance when updates are missing.
* **Documentation**
* Updated CI docs to reflect the new cached wheel-base flow and how to
refresh it via the wheel cache image tag.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Nir Wolfson <nwolfer@nvidia.com>
Signed-off-by: NirWolfer <nwolfer@nvidia.com>
NirWolfer added a commit to NirWolfer/nixl that referenced this pull request Jul 22, 2026
…deps (ai-dynamo#1870)

## What?
Add a wheel_base Docker build target covering all dependency compilation
(hwloc, OpenSSL, Abseil, gRPC, AWS/Azure SDKs, Rust, DOCA, libfabric,
UCX, etc.). The wheel stage starts FROM wheel_base and only runs the
NIXL build and wheel creation steps.

In CI the wheel_base image is built and pushed to Artifactory by ci-demo
when Dockerfile.manylinux changes (via the new runs_on_dockers entry).
The build-wheel pipeline then passes --wheel-base-image to
build-container.sh which pulls the cached image and runs --target wheel,
skipping the expensive dep compilation on every PR.

Local docker builds are unaffected: wheel_base ARG defaults to the local
stage name so docker build ./contrib/Dockerfile.manylinux builds the
full image as before.

Also removes the now-redundant manylinux matrix axis (was single-valued
2_28, hardcoded in the image name and wheel-base arg), and wires
cidemo-init.sh to enforce a CI_IMAGE_TAG bump in build-wheel-matrix.yaml
whenever Dockerfile.manylinux changes.

## Why?
Lower nixl-ci-build-wheel pipeline times, and to justify adding ai-dynamo#1777
later on which adds time to this pipeline


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Wheel builds now use a single cached wheel-base (manylinux_2_28) and
support selecting a prebuilt dependencies image, alongside optional
Torch version selection.
* The wheel build matrix is simplified to vary only by Python version
and architecture for more consistent builds.
* **Bug Fixes**
* CI validation now more reliably enforces required image tag updates
for both standard CI changes and wheel Dockerfile changes, with clearer
guidance when updates are missing.
* **Documentation**
* Updated CI docs to reflect the new cached wheel-base flow and how to
refresh it via the wheel cache image tag.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Nir Wolfson <nwolfer@nvidia.com>
Signed-off-by: NirWolfer <nwolfer@nvidia.com>
nv-nmailhot pushed a commit that referenced this pull request Jul 22, 2026
…ing to release/1.4.0 (#1968)

## What

Backports the wheel-build and CI infrastructure from `main` to
`release/1.4.0` — six commits, cherry-picked in main's chronological
order:

1. **#1870** — split `Dockerfile.manylinux` into `wheel_base`/`wheel`
stages so CI caches the expensive deps image; adds `--wheel-base-image`
to `build-container.sh`
2. **#1933** — auto-derive `CI_IMAGE_TAG` in `cidemo-init.sh` (matrix
YAMLs carry a `CI_MANAGED` placeholder patched at CI time; no manual tag
bumps)
3. **#1941** — `--build-infinia`: bundle the Infinia DDN plugin
(`libplugin_INFINIA.so`) into the wheel (opt-in)
4. **#1918** — `--build-ucx-spcx-plugin`: build and bundle the UCX spcx
external plugin (`libuct_ib_mlx5_ext.so`) into the wheel (opt-in)
5. **#1956** — rename `BUILD_TARGET` → `DOCKER_BUILD_TARGET` in
`build-container.sh` (Jenkins exports a `BUILD_TARGET` job param that
leaked into the docker command line)
6. **#1863** — add the per-PR container-build pipeline
(`nixl-ci-build-container-pr`) and dispatcher fan-out

After this PR, `Jenkinsfile.dispatcher`, `cidemo-init.sh`,
`build-container.sh`, `Dockerfile.manylinux`, and
`build-container-pr-matrix.yaml` are byte-identical to `main`.

Conflict resolutions (all mechanical):
- Matrix YAMLs: release-branch hardcoded `CI_IMAGE_TAG` values replaced
by the `CI_MANAGED` placeholder (#1933)
- `ci-overview.md`: kept release-accurate wording where main's text
references features not backported (vLLM/SGLang sanity #1777, cleanup
job #1785)

Intentionally **not** backported (not needed for release CI):
vLLM/SGLang sanity testing (#1777), Artifactory cleanup job (#1785,
#1964), CI timeout right-sizing (#1932), LLM base image pins (#1893),
port pool cap (#1685).

## Why

Needed for the 1.4.0 release: internal wheels must bundle the UCX spcx
and Infinia plugins, and the release-branch CI needs the same
wheel-build pipeline as `main` to build them.

Both plugin features are opt-in (off by default); default builds are
byte-identical to the current release branch.

---------

Signed-off-by: Nir Wolfson <nwolfer@nvidia.com>
Signed-off-by: NirWolfer <nwolfer@nvidia.com>
Co-authored-by: Alexey Rivkin <57558538+Alexey-Rivkin@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants