<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hardik Arora</title>
    <description>The latest articles on DEV Community by Hardik Arora (@hardik_arora999).</description>
    <link>https://dev.to/hardik_arora999</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3247388%2F2ec4643f-9cdd-4531-bcaa-487ba9ba8ef8.jpeg</url>
      <title>DEV Community: Hardik Arora</title>
      <link>https://dev.to/hardik_arora999</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hardik_arora999"/>
    <language>en</language>
    <item>
      <title>Building a Custom Kubernetes Scheduler(with Simple ShellScripts): A Hands-On Lab Journey</title>
      <dc:creator>Hardik Arora</dc:creator>
      <pubDate>Sun, 23 Aug 2026 11:49:15 +0000</pubDate>
      <link>https://dev.to/hardik_arora999/building-a-custom-kubernetes-scheduler-a-hands-on-lab-journey-be8</link>
      <guid>https://dev.to/hardik_arora999/building-a-custom-kubernetes-scheduler-a-hands-on-lab-journey-be8</guid>
      <description>&lt;p&gt;As Kubernetes continues to dominate container orchestration, understanding how its scheduler works becomes crucial for any DevOps engineer.This hands-on lab demonstrates the inner workings of Kubernetes scheduling and walks through creating a custom scheduler from scratch.&lt;/p&gt;

&lt;p&gt;Understanding the Kubernetes Scheduler&lt;/p&gt;

&lt;p&gt;Before diving into the terminal, it's important to understand the component at play. The Kube-Scheduler serves as the brain behind pod placement in Kubernetes. It follows a three-phase process:&lt;/p&gt;

&lt;p&gt;Filtering - Eliminates nodes that don't meet requirements&lt;/p&gt;

&lt;p&gt;Scoring - Ranks suitable nodes based on various factors&lt;/p&gt;

&lt;p&gt;Binding - Creates the connection between pod and chosen node&lt;/p&gt;

&lt;p&gt;The scheduler considers CPU, memory, node affinity, taints, tolerations, and various other constraints to make intelligent placement decisions.&lt;/p&gt;

&lt;p&gt;Lab Setup: Creating the First Scheduled Pod&lt;/p&gt;

&lt;p&gt;The lab begins by templating an nginx pod that will later be targeted with a custom scheduler:&lt;/p&gt;

&lt;p&gt;kubectl run nginx --image=nginx -o yaml --dry-run=client | tee nginx_scheduler.yaml&lt;/p&gt;

&lt;p&gt;This command generates a pod manifest without actually creating the pod. The --dry-run=client flag proves perfect for templating purposes.&lt;/p&gt;

&lt;p&gt;Exploring the schedulerName Option&lt;/p&gt;

&lt;p&gt;Kubernetes provides a powerful but lesser-known feature: the ability to specify which scheduler should handle a pod. The lab explores this capability:&lt;/p&gt;

&lt;p&gt;kubectl explain pod.spec | more&lt;/p&gt;

&lt;p&gt;Among the various pod specification options, schedulerName appears - a string field that allows targeting a specific scheduler. This becomes the gateway to custom scheduling logic.&lt;/p&gt;

&lt;p&gt;Configuring a Custom Scheduler&lt;/p&gt;

&lt;p&gt;The next step involves updating the nginx pod manifest to use a custom scheduler called my-scheduler:&lt;/p&gt;

&lt;p&gt;apiVersion: v1&lt;br&gt;
kind: Pod&lt;br&gt;
metadata:&lt;br&gt;
  creationTimestamp: null&lt;br&gt;
  labels:&lt;br&gt;
    run: nginx&lt;br&gt;
  name: nginx&lt;br&gt;
spec:&lt;br&gt;
  schedulerName: my-scheduler&lt;br&gt;
  containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;image: nginx
name: nginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key addition here is schedulerName: my-scheduler under the spec section.&lt;/p&gt;

&lt;p&gt;Figure 2: The nginx_scheduler.yaml file with schedulerName specified&lt;/p&gt;

&lt;p&gt;Applying the Configuration&lt;/p&gt;

&lt;p&gt;The lab proceeds with applying this configuration:&lt;/p&gt;

&lt;p&gt;kubectl apply -f nginx_scheduler.yaml&lt;/p&gt;

&lt;p&gt;Checking the pod status reveals an interesting result:&lt;/p&gt;

&lt;p&gt;kubectl get pods -o wide&lt;/p&gt;

&lt;p&gt;Observation: The pod remains stuck in Pending state. This occurs because there's no scheduler called my-scheduler running in the cluster. The pod waits patiently for its designated scheduler to pick it up.&lt;/p&gt;

&lt;p&gt;Figure 1: The nginx pod remains in Pending state as it waits for the my-scheduler&lt;/p&gt;

&lt;p&gt;Building the Custom Scheduler&lt;/p&gt;

&lt;p&gt;While production schedulers are typically written in Golang and deeply integrated with the Kubernetes API, this lab uses a bash script for educational purposes. This approach helps demonstrate the scheduler's workflow without getting lost in complex code.&lt;/p&gt;

