Why architecture matters here

The core problem node labels solve is that a scheduler which cannot see hardware distinctions will make expensive mistakes. Put GPU nodes into a shared pool with no labels and a large CPU-only batch job will happily fill them, leaving your GPU-hungry training jobs pending behind work that gets zero value from the accelerators. The GPUs — the most expensive resource in the building — sit doing integer arithmetic while the team that needs them waits. Labels make the placement constraint a first-class part of the request so the scheduler can refuse to waste scarce hardware.

The second driver is guaranteed access to a resource someone has paid a premium for. Reserved capacity, licensed nodes (where a per-core software license means only certain machines may legally run a workload), and compliance-isolated hardware all share a shape: a specific set of nodes must be reachable by a specific set of tenants and, often, by nobody else. An EXCLUSIVE label expresses exactly that — the nodes are walled off, and only queues whose accessible-node-labels include the label can place containers there. Without labels you would be reduced to running a whole separate cluster, doubling operational surface to enforce a boundary that is really just 'these ten machines are special.'

The third driver is efficiency under heterogeneity. Reserved and specialized hardware is idle a lot of the time, and idle expensive hardware is pure waste. NON_EXCLUSIVE labels let you have it both ways: the labeled nodes still prefer the apps that requested the label, but when those apps are not asking, the scheduler lends the spare capacity to the DEFAULT partition so ordinary work can use it. The instant a label-requesting app appears, the borrowed capacity is preempted back. This borrow-and-reclaim behavior is the difference between a partition that guarantees isolation and one that also guarantees utilization.

Architecturally, the reason this belongs in the scheduler rather than in application logic is that placement is a global, adversarial decision. Any individual app, left to choose its own nodes, will grab whatever it can; only a central authority with a cluster-wide view can enforce that the GPU partition's capacity is divided fairly among competing ML teams while the default partition is divided among everyone else. The label is metadata the scheduler needs to make that decision correctly — it is not something an ApplicationMaster can be trusted to honor voluntarily.

Finally, labels matter because they compose with the capacity model rather than replacing it. Teams already reason about YARN in terms of queues, guaranteed capacity, elasticity up to a maximum, and preemption. Node labels extend every one of those concepts along a new axis — the partition — without inventing a parallel governance system. A queue's administrators keep the same mental model; they just now set capacities per partition and declare which partitions the queue may reach. That continuity is why labels are the standard answer to hardware heterogeneity in YARN instead of a bolt-on that fights the scheduler.

Advertisement

The architecture: every piece explained

The authoritative component is the RMNodeLabelsManager inside the ResourceManager. It holds the set of cluster labels, each label's exclusivity flag, and the mapping from node (or host) to label. This state is the source of truth for every placement decision, so it must survive a ResourceManager restart: the manager mirrors the label collection and node mappings to a configured filesystem directory (typically on HDFS) and reloads it on startup. An admin mutates it through the yarn rmadmin CLI or the REST API — first adding a label to the cluster with its exclusivity, then replacing the labels on a set of nodes.

Exclusivity is the single most important property of a label. An EXCLUSIVE partition grants its resources only to applications that explicitly request the label; unlabeled applications can never land there, so the partition is a hard wall. A NON_EXCLUSIVE (sharable) partition prefers label-requesting apps but will allocate idle resources to DEFAULT-partition apps when no labeled demand exists — and those borrowed containers are the first to be preempted when labeled demand returns. Choosing exclusivity is a policy decision: isolation versus utilization.

The node-to-label mapping can be centralized (an admin explicitly assigns labels) or delegated to a distributed configuration where a script on each NodeManager reports the node's label on heartbeat — useful when nodes are provisioned by an external system that already knows the hardware type. Either way, a node has exactly one label; there is no notion of a node belonging to two partitions. Unlabeled nodes constitute the special DEFAULT partition, which is always present and always sharable.

The CapacityScheduler is where labels acquire teeth. Each leaf and parent queue gains a per-partition capacity: capacity[gpu]=40% means this queue is guaranteed 40% of the GPU partition. A queue also declares accessible-node-labels; a queue that does not list gpu cannot place a single container on GPU nodes regardless of what an app requests. The sum of sibling queues' capacities within a partition must total 100% of that partition, exactly as it does for the default capacity, so each partition is a self-contained capacity plane with its own guarantees and maximums.

Two more pieces complete the picture. A ResourceRequest carries a node-label expression — the label the app wants its containers placed against; if it names gpu, the scheduler will only satisfy the request from GPU nodes in a queue permitted to reach that partition. And preemption becomes partition-aware: the capacity monitor computes over-capacity and headroom per partition, so a GPU queue exceeding its guarantee is only preempted for the benefit of other GPU-partition queues, never confused with pressure in the default partition. The diagram below shows how the label store, the partitions, and the per-partition capacity model fit together.

YARN node labels — partition the cluster into named, capacity-governed node groupsResourceManager owns the label-to-node map, scheduler places by partitionAdmin CLI / RESTadd-to-cluster-node-labels, replace-labelsRMNodeLabelsManagerauthoritative label store (in-memory + FS mirror)NodeManagersheartbeat, report resourcesDEFAULT partitionunlabeled nodes, sharedgpu (EXCLUSIVE)only gpu-mapped queues run herespot (NON_EXCLUSIVE)idle capacity lent to DEFAULTCapacityScheduler — per-queue capacity[partition] + accessible-node-labelsOps — cluster metrics per partition, headroom, preemption, label auditmapmapmapstoregoverngoverngovernoperate
YARN node labels: admins map nodes into named partitions; the scheduler grants each partition's capacity only to queues whose accessible-node-labels include it.
Advertisement

End-to-end flow

Consider a training job that needs eight GPU containers. The administrator has already created an EXCLUSIVE label gpu, mapped the ten GPU machines to it, and configured the ml queue with accessible-node-labels=gpu and capacity[gpu]=60%. The lifecycle begins when the job is submitted to the ml queue and its ApplicationMaster starts (the AM itself typically runs in the DEFAULT partition unless configured otherwise).

The ApplicationMaster then issues ResourceRequests for its eight workers, each stamped with the node-label expression gpu. These requests flow to the ResourceManager on the AM's regular allocate heartbeat. The scheduler routes them into the ml queue's accounting for the gpu partition — a separate ledger from that queue's default capacity — and checks that the queue has headroom against its 60% guarantee on the GPU partition.

On each NodeManager heartbeat from a GPU-labeled node, the scheduler looks for pending requests it can satisfy from that node. Because the node's partition is gpu and the ml queue is permitted to reach it with capacity to spare, the scheduler creates a container allocation. Crucially, a request with the gpu expression will never be satisfied by a DEFAULT-partition node, and a request with no expression will never be satisfied by an EXCLUSIVE gpu node — the label acts as a hard predicate on both sides of the match.

The allocation is returned to the ApplicationMaster on its next heartbeat as a container token; the AM contacts the NodeManager on the GPU host to launch the worker. From here the container lifecycle is completely ordinary — localization, launch, monitoring, completion — and nothing about running on a labeled node changes how the container executes. The label influenced only where the container was placed, not how it runs.

Now suppose the label were NON_EXCLUSIVE instead. While the ML team is idle, a batch job in the DEFAULT partition can borrow the GPU nodes' capacity and run there. When the training job later requests its eight GPU containers and the partition is fully lent out, the partition-aware preemption monitor detects that labeled demand now exists, selects the borrowed DEFAULT-partition containers on GPU nodes, and preempts them to make room — returning the capacity to its rightful partition. The same allocation loop then places the training workers. That reclaim step is the entire reason NON_EXCLUSIVE labels can promise both isolation and high utilization.