Topic 4.1
Requests, Limits & Quality of Service
In one line
A resource REQUEST tells the scheduler what a pod needs to be placed at all; a LIMIT caps what it's allowed to use — get this wrong and a pod either can't be scheduled anywhere or gets killed for using too much.
Think of it like this
Booking a hotel room. Your REQUEST is the room size you told the hotel you need when booking (they won't confirm the reservation without knowing this) — your LIMIT is the absolute maximum the room can ever physically hold before something has to give, regardless of what you originally booked.
Key ideas
- 01
A REQUEST is what a container declares it needs, and the SCHEDULER uses this number specifically to decide which node has enough available capacity to place it — a pod requesting more CPU/memory than any single node has free will simply remain
Pendingforever, never scheduled, until enough capacity becomes available somewhere. - 02
A LIMIT is the maximum a container is allowed to actually use at runtime — exceeding a CPU limit results in THROTTLING (the container is slowed down, not killed); exceeding a MEMORY limit results in the container being KILLED immediately with an
OOMKilledstatus (Phase 6 covers this failure mode directly), since memory, unlike CPU, can't simply be throttled the same way. - 03
Kubernetes assigns every pod a QUALITY OF SERVICE (QoS) class based on how its requests and limits compare:
Guaranteed(requests exactly equal limits, for every container and every resource — the highest protection, evicted last under node pressure),Burstable(requests are set but lower than limits — evicted before Guaranteed pods), andBestEffort(no requests or limits set at all — evicted FIRST under any node resource pressure). - 04
A pod with NO requests or limits set at all (BestEffort) might schedule successfully and run fine under light load, but offers zero protection when a node comes under real memory or CPU pressure — it's the first thing killed to free up resources for anything with a higher QoS class, making it a genuinely risky default for anything that actually matters in production.
- 05
kubectl top podsandkubectl top nodes(requiring the metrics-server component to be installed) show ACTUAL current CPU/memory usage, letting you compare real usage against your configured requests and limits — genuinely essential for setting these values based on real observed behavior rather than guessing.
In your stack
- →
A genuinely critical, well-known gotcha for Java specifically: the JVM's default heap sizing historically didn't account for CONTAINER memory limits at all (it looked at the HOST's total memory instead) — an app with a 512Mi container memory limit but a JVM that decides to use 2GB of heap gets
OOMKilledalmost immediately. Modern JVMs (10+) respect container limits automatically via-XX:+UseContainerSupport(on by default), but explicitly setting-XX:MaxRAMPercentageis still standard practice to leave headroom for non-heap memory (thread stacks, metaspace) within the same container limit.
Code & diagrams
The scheduler cares about requests; the running container is bounded by limits — two different jobs, two different consequences.
A Guaranteed QoS pod — requests exactly equal limits.
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app
image: my-registry/my-app:1.5.0
resources:
requests:
cpu: "500m" # 0.5 of one CPU core
memory: "512Mi"
limits:
cpu: "500m" # equal to request = Guaranteed QoS
memory: "512Mi"See real usage against configured requests/limits, and confirm a pod's QoS class.
kubectl apply -f resources.yaml
# Confirm the assigned QoS class
kubectl get pod my-app -o jsonpath='{.status.qosClass}'
# Guaranteed
# See ACTUAL current usage (requires metrics-server)
kubectl top pod my-app
# Compare against a node's total allocatable resources
kubectl describe node <node-name> | grep -A5 "Allocated resources"
# Deliberately trigger OOMKilled to see it happen
# (set memory limit lower than what the app actually needs, then:)
kubectl get pod my-app
# STATUS: OOMKilled once it exceeds its memory limitExplain it without notes
What's the practical difference between exceeding a CPU limit versus exceeding a memory limit, in terms of what actually happens to the container?
Why does a pod with no resource requests or limits set at all pose a genuine risk in production, even if it runs perfectly fine most of the time?
Practice
Create a pod with requests exactly equal to limits, confirm its QoS class is Guaranteed, then create one with no resources set at all and confirm it's BestEffort.
Using kubectl top pod (with metrics-server installed), compare a running pod's actual real-time usage against its configured requests and limits — is it over-provisioned, under-provisioned, or about right?
Trade-offs
- ↔
Setting requests/limits precisely (matching real observed usage) maximizes cluster efficiency and protects against noisy-neighbor problems, but requires genuine ongoing observation and tuning as an application's real usage changes over time — setting them too conservatively (much higher than actually needed 'to be safe') wastes real, reservable cluster capacity that could otherwise be used by other workloads, which is a genuinely common, avoidable inefficiency in real clusters.
Done when you can
I understand the difference between what happens when a CPU limit versus a memory limit is exceeded.
I can explain Kubernetes' three QoS classes and how they're derived from requests and limits.
I know why a pod with no requests or limits set is a real production risk, not just a missing optimization.