&lt;p&gt;First, the necessary tools are installed:&lt;/p&gt;

&lt;p&gt;apt update &amp;amp;&amp;amp; apt install -y git jq&lt;/p&gt;

&lt;p&gt;Next, the example scheduler repository is cloned:&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/spurin/simple-kubernetes-scheduler-example.git" rel="noopener noreferrer"&gt;https://github.com/spurin/simple-kubernetes-scheduler-example.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lab then moves into the directory and examines the scheduler script:&lt;/p&gt;

&lt;p&gt;cd simple-kubernetes-scheduler-example&lt;br&gt;
more my-scheduler.sh&lt;/p&gt;

&lt;p&gt;Understanding the Scheduler Logic&lt;/p&gt;

&lt;p&gt;The script performs several key operations:&lt;/p&gt;

&lt;p&gt;Queries available nodes using jsonpath to extract node names&lt;/p&gt;

&lt;p&gt;Identifies pods waiting for my-scheduler&lt;/p&gt;

&lt;p&gt;Selects a node (randomly in this simple example)&lt;/p&gt;

&lt;p&gt;Makes a binding request to the API server&lt;/p&gt;

&lt;p&gt;To understand how the JSON traversal works, the raw node data can be examined:&lt;/p&gt;

&lt;p&gt;kubectl get nodes -o json&lt;/p&gt;

&lt;p&gt;This reveals the structure the script navigates through - items as a list, each with metadata and a name field.&lt;/p&gt;

&lt;p&gt;Running the Custom Scheduler&lt;/p&gt;

&lt;p&gt;The exciting moment arrives when the custom scheduler is executed:&lt;/p&gt;

&lt;p&gt;./my-scheduler.sh&lt;/p&gt;

&lt;p&gt;The script picks up the pending pod and binds it to a node. After pressing Ctrl+C, the pod status is checked again:&lt;/p&gt;

&lt;p&gt;kubectl get pods -o wide&lt;/p&gt;

&lt;p&gt;Success! The pod is now running on a specific node, scheduled by the custom scheduler.&lt;/p&gt;

&lt;p&gt;Figure 3: The my-scheduler.sh script in action, binding the pod to a node&lt;/p&gt;

&lt;p&gt;Figure 4: The nginx pod now shows as Running with a node assignment&lt;/p&gt;

&lt;p&gt;The Power of nodeName: Bypassing the Scheduler&lt;/p&gt;

&lt;p&gt;Kubernetes also provides a way to bypass scheduling altogether using the nodeName field. This directly assigns a pod to a specific node without any scheduler involvement.&lt;/p&gt;

&lt;p&gt;The lab first returns to the previous directory:&lt;/p&gt;

&lt;p&gt;cd ..&lt;/p&gt;

&lt;p&gt;The options for nodeName under pod.spec can be reviewed:&lt;/p&gt;

&lt;p&gt;kubectl explain pod.spec | more&lt;/p&gt;

&lt;p&gt;Direct Node Assignment with nodeName&lt;/p&gt;

&lt;p&gt;The yaml file is then updated to directly specify worker-2 as the target node:&lt;/p&gt;

&lt;p&gt;cat &amp;lt; nginx_scheduler.yaml&lt;br&gt;
apiVersion: v1&lt;br&gt;
kind: Pod&lt;br&gt;
metadata:&lt;br&gt;
  creationTimestamp: null&lt;br&gt;
  labels:&lt;br&gt;
    run: nginx&lt;br&gt;
  name: nginx&lt;br&gt;
spec:&lt;br&gt;
  nodeName: worker-2&lt;br&gt;
  containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;image: nginx
name: nginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configuration is applied:&lt;/p&gt;

&lt;p&gt;kubectl apply -f nginx_scheduler.yaml&lt;/p&gt;

&lt;p&gt;Checking the pod status confirms it has been scheduled directly to worker-2:&lt;/p&gt;

&lt;p&gt;kubectl get pods -o wide&lt;/p&gt;

&lt;p&gt;Result: The pod appears on worker-2 immediately, bypassing the scheduler entirely. This demonstrates how nodeName provides a direct path from pod specification to node assignment.&lt;/p&gt;

&lt;p&gt;Figure 5: The pod scheduled directly to worker-2 using nodeName specification&lt;/p&gt;

&lt;p&gt;The pod is removed for the next demonstration:&lt;/p&gt;

&lt;p&gt;kubectl delete pod/nginx --now&lt;/p&gt;

&lt;p&gt;nodeSelector: Label-Based Scheduling (Further Study)&lt;/p&gt;

&lt;p&gt;Another approach to pod placement involves using nodeSelector, which leverages node labels for targeting specific nodes. This method provides more flexibility than nodeName while still maintaining control over placement.&lt;/p&gt;

&lt;p&gt;Examining Node Labels&lt;/p&gt;

&lt;p&gt;The worker-1 node is examined to view its labels:&lt;/p&gt;

&lt;p&gt;kubectl describe node/worker-1 | more&lt;/p&gt;

