Skip to content

Commit 9804a7c

Browse files
authored
Document heterogeneous node groups for tasks (#4154)
* Document heterogeneous node groups for tasks * Use node groups in Ray, Miles, and NCCL/RCCL examples * Address review feedback on node groups docs --------- Co-authored-by: Bihan Rana
1 parent 5fdea61 commit 9804a7c

5 files changed

Lines changed: 309 additions & 127 deletions

File tree

mkdocs/docs/concepts/tasks.md

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,118 @@ Jobs on each node communicate using their private IP addresses. Use `DSTACK_MAST
179179
For convenience, `~/.ssh/config` is preconfigured with these options, so a simple `ssh <node_ip>` is enough.
180180
For a list of nodes IPs check the `DSTACK_NODES_IPS` environment variable.
181181

182+
### Node groups
183+
184+
A task can define multiple node groups. Each group has its own `nodes` count,
185+
`resources`, `commands`, and `ports`.
186+
187+
<div editor-title=".dstack.yml">
188+
189+
```yaml
190+
type: task
191+
name: ray-cluster
192+
193+
python: 3.12
194+
195+
groups:
196+
- name: head
197+
nodes: 1
198+
commands:
199+
- pip uninstall -y ray && pip install -U "ray[default]"
200+
- ray start --head --port=6379 --block
201+
resources:
202+
cpu: 2
203+
memory: 4GB..
204+
ports:
205+
- 8265
206+
207+
- name: workers
208+
nodes: 2
209+
commands:
210+
- pip uninstall -y ray && pip install -U "ray[default]"
211+
- ray start --address=${{ groups[0].nodes[0].IP_ADDRESS }}:6379 --block
212+
resources:
213+
gpu: H100:8
214+
```
215+
216+
</div>
217+
218+
Commands in any group can reference the internal IP address of any node in the run via
219+
`${{ groups[i].nodes[j].IP_ADDRESS }}`, where `i` is the index of the group in `groups` and `j` is
220+
the index of the node within that group.
221+
222+
> `groups[0].nodes[0]` is the run's master node — it is what `DSTACK_MASTER_NODE_IP` resolves to.
223+
224+
Currently, only `resources`, `commands`, and `ports` can be configured per node group. [`groups`](../reference/dstack.yml/task.md#groups) and top-level `nodes` are mutually exclusive.Support for other properties is coming soon.
225+
226+
??? info "Prefill/decode example"
227+
Node groups can mix CPU and GPU roles. This SGLang prefill/decode split uses a CPU
228+
router (`groups[0]`, the master) and GPU workers. `startup_order: workers-first`
229+
starts prefill and decode before the router.
230+
231+
<div editor-title=".dstack.yml">
232+
233+
```yaml
234+
type: task
235+
name: prefill-decode
236+
image: lmsysorg/sglang:v0.5.10.post1
237+
env:
238+
- HF_TOKEN
239+
- MODEL_ID=zai-org/GLM-4.5-Air-FP8
240+
241+
startup_order: workers-first
242+
groups:
243+
# Router (CPU) — master node; wires prefill + decode by IP
244+
- name: router
245+
nodes: 1
246+
commands:
247+
- pip install smg
248+
- |
249+
echo "prefill=${{ groups[1].nodes[0].IP_ADDRESS }}"
250+
echo "decode=${{ groups[2].nodes[0].IP_ADDRESS }}"
251+
smg launch \
252+
--pd-disaggregation \
253+
--prefill http://${{ groups[1].nodes[0].IP_ADDRESS }}:8000 8998 \
254+
--decode http://${{ groups[2].nodes[0].IP_ADDRESS }}:8000 \
255+
--prefill-policy cache_aware \
256+
--host 0.0.0.0 --port 8000
257+
ports:
258+
- 8000
259+
resources:
260+
cpu: 4
261+
262+
- name: prefill
263+
nodes: 1
264+
commands:
265+
- |
266+
python -m sglang.launch_server \
267+
--model-path $MODEL_ID \
268+
--disaggregation-mode prefill \
269+
--disaggregation-transfer-backend nixl \
270+
--host 0.0.0.0 --port 8000 \
271+
--disaggregation-bootstrap-port 8998
272+
resources:
273+
gpu: H200
274+
275+
- name: decode
276+
nodes: 1
277+
commands:
278+
- |
279+
python -m sglang.launch_server \
280+
--model-path $MODEL_ID \
281+
--disaggregation-mode decode \
282+
--disaggregation-transfer-backend nixl \
283+
--host 0.0.0.0 --port 8000
284+
resources:
285+
gpu: H200
286+
```
287+
288+
</div>
289+
290+
!!! info "Examples"
291+
See the [Ray+RAGEN](../examples/training/ray-ragen.md) example for running a Ray cluster,
292+
and the [NCCL/RCCL tests](../examples/clusters/nccl-rccl-tests.md) example for running `mpirun` with node groups.
293+
182294
### Resources
183295

184296
When you specify a resource value like `cpu` or `memory`,
@@ -460,7 +572,7 @@ If you don't assign a value to an environment variable (see `HF_TOKEN` above),
460572
| `DSTACK_NODE_RANK` | The rank of the node |
461573
| `DSTACK_MASTER_NODE_IP` | The internal IP address of the master node |
462574
| `DSTACK_NODES_IPS` | The list of internal IP addresses of all nodes delimited by "\n" |
463-
| `DSTACK_MPI_HOSTFILE` | The path to a pre-populated MPI hostfile |
575+
| `DSTACK_MPI_HOSTFILE` | The path to a pre-populated MPI hostfile. The file lists GPU nodes as `<ip> slots=<gpus>` and CPU nodes as `<ip>` |
464576
| `DSTACK_WORKING_DIR` | The working directory of the run |
465577
| `DSTACK_REPO_DIR` | The directory where the repo is mounted (if any) |
466578

mkdocs/docs/examples/clusters/nccl-rccl-tests.md

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This example shows how to run [NCCL](https://github.com/NVIDIA/nccl-tests) or [R
1212

1313
## Running as a task
1414

15-
Here's an example of a task that runs AllReduce test on 2 nodes, each with 4 GPUs (8 processes in total).
15+
Here's an example of a task that runs AllReduce test on 2 nodes, each with 4 GPUs (8 processes in total), using [node groups](../../concepts/tasks.md#node-groups).
1616

1717
=== "NCCL tests"
1818

@@ -22,33 +22,38 @@ Here's an example of a task that runs AllReduce test on 2 nodes, each with 4 GPU
2222
type: task
2323
name: nccl-tests
2424

25-
nodes: 2
26-
2725
startup_order: workers-first
2826
stop_criteria: master-done
2927

3028
env:
3129
- NCCL_DEBUG=INFO
32-
commands:
33-
- |
34-
if [ $DSTACK_NODE_RANK -eq 0 ]; then
35-
mpirun \
36-
--allow-run-as-root \
37-
--hostfile $DSTACK_MPI_HOSTFILE \
38-
-n $DSTACK_GPUS_NUM \
39-
-N $DSTACK_GPUS_PER_NODE \
40-
--bind-to none \
41-
/opt/nccl-tests/build/all_reduce_perf -b 8 -e 8G -f 2 -g 1
42-
else
43-
sleep infinity
44-
fi
30+
31+
groups:
32+
- name: master # The name property is optional
33+
nodes: 1
34+
commands:
35+
- |
36+
mpirun \
37+
--allow-run-as-root \
38+
--hostfile $DSTACK_MPI_HOSTFILE \
39+
-n $DSTACK_GPUS_NUM \
40+
-N $DSTACK_GPUS_PER_NODE \
41+
--bind-to none \
42+
/opt/nccl-tests/build/all_reduce_perf -b 8 -e 8G -f 2 -g 1
43+
resources:
44+
gpu: nvidia:1..8
45+
shm_size: 16GB
46+
47+
- name: workers
48+
nodes: 1
49+
commands:
50+
- sleep infinity
51+
resources:
52+
gpu: nvidia:1..8
53+
shm_size: 16GB
4554

4655
# Uncomment if the `kubernetes` backend requires it for `/dev/infiniband` access
4756
#privileged: true
48-
49-
resources:
50-
gpu: nvidia:1..8
51-
shm_size: 16GB
5257
```
5358

5459
</div>
@@ -65,7 +70,6 @@ Here's an example of a task that runs AllReduce test on 2 nodes, each with 4 GPU
6570
type: task
6671
name: rccl-tests
6772

68-
nodes: 2
6973
startup_order: workers-first
7074
stop_criteria: master-done
7175

@@ -77,35 +81,50 @@ Here's an example of a task that runs AllReduce test on 2 nodes, each with 4 GPU
7781
env:
7882
- NCCL_DEBUG=INFO
7983
- OPEN_MPI_HOME=/usr/lib/x86_64-linux-gnu/openmpi
80-
commands:
81-
# Setup MPI and build RCCL tests
82-
- apt-get install -y git libopenmpi-dev openmpi-bin
83-
- git clone https://github.com/ROCm/rccl-tests.git
84-
- cd rccl-tests
85-
- make MPI=1 MPI_HOME=$OPEN_MPI_HOME
86-
87-
# Preload the RoCE driver library from the host (for Broadcom driver compatibility)
88-
- export LD_PRELOAD=/mnt/lib/libbnxt_re-rdmav34.so
89-
90-
# Run RCCL tests via MPI
91-
- |
92-
if [ $DSTACK_NODE_RANK -eq 0 ]; then
93-
mpirun --allow-run-as-root \
94-
--hostfile $DSTACK_MPI_HOSTFILE \
95-
-n $DSTACK_GPUS_NUM \
96-
-N $DSTACK_GPUS_PER_NODE \
97-
--mca btl_tcp_if_include ens41np0 \
98-
-x LD_PRELOAD \
99-
-x NCCL_IB_HCA=mlx5_0/1,bnxt_re0,bnxt_re1,bnxt_re2,bnxt_re3,bnxt_re4,bnxt_re5,bnxt_re6,bnxt_re7 \
100-
-x NCCL_IB_GID_INDEX=3 \
101-
-x NCCL_IB_DISABLE=0 \
102-
./build/all_reduce_perf -b 8M -e 8G -f 2 -g 1 -w 5 --iters 20 -c 0;
103-
else
104-
sleep infinity
105-
fi
106-
107-
resources:
108-
gpu: MI300X:8
84+
85+
groups:
86+
- name: master # The name property is optional
87+
nodes: 1
88+
commands:
89+
# Setup MPI and build RCCL tests
90+
- apt-get install -y git libopenmpi-dev openmpi-bin
91+
- git clone https://github.com/ROCm/rccl-tests.git
92+
- cd rccl-tests
93+
- make MPI=1 MPI_HOME=$OPEN_MPI_HOME
94+
95+
# Preload the RoCE driver library from the host (for Broadcom driver compatibility)
96+
- export LD_PRELOAD=/mnt/lib/libbnxt_re-rdmav34.so
97+
98+
# Run RCCL tests via MPI
99+
- |
100+
mpirun --allow-run-as-root \
101+
--hostfile $DSTACK_MPI_HOSTFILE \
102+
-n $DSTACK_GPUS_NUM \
103+
-N $DSTACK_GPUS_PER_NODE \
104+
--mca btl_tcp_if_include ens41np0 \
105+
-x LD_PRELOAD \
106+
-x NCCL_IB_HCA=mlx5_0/1,bnxt_re0,bnxt_re1,bnxt_re2,bnxt_re3,bnxt_re4,bnxt_re5,bnxt_re6,bnxt_re7 \
107+
-x NCCL_IB_GID_INDEX=3 \
108+
-x NCCL_IB_DISABLE=0 \
109+
./build/all_reduce_perf -b 8M -e 8G -f 2 -g 1 -w 5 --iters 20 -c 0;
110+
resources:
111+
gpu: MI300X:8
112+
113+
- name: workers
114+
nodes: 1
115+
commands:
116+
# Setup MPI and build RCCL tests
117+
- apt-get install -y git libopenmpi-dev openmpi-bin
118+
- git clone https://github.com/ROCm/rccl-tests.git
119+
- cd rccl-tests
120+
- make MPI=1 MPI_HOME=$OPEN_MPI_HOME
121+
122+
# Preload the RoCE driver library from the host (for Broadcom driver compatibility)
123+
- export LD_PRELOAD=/mnt/lib/libbnxt_re-rdmav34.so
124+
125+
- sleep infinity
126+
resources:
127+
gpu: MI300X:8
109128
```
110129

111130
</div>

0 commit comments

Comments
 (0)