Manual Scheduling
In Kubernetes, pod scheduling is typically handled automatically by the scheduler component. However, there are situations where we might need to manually specify which node a pod should run on. This is achieved through a field called nodeName
.
Understanding the nodeName Field
All pods have a field called nodeName
which is empty by default. When this field is empty, the Kubernetes scheduler works as follows:
- The scheduler identifies pods without a specified
nodeName
- It runs its scheduling algorithm to determine the best node for each pod
- It assigns the pod to the selected node by creating a binding object
The nodeName
field is part of the Pod specification and can be found in the spec
section.
What Happens Without a Scheduler?
If a Kubernetes cluster doesn’t have a functioning scheduler:
- Pods will remain indefinitely in the
Pending
state - They will not be automatically assigned to any node
- We must manually intervene to schedule these pods
Manually Scheduling a Pod
We have two methods to manually schedule pods:
Method 1: Directly Specifying nodeName
We can specify the nodeName
field when creating a pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx
nodeName: worker-node-1 # Directly specify the node
When we use this method:
- The pod bypasses the scheduler entirely
- The kubelet on the specified node will directly create the pod
- No scheduling evaluation occurs (resource checks, taints, etc.)
The nodeName
field can only be set during pod creation. We cannot modify it for an existing pod.
Method 2: Creating a Binding Object
If we need to manually schedule an existing pod that’s stuck in Pending
state, we need to create a binding object and make an API call:
- First, we create a binding object in JSON format:
{
"apiVersion": "v1",
"kind": "Binding",
"metadata": {
"name": "nginx-pod"
},
"target": {
"apiVersion": "v1",
"kind": "Node",
"name": "worker-node-1"
}
}
- Then, we make a POST request to the binding API endpoint:
curl --header "Content-Type: application/json" \
--request POST \
--data @binding.json \
http://$SERVER/api/v1/namespaces/default/pods/nginx-pod/binding/
This mimics what the scheduler does when it assigns a pod to a node.
When to Use Manual Scheduling
We might consider manual scheduling in the following scenarios:
- Scheduler Failure: When the default scheduler is not functioning
- Testing and Debugging: To test specific pod-node interactions
- Special Hardware Requirements: To ensure pods run on nodes with specific hardware
- Troubleshooting: To replicate and diagnose node-specific issues
- Migration Scenarios: When migrating workloads with specific node requirements
Limitations of Manual Scheduling
Manual scheduling has several important limitations to consider:
- No Automatic Rescheduling: If the node fails, the pod won’t be automatically rescheduled
- No Constraint Checking: The system won’t verify if the node has sufficient resources
- No Consideration for Taints: Taints on the node are ignored
- No Optimization: Load balancing and optimal resource utilization aren’t considered
- Maintenance Burden: Manual tracking and maintenance of pod-node assignments becomes necessary
Best Practices
If we need to influence pod placement, consider these alternatives to manual scheduling:
- Node Selectors: Use
nodeSelector
to select nodes based on labels - Node Affinity: Use node affinity rules for more complex node selection
- Taints and Tolerations: Configure which nodes repel certain pods
- Pod Affinity/Anti-Affinity: Control pod placement relative to other pods
- Custom Schedulers: Implement specialized scheduling logic if needed