Why the Scheduler Cares About Your Resource Declarations
Before diving into bin packing or QoS, you need to understand the two numbers that drive every scheduling decision: requests and limits.
Requests are a promise to the scheduler. When you declare cpu: "250m" as a request, you are telling Kubernetes: "This container needs at least 250 millicores of CPU to function." The scheduler uses requests — not limits — to decide whether a Node has room for your Pod. If a Node has 1000m of CPU capacity and three Pods each requesting 250m, the scheduler considers 750m consumed and 250m free, regardless of what those Pods are actually using at runtime.
Limits are a ceiling enforced at runtime by the Linux kernel's cgroups mechanism. If a container tries to use more CPU than its limit, it gets throttled. If it tries to use more memory than its limit, it gets killed with an OOMKill (Out Of Memory Kill) signal.
A container's resource block declares both numbers side by side — a memory and CPU request the scheduler reserves against the node's capacity, and a memory and CPU limit enforced at runtime, independent of each other. The gap between requests and limits is where things get interesting — and dangerous. A Pod requesting 256Mi but allowed to burst to 512Mi might be fine in isolation, but if ten such Pods all burst simultaneously on the same Node, the Node runs out of memory and the kernel starts killing processes. This is called memory overcommit, and it is the root cause of most mysterious Pod evictions in production.
Interviewers frequently ask: "What's the difference between CPU throttling and OOMKill?" The answer reveals whether you understand the asymmetry between CPU and memory. CPU is compressible — exceeding a CPU limit causes throttling (slowness), not termination. Memory is incompressible — exceeding a memory limit causes the container to be immediately killed. This asymmetry is why setting memory limits conservatively is more critical than setting CPU limits conservatively.