Kubernetes Manual Scheduling

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: v1
kind: Pod
metadata:
  name: pinned-pod
spec:
  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 stuck
kubectl 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.

# Label a node
kubectl label node worker-1 disk=ssd
kubectl label node worker-1 gpu=true
apiVersion: v1
kind: Pod
metadata:
  name: fast-storage-pod
spec:
  nodeSelector:
    disk: ssd
  containers:
  - name: app
    image: postgres

Characteristics:

  • 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
OperatorBehavior
InKey has one of the listed values
NotInKey does not have any of the listed values
ExistsKey exists (regardless of value)
DoesNotExistKey does not exist
GtKey value > specified integer
LtKey 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 node
kubectl taint node worker-1 maintenance=true:NoSchedule
spec:
  tolerations:
  - key: maintenance
    operator: Equal
    value: "true"
    effect: NoSchedule
EffectBehavior
NoScheduleNew Pods without a toleration are not scheduled
PreferNoScheduleSoft avoidance — scheduler tries to avoid but will place if no alternative
NoExecuteEvicts 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

MethodScheduler InvolvedValidationFlexibilityProduction Use
nodeName❌ No❌ NoneMinimalDebugging only
nodeSelector✅ Yes✅ Clear eventsLow (exact match)Simple constraints
nodeAffinity✅ Yes✅ Clear eventsHigh (soft/hard)Complex constraints
taints + tolerations✅ Yes✅ Clear eventsMediumNode isolation, maintenance

Essential Commands

# List nodes with their labels
kubectl get nodes --show-labels
 
# Label a node
kubectl label node worker-1 disk=ssd
 
# Remove a label
kubectl label node worker-1 disk-
 
# Taint a node
kubectl taint node worker-1 gpu=true:NoSchedule
 
# Remove a taint
kubectl taint node worker-1 gpu=true:NoSchedule-
 
# Check which Pods are on a node
kubectl 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.
  • Speed Patterns:
    kubectl run nginx --image=nginx --restart=Never --overrides='{"spec":{"nodeName":"worker-1"}}'
    kubectl label node worker-1 disk=ssd
    kubectl taint node worker-1 gpu=true:NoSchedule

Tags: kubernetes scheduling manual-scheduling node-selector node-affinity taints tolerations cka devops kube-scheduler