Cloud Native 37 min read

Deep Dive into Kubernetes Scheduler: From Scheduling Cycle to Managing Ten‑Thousands of Pods

This guide explains why the kube‑scheduler becomes a bottleneck in large clusters, walks through its architecture, scheduling cycle, queue mechanics, constraint handling, plugin framework, multi‑scheduler designs, production‑grade tuning, observability, and real‑world case studies for achieving stable, high‑throughput pod placement.

Cloud Architecture
Cloud Architecture
Cloud Architecture
Deep Dive into Kubernetes Scheduler: From Scheduling Cycle to Managing Ten‑Thousands of Pods

1. Why Scheduling Failures Appear as Resource Shortage

Many teams encounter pending pods even when node CPU is only 45% and memory is low; the root cause is scheduler degradation under conditions such as massive pod creation, combined podAntiAffinity, topologySpreadConstraints, taints, heterogeneous node pools, priority preemption, and frequent node status flapping.

In these scenarios the kube‑scheduler is no longer a simple "place pod on an empty node" but a distributed decision engine balancing constraints, resource fragmentation, latency, fairness, and scalability.

2. Scheduler as a Constraint Solver

The scheduler solves the problem: given a pod and a changing set of nodes, select the optimal node that satisfies hard constraints while minimizing fragmentation, zone imbalance, priority fairness, and throughput latency.

Feasibility : pod must fit on at least one node (CPU, memory, taints, etc.).

Local optimality : choose the best node among feasible ones (requires filtering and scoring).

Global stability : avoid resource fragmentation and hotspots (needs long‑term balancing).

System throughput : maintain scheduling speed when many pods arrive (limits computation cost).

This creates an engineering "impossible triangle": finer scheduling reduces throughput, higher throughput makes decisions coarser, and aggressive resource utilization increases fragmentation risk.

3. Scheduler Position in the Control Plane

The scheduling flow is part of the control‑plane event loop:

User/Controller creates a Pod (nodeName empty).

API Server persists the Pod.

Scheduler watches unscheduled Pods.

Scheduling cycle: queue → filter → score → select.

Binding cycle: bind Pod to Node.

API Server updates spec.nodeName.

Kubelet on the target node starts the containers.

3.1 Scheduler Responsibilities

Watch unscheduled Pods.

Read cluster state from the local cache.

Select a target node for each Pod.

Bind the Pod by setting spec.nodeName.

It does not create containers, pull images, configure networking, or handle application start failures – those are performed by the kubelet and container runtime.

3.2 Cache‑Based Design

The scheduler never queries the API server for every decision; it maintains an informer‑driven cache of nodes, pods, PVCs, taints, etc. This cache enables high‑frequency decisions without a strong consistency guarantee.

3.3 No Global Optimum

In clusters of hundreds to thousands of nodes, the scheduler first finds a feasible node set, then scores a limited subset, and finally binds quickly to keep the queue moving.

4. Scheduling Cycle Decomposition

Since v1.19 the Scheduling Framework defines two main phases for a single pod:

Scheduling Cycle : decide the node.

Binding Cycle : perform the bind.

The configuration API version kubescheduler.config.k8s.io/v1 is stable; v1beta3 was removed in v1.29.

4.1 Full Scheduling Flow

PreEnqueue / QueueSort
    -> PreFilter
    -> Filter
    -> PostFilter
    -> PreScore
    -> Score
    -> NormalizeScore
    -> Reserve
    -> Permit
    -> PreBind
    -> Bind
    -> PostBind

The flow can be viewed in three semantic layers:

Queue layer – decides who is scheduled first.

Feasibility layer – filters nodes that satisfy hard constraints.

Decision & execution layer – scores and binds the chosen node.

4.2 QueueSort

Pods are ordered by priority, backoff status, nominated node, and retry status. High‑priority bursts can starve low‑priority pods.

4.3 PreFilter / Filter

Hard‑constraint checks include CPU/memory, nodeSelector / nodeAffinity, taints/tolerations, podAffinity/anti‑affinity, topologySpreadConstraints, PVC binding, and port conflicts. Expensive checks are often podAntiAffinity, large topology spreads, and complex volume bindings.

4.4 Score

After filtering, the Score phase applies plugins such as NodeResourcesFit, NodeResourcesBalancedAllocation, ImageLocality, PodTopologySpread, InterPodAffinity, and TaintToleration. Over‑stacking plugins can dramatically increase per‑pod scheduling cost and cause throughput collapse during releases.

4.5 Reserve / Permit / Bind

These stages ensure that concurrent schedulers do not race for the same resource slot, providing a stateful reservation before the final bind.

5. Internal Queue Mechanics

The scheduler queue consists of three parts:

ActiveQ : pods currently eligible for scheduling.

BackoffQ : pods that recently failed and are waiting before retry.

UnschedulablePods : pods that cannot be scheduled under current constraints.

Backoff defaults are 1 s ( podInitialBackoffSeconds) and 10 s ( podMaxBackoffSeconds). Large batch releases (e.g., 800 pods) can quickly fill BackoffQ and Unschedulable sets, amplifying throughput consumption.

