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
resources.requests✅ Yes✅ Clear eventsMediumCapacity-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 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

Practical Practice

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 wide Solution:

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 wide Solution:

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

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