Techniques to bypass or constrain the Kubernetes scheduler, pinning Pods to specific nodes. Manual scheduling is essential for debugging, node-local workloads, and the CKA exam’s “Workloads & Scheduling” domain. Synthesized from CKA Day 13 — Static Pods, Manual Scheduling, Labels, and Selectors.
What Is Manual Scheduling?
By default, Kubernetes uses the kube-scheduler to assign newly created Pods to nodes. The scheduler evaluates resource availability, taints, tolerations, affinity/anti-affinity rules, and data locality to pick the “best” node. Manual scheduling overrides this process, giving you direct control over node placement.
Key Insight: Manual scheduling is not a production anti-pattern — it is a deliberate tool. GPU workloads, licensing-bound software, zone-specific storage, and compliance requirements often mandate exact node placement. However, overuse leads to cluster imbalance and scheduling fragility. Source: CKA Day 13
Technique 1: nodeName (Direct Assignment)
The simplest and most forceful method. Setting nodeName in the Pod spec bypasses the scheduler entirely. The kubelet on the named node immediately attempts to run the Pod.
apiVersion: v1kind: Podmetadata: name: pinned-podspec: nodeName: worker-1 # Direct assignment — no scheduler evaluation containers: - name: nginx image: nginx
Characteristics:
No scheduler involvement: The Pod does not appear in the scheduler’s queue.
No validation: If worker-1 is down, out of CPU/memory, or tainted, the Pod remains stuck in Pending with no events explaining why. This is a common troubleshooting trap.
Use case: Emergency debugging, testing node-specific behavior, or when the scheduler is unavailable.
# Check why a nodeName-assigned Pod is stuckkubectl describe pod pinned-pod# Look for: 0/1 nodes are available: 1 node(s) had taint ...# But because the scheduler skipped it, the message may be minimal.
Technique 2: nodeSelector (Label-Based Filtering)
The recommended approach for simple manual scheduling. You label a node, then the Pod spec includes a nodeSelector that matches the label. The scheduler is still involved, but it only considers nodes that satisfy the selector.
Scheduler validates: If no node matches, the Pod stays Pending with a clear event: 0/3 nodes are available: 3 node(s) didn't match Pod's node affinity/selector.
Multiple selectors: All key-value pairs must match on the same node (AND logic).
Use case: SSD storage, GPU availability, OS type, zone placement.
Technique 3: Node Affinity (Advanced Constraint)
Node affinity is the advanced, expressive successor to nodeSelector. It supports “preferred” (soft) vs “required” (hard) constraints and set-based matching. See the dedicated deep-dive page for full YAML anatomy, operator reference, troubleshooting matrix, and the production pattern combining taints + affinity + tolerations.
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disk operator: In values: - ssd - nvme
Operator
Behavior
In
Key has one of the listed values
NotIn
Key does not have any of the listed values
Exists
Key exists (regardless of value)
DoesNotExist
Key does not exist
Gt
Key value > specified integer
Lt
Key value < specified integer
Key Distinction: Both required and preferred types include the suffix IgnoredDuringExecution, meaning changes to node labels after scheduling do not evict existing Pods. This is fundamentally different from NoExecute taints. Source: CKA Day 15
Note: Node affinity is more verbose than nodeSelector but offers soft constraints (preferredDuringSchedulingIgnoredDuringExecution) that allow the scheduler to place the Pod elsewhere if the ideal node is unavailable. This is the production-grade choice for node preference.
Technique 4: Taints and Tolerations (Negative Scheduling)
Taints are the inverse of node selectors: they repel Pods from nodes. A taint is a property on a node that says “do not schedule here unless you tolerate this condition.” This section provides a concise overview; see the dedicated page for a deep dive with full YAML anatomy, built-in taint catalog, and troubleshooting matrix.
# Taint a nodekubectl taint node worker-1 maintenance=true:NoSchedule
Soft avoidance — scheduler tries to avoid but will place if no alternative
NoExecute
Evicts existing Pods without a toleration
Common taints:
node-role.kubernetes.io/control-plane:NoSchedule — prevents user workloads on control plane nodes
node.kubernetes.io/not-ready:NoSchedule — automatically applied by the node controller
CKA Tip: To run a Pod on a control plane node, you must add a toleration for the control-plane taint. This is how kube-proxy and some monitoring DaemonSets run on master nodes. Source: CKA Day 13 & Source: CKA Day 14
Comparative Summary
Method
Scheduler Involved
Validation
Flexibility
Production Use
nodeName
❌ No
❌ None
Minimal
Debugging only
nodeSelector
✅ Yes
✅ Clear events
Low (exact match)
Simple constraints
nodeAffinity
✅ Yes
✅ Clear events
High (soft/hard)
Complex constraints
taints + tolerations
✅ Yes
✅ Clear events
Medium
Node isolation, maintenance
resources.requests
✅ Yes
✅ Clear events
Medium
Capacity-aware scheduling
Resource requests are another scheduler filter. Even if a Pod matches node labels, tolerates node taints, and satisfies affinity rules, it still cannot land on a node without enough remaining requested CPU/memory capacity. In that case the Pod remains Pending with events such as Insufficient memory. Source: CKA Day 16
Essential Commands
# List nodes with their labelskubectl get nodes --show-labels# Label a nodekubectl label node worker-1 disk=ssd# Remove a labelkubectl label node worker-1 disk-# Taint a nodekubectl taint node worker-1 gpu=true:NoSchedule# Remove a taintkubectl taint node worker-1 gpu=true:NoSchedule-# Check which Pods are on a nodekubectl get pods --all-namespaces -o wide | grep worker-1# Schedule a Pod with nodeName (imperative)kubectl run debug --image=busybox --overrides='{"spec":{"nodeName":"worker-1"}}' --restart=Never
CKA Exam Relevance
Workloads & Scheduling (~15%): Expect tasks to place a Pod on a specific node using nodeName, nodeSelector, or tolerations.
Troubleshooting (~30%): A Pod stuck in Pending may have a nodeSelector that matches no nodes, or a missing toleration for a tainted node.
Exam-style hands-on tasks for this topic. Complete each task before reviewing the solution. Time yourself — CKA tasks average 5–7 minutes.
Task 1: Direct Node Assignment with nodeName
You are asked to create a Pod debug and force it to run on node worker2, bypassing the scheduler.
Requirements: Use nodeName in the Pod spec.
Verification:kubectl get pod debug -o wideSolution:
kubectl run debug --image=busybox --restart=Never \ --overrides='{"spec":{"nodeName":"worker2"}}' -- sleep 3600
Task 2: Schedule a GPU Pod with nodeSelector
You are asked to run a Pod cuda that must land on nodes labeled gpu=true.
Requirements: Use nodeSelector in the Pod spec.
Verification:kubectl get pod cuda -o wideSolution:
kubectl run cuda --image=nvidia/cuda:base --restart=Never --dry-run=client -o yaml > cuda.yaml# Edit cuda.yaml to add:# nodeSelector:# gpu: "true"kubectl apply -f cuda.yaml