5.1 Common Failure Reasons

0/300 nodes are available:
 60 Insufficient cpu,
 45 node(s) had untolerated taint,
 120 node(s) didn't match Pod anti-affinity rules,
 75 node(s) didn't satisfy existing pods anti-affinity rules

Usually the dominant cause is rule conflict rather than pure CPU shortage.

6. Scheduler Framework Value

The framework makes the scheduler extensible via plugins instead of forking the whole codebase.

6.1 When to Use Plugins

Custom node scoring.

Business‑specific scheduling constraints.

Cost‑aware placement.

Fine‑grained bin‑packing or hotspot avoidance.

6.2 When Not to Use Plugins

Simple zone‑level spreading.

Basic affinity or taint‑toleration.

Batch‑task avoidance of online services.

In these cases native capabilities ( nodeAffinity, taints/tolerations, topologySpreadConstraints, priorityClass) are preferred.

6.3 Multi‑Scheduler Scenarios

Separate schedulers for online services, GPU workloads, and batch jobs avoid policy interference and improve isolation.

7. Native Capability First

Before writing plugins, leverage native features such as topologySpreadConstraints instead of strict podAntiAffinity, and use taints to separate high‑cost node pools.

7.1 Example: Replace Strict Anti‑Affinity

podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
  - labelSelector:
      matchLabels:
        app: payment
    topologyKey: kubernetes.io/hostname

Switch to a topology spread constraint that allows flexible distribution while still preventing same‑node placement.

7.2 Example: Taint‑Based Isolation

apiVersion: v1
kind: Node
metadata:
  name: gpu-node-01
spec:
  taints:
  - key: workload-type
    value: gpu
    effect: NoSchedule

Workloads that need GPUs add a matching toleration; mis‑labelled pods will never land on expensive nodes.

8. High‑Throughput Production Tuning

8.1 Limit Nodes Scanned per Pod

The percentageOfNodesToScore parameter controls how many nodes are examined after a feasible set is found. In clusters of 300+ nodes, tuning this value (e.g., 20 % for default, 100 % for GPU profiles) balances latency and fragmentation.

8.2 Reduce Expensive Constraint Coverage

Identify the three most costly rules: mandatory podAntiAffinity, many topologySpreadConstraints, and complex volume bindings. Apply them only to critical services; use softer constraints for the rest.

8.3 Separate Online and Offline Schedulers

Deploy default-scheduler for latency‑sensitive services and a dedicated batch-scheduler (or gpu-scheduler) for batch and AI workloads, each with its own node pool and taint‑based isolation.

8.4 Include Autoscaling in the Scheduling Loop

Pending pods often stem from slow node provisioning. Integrate Cluster Autoscaler or Karpenter, pre‑warm node images, and accelerate image pulls to avoid “still no place to put” loops.

9. Multi‑Scheduler Architecture

A typical production layout includes:

API Server → default-scheduler (online services)

API Server → gpu-scheduler API Server → batch-scheduler Node pools isolated by taints/labels.

Descheduler for secondary rebalancing.

9.1 Sample KubeSchedulerConfiguration

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
  percentageOfNodesToScore: 25
- schedulerName: gpu-scheduler
  percentageOfNodesToScore: 100
  plugins:
    score:
      disabled:
      - name: "*"
      enabled:
      - name: NodeResourcesFit
        weight: 3
      - name: ImageLocality
        weight: 1
      - name: TaintToleration
        weight: 2

Key points: default scheduler scans a fraction of nodes for throughput; GPU profile scans all nodes with higher scoring granularity.

9.2 Pod‑Specific Scheduler Selection

apiVersion: apps/v1
kind: Deployment
metadata:
  name: recommendation-inference
spec:
  replicas: 6
  template:
    spec:
      schedulerName: gpu-scheduler
      tolerations:
      - key: workload-type
        operator: Equal
        value: gpu
        effect: NoSchedule
      containers:
      - name: app
        image: registry.example.com/reco-inference:4.2.1
        resources:
          requests:
            cpu: "8"
            memory: "24Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "8"
            memory: "24Gi"
            nvidia.com/gpu: "1"

10. Production‑Ready Custom Plugin: Tenant‑Cost Score

The plugin reads node annotations (cost tier, tenant allow‑list) from the local cache and returns a score without remote calls.

10.1 Node Annotation Example

apiVersion: v1
kind: Node
metadata:
  name: worker-az1-03
  labels:
    node-pool: online-general
    topology.kubernetes.io/zone: az1
  annotations:
    scheduling.example.com/cost-tier: "gold"
    scheduling.example.com/tenant-allowlist: "payment,reco,search"

10.2 Scoring Logic (simplified)

If the pod's tenant is not in the node's allow‑list, return a low score (5).

For tenant payment, give 100 for gold, 80 for silver, otherwise 40.

For tenant analytics, give 100 for bronze, 60 for silver, otherwise 20.

Default case: 90 for silver, 70 for gold, else 60.

The plugin is registered with weight 5 in the scheduler configuration.

11. Observability of the Scheduler

11.1 Queue‑Level Metrics

ActiveQ length.