&lt;p&gt;The labels section at the top reveals various metadata, including kubernetes.io/hostname=worker-1. This label becomes the selector for targeting this specific node.&lt;/p&gt;

&lt;p&gt;Figure 6: Examining the labels on worker-1 node&lt;/p&gt;

&lt;p&gt;Figure 7: The kubernetes.io/hostname label that will be used in nodeSelector&lt;/p&gt;

&lt;p&gt;Configuring nodeSelector&lt;/p&gt;

&lt;p&gt;The yaml file is updated to use nodeSelector with the hostname label:&lt;/p&gt;

&lt;p&gt;cat &amp;lt; nginx_scheduler.yaml&lt;br&gt;
apiVersion: v1&lt;br&gt;
kind: Pod&lt;br&gt;
metadata:&lt;br&gt;
  creationTimestamp: null&lt;br&gt;
  labels:&lt;br&gt;
    run: nginx&lt;br&gt;
  name: nginx&lt;br&gt;
spec:&lt;br&gt;
  nodeSelector:&lt;br&gt;
    kubernetes.io/hostname: worker-1&lt;br&gt;
  containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;image: nginx
name: nginx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The update is applied:&lt;/p&gt;

&lt;p&gt;kubectl apply -f nginx_scheduler.yaml&lt;/p&gt;

&lt;p&gt;Verification shows the pod has been scheduled to worker-1:&lt;/p&gt;

&lt;p&gt;kubectl get pods -o wide&lt;/p&gt;

&lt;p&gt;Observation: Unlike nodeName, nodeSelector still goes through the scheduler but constrains the scheduling decision to nodes matching the specified labels. This approach offers more flexibility and is considered a best practice for most use cases.&lt;/p&gt;

&lt;p&gt;Figure 8: The nodeSelector configuration using the hostname label&lt;/p&gt;

&lt;p&gt;Cleanup&lt;/p&gt;

&lt;p&gt;Finally, all lab resources are cleaned up:&lt;/p&gt;

&lt;p&gt;kubectl delete pod/nginx --now&lt;br&gt;
rm -rf simple-kubernetes-scheduler-example&lt;br&gt;
rm -rf nginx_scheduler.yaml&lt;/p&gt;

&lt;p&gt;Key Takeaways&lt;/p&gt;

&lt;p&gt;Through this lab, several important concepts are demonstrated:&lt;/p&gt;

&lt;p&gt;Scheduler workflow: Filtering → Scoring → Binding&lt;/p&gt;

&lt;p&gt;Custom schedulers: How to create and target custom scheduling logic&lt;/p&gt;

&lt;p&gt;The binding process: How pods get assigned to nodes&lt;/p&gt;

&lt;p&gt;Direct node assignment: Using nodeName to bypass scheduling entirely&lt;/p&gt;

&lt;p&gt;Label-based scheduling: Using nodeSelector for flexible, constraint-based placement&lt;/p&gt;

&lt;p&gt;Scheduling Methods Compared&lt;/p&gt;

&lt;p&gt;The lab explored three distinct approaches to pod scheduling:&lt;/p&gt;

&lt;p&gt;Custom Scheduler (schedulerName): Provides complete control over scheduling logic, ideal for specialized placement algorithms&lt;/p&gt;

&lt;p&gt;Direct Assignment (nodeName): Bypasses the scheduler entirely, useful for debugging or specific fixed placements&lt;/p&gt;

&lt;p&gt;Label-Based Selection (nodeSelector): Combines scheduler intelligence with constraint-based node selection, offering the best balance for most production scenarios&lt;/p&gt;

&lt;p&gt;When Would One Use This?&lt;/p&gt;

&lt;p&gt;Custom schedulers prove valuable when:&lt;/p&gt;

&lt;p&gt;Specialized placement logic is needed for specific workloads&lt;/p&gt;

&lt;p&gt;Default scheduler constraints don't meet requirements&lt;/p&gt;

&lt;p&gt;Advanced placement strategies are being implemented (GPU scheduling, network topology awareness, etc.)&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Understanding Kubernetes scheduling unlocks a deeper level of cluster control. While the default scheduler handles most scenarios effectively, knowing how to implement custom scheduling provides the flexibility to optimize for specific use cases.&lt;/p&gt;

&lt;p&gt;The ability to bypass or customize scheduling demonstrates Kubernetes' modular design philosophy - providing sensible defaults while allowing deep customization when needed.&lt;/p&gt;

&lt;p&gt;This lab successfully demonstrates that the scheduler's "magic" is actually a well-designed, understandable process that can be customized to meet unique requirements.&lt;/p&gt;

&lt;p&gt;What's Next?&lt;/p&gt;

&lt;p&gt;Having mastered Kubernetes scheduling mechanisms, the next topic in this series will explore Kubernetes Storage - another critical component for running stateful workloads in production environments. 🚀&lt;/p&gt;

&lt;p&gt;For those interested in diving deeper into Kubernetes internals, this hands-on approach provides an excellent foundation for understanding how the platform orchestrates containerized workloads at scale.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
