Chapter 04: Resource Scheduling, Custom Constraints & Placement Groups
- Topic Focus • Hardware Resource Demands, Custom Resource Tags, Placement Groups, and Gang Scheduling
- Interactive Challenges • 6 Hands-on Exercises
- Launch Playground in Wasm →
1. Architectural Overview & Control Plane Mechanics
Ray's distributed scheduler uses logical resource requirements (num_cpus, num_gpus, resources={"custom_accelerator": 1}) to match tasks and actors to available node capacity across the cluster.
flowchart TD
Req["Placement Group Request<br/><code>bundles=[{GPU:1}, {GPU:1}], strategy='STRICT_SPREAD'</code>"] -->|"1. Submit Reservation"| GCS["GCS Resource Manager<br/>(Global Coordination)"]
GCS -->|"2. Atomic Gang Lock"| Node1["Node 1 (Host A)<br/>Bundle 0: 1 GPU"]
GCS -->|"2. Atomic Gang Lock"| Node2["Node 2 (Host B)<br/>Bundle 1: 1 GPU"]
Node1 -.->|"3. Schedule Worker"| Actor1["Worker Actor Rank 0"]
Node2 -.->|"3. Schedule Worker"| Actor2["Worker Actor Rank 1"]
style Req fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
style GCS fill:#1e293b,stroke:#f59e0b,stroke-width:2px,color:#f8fafc
style Node1 fill:#0f172a,stroke:#34d399,stroke-width:2px,color:#f8fafc
style Node2 fill:#0f172a,stroke:#06b6d4,stroke-width:2px,color:#f8fafc
style Actor1 fill:#1e1e38,stroke:#c084fc,stroke-width:1px,color:#f8fafc
style Actor2 fill:#1e1e38,stroke:#c084fc,stroke-width:1px,color:#f8fafc
Diagram Walkthrough & Core Concepts: - Atomic Gang Scheduling: Placement groups guarantee all-or-nothing resource reservation across physical cluster hosts before workers are dispatched. - Topology Control Strategies:
STRICT_SPREADenforces anti-affinity across separate physical machines for fault tolerance, whileSTRICT_PACKguarantees co-location on the same machine for ultra-fast NVLink communication. - Targeted Bundle Binding: Workers and actors bind to specific bundle indices within the placement group, ensuring predictable CPU and GPU resource isolation.
2. Annotated Python Code Anatomy & API Reference
import ray
from ray.util.placement_group import placement_group
from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy
# 1. Define atomic resource bundles for distributed training
pg = placement_group(
bundles=[{"CPU": 2, "GPU": 1}, {"CPU": 2, "GPU": 1}],
strategy="STRICT_SPREAD"
)
# 2. Block until the gang-scheduling reservation is granted
ray.get(pg.ready())
# 3. Schedule actors onto specific reserved bundles
@ray.remote(num_cpus=2, num_gpus=1)
class DistributedWorker:
def __init__(self, rank: int):
self.rank = rank
def run_training_step(self) -> str:
return f"Worker {self.rank} completed step on dedicated GPU"
workers = [
DistributedWorker.options(
scheduling_strategy=PlacementGroupSchedulingStrategy(
placement_group=pg,
placement_group_bundle_index=idx
)
).remote(idx)
for idx in range(2)
]
Key API Parameter Reference
@ray.remote(num_cpus=N, num_gpus=M, resources={...}): Resource demands.placement_group(bundles, strategy): Allocates atomic multi-node resource reservations. Strategies:STRICT_SPREAD,STRICT_PACK,PACK,SPREAD.PlacementGroupSchedulingStrategy: Binds a task or actor to an allocated bundle.ray.util.remove_placement_group(pg): Releases reserved capacity back to the cluster pool.
3. Production Best Practices & Hardening Guidelines
- Use Fractional GPUs for Inference: Declare
num_gpus=0.25for lightweight inference tasks to multiplex up to 4 workers onto a single physical GPU. - Always Check
pg.ready()with Timeout: When allocating placement groups in dynamic clusters, wait forpg.ready()before scheduling dependent workloads. - Release Unused Placement Groups: Always call
remove_placement_group()infinally:blocks to prevent resource leaks. - Prefer STRICT_PACK for All-Reduce: Co-locate distributed workers on NVLink-connected multi-GPU nodes with
STRICT_PACKfor maximum communication bandwidth. - Use Custom Resources for Hardware Affinity: Tag specialized nodes with custom resources (e.g.
{"h100": 1, "fpga": 1}).
4. Troubleshooting & Diagnostic Workflows
- Placement Group Pending Indefinitely:
- Symptom: Cluster hangs on
ray.get(pg.ready()). - Fix: Verify total bundle requirements do not exceed total cluster node resources; check for autoscaler node provisioning delays.
- Fractional GPU CUDA OOM:
- Symptom: Out of memory when multiple workers share a single GPU.
- Fix: Set PyTorch memory allocation caps (
torch.cuda.set_per_process_memory_fraction()). - Infeasible Scheduling Request:
- Symptom: Task warning "Task requires resources that cannot fit on any node".
- Fix: Verify individual bundle sizes fit within single node maximum resource bounds.
5. Hands-on Practice Exercises
| Exercise ID | Goal / Topic | Playground Link |
|---|---|---|
scheduling01 |
Fractional & Custom Resources | Open Exercise scheduling01 → |
scheduling02 |
Node Affinity Scheduling | Open Exercise scheduling02 → |
scheduling03 |
Placement Groups: SPREAD Strategy | Open Exercise scheduling03 → |
scheduling04 |
Placement Groups: PACK Strategy | Open Exercise scheduling04 → |
scheduling05 |
Gang Scheduling Multi-Bundle | Open Exercise scheduling05 → |
scheduling06 |
Dynamic Runtime Environments | Open Exercise scheduling06 → |