Unschedulable pod count.

BackoffQ size.

Failure‑reason distribution.

High ActiveQ indicates the scheduler cannot keep up; high BackoffQ shows repeated failures; high Unschedulable signals structural constraint issues.

11.2 Latency & Throughput Metrics

Pod‑level scheduling latency percentiles.

Filter and Score stage durations.

Successful pods per second.

Bind latency.

Example Prometheus query to get 99th‑percentile extension‑point latency:

histogram_quantile(
  0.99,
  sum(rate(scheduler_framework_extension_point_duration_seconds_bucket[5m])) by (extension_point, le)
)

11.3 Business‑Facing Metrics

Time from Deployment creation to Ready.

Pending duration during bulk releases.

Queueing latency for high‑priority services.

Recovery time for preempted pods.

12. Real‑World Case Study: Large‑Scale Release Snow‑Avalanche

12.1 Background

Cluster: 420 nodes, ~16 000 pods.

Release: 38 services, ~1 100 new pods.

Core services used mandatory node‑level anti‑affinity and cross‑AZ topology spreads.

Batch jobs still running during the release window.

12.2 Symptoms

Many pods pending > 90 s.

High‑priority services preempted batch jobs.

Preempted pods re‑queued, creating a feedback loop.

Cluster Autoscaler added nodes, but they became Ready only after 3–5 min.

Scheduler 99th‑percentile latency kept rising.

12.3 Root‑Cause Decomposition

Strict anti‑affinity narrowed candidate nodes.

Release batch size too large, flooding the queue.

Online and batch workloads shared the same scheduler and node pool.

Autoscaling lagged behind the rapid retry cycle.

12.4 Remediation Actions

Replace mandatory anti‑affinity with topology spread constraints and softer distribution.

Separate online and batch workloads into different schedulers and node pools.

Throttle release to ≤ 80 pods per batch.

Keep high priority for critical services but make most background tasks non‑preempting.

Reduce percentageOfNodesToScore for the default profile.

Shrink batch jobs before the promotion window.

12.5 Outcome

Critical services' average pending time dropped from 46 s to < 4 s.

Scheduler 99th‑percentile latency improved > 60 %.

Preemption events decreased dramatically.

Release window stability increased noticeably.

13. Production Configuration Baseline

13.1 Strategy Layering

Online services: prioritize stability, cross‑AZ spread, moderate node dispersion.

Offline tasks: prioritize cost and packing, tolerate queuing.

GPU workloads: prioritize exact resource matching, allow finer scans.

13.2 Constraint Ordering

Use taints/tolerations to separate workload domains.

Apply nodeAffinity to select the appropriate node pool.

Apply topologySpreadConstraints for zone and node dispersion.

Use inter‑pod affinity/anti‑affinity only when necessary.

13.3 Release Governance

Rate‑limit large rolling updates.

Avoid queuing hundreds of pods simultaneously.

Clear low‑priority batch jobs before critical release windows.

Release system should monitor pending duration and auto‑throttle.

13.4 Capacity Management

Reserve buffer capacity for online services.

Distinguish "schedulable capacity" from total raw resources.

Include image warm‑up, PVC binding, and node‑Ready latency in scaling decisions.

14. Common Pitfalls

Assuming Pending always means insufficient resources – often rule conflicts.

Believing stricter anti‑affinity always improves availability – can hurt during failures or scaling.

Equating higher priority with higher SLA – leads to priority inflation.

Assuming custom plugins are always superior – native features are simpler and more maintainable.

Focusing only on scheduler parameters – real issues involve workload design, release pipelines, node‑pool planning, autoscaling, and observability.

15. Pre‑Production Checklist

15.1 Policy Correctness

Validate necessity of strict anti‑affinity.

Check for conflicting nodeAffinity and topologySpreadConstraints.

Ensure high‑priority is reserved for truly critical services.

15.2 Performance Validation

Benchmark batch creation of 100, 500, 1000 pods.

Identify Filter/Score hotspots.

Assess impact of node‑scan percentage on fragmentation.

15.3 Failure Scenarios

Verify business can recover from preemptions.

Test flexible distribution when a node pool fails.

Ensure Descheduler does not cause cascade evictions.

15.4 Operability

Integrate scheduler metrics into Prometheus/Alertmanager.

Provide a FailedScheduling reason dashboard.

Make release system aware of prolonged Pending states.

16. Conclusion

The Kubernetes scheduler is not just a component that places pods; it is the core decision engine that determines whether a platform can sustain high concurrency, complex constraints, and rapid resource fluctuations. Mastering its internals, leveraging native capabilities first, extending with lightweight plugins when needed, and closing the loop with observability, capacity planning, and release governance turns scheduling from a hidden bottleneck into a reliable platform service.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

schedulerSchedulinghigh-throughputmulti-schedulerkube-schedulerproduction-tuning
Cloud Architecture
Written by

Cloud Architecture

Focuses on cloud‑native and distributed architecture engineering, sharing practical solutions and lessons learned. Covers microservice governance, Kubernetes, observability, and stability engineering to help your systems run stable, fast, and cost‑effectively.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.