Wrong resource settings cause most mysterious OOMKills, throttling and unschedulable pods.
Each container can declare resources.requests and resources.limits for cpu (cores: 500m = half a core) and memory (256Mi, 1Gi).
- Requests are what the scheduler uses: a pod fits on a node only if the sum of requests fits the node's allocatable capacity. They also set the CPU share under contention.
- Limits are enforced by the kernel through cgroups. Exceeding the memory limit gets the container OOMKilled (exit code 137). Exceeding the CPU limit only throttles it; it slows down but isn't killed.
The combination gives a QoS class:
Guaranteed: every container has requests equal to limits for CPU and memory.Burstable: some requests or limits set.BestEffort: none at all; first to be evicted when a node runs short of memory.
Practical advice:
- Always set memory requests, and a memory limit close to it; memory isn't compressible.
- Set CPU requests from measured usage. Many teams omit CPU limits to avoid needless throttling of latency-sensitive services.
- Use
kubectl top pods(needs metrics-server) to see real usage. - A
LimitRangecan give defaults in a namespace and aResourceQuotacaps its total.
Runtimes such as recent JVMs, Go and Node read the cgroup limits; check yours does before relying on that.
Example
containers:
- name: api
image: ghcr.io/acme/api:2.1.0
resources:
requests:
cpu: 250m # scheduler reserves a quarter core
memory: 512Mi
limits:
memory: 512Mi # above this: OOMKilled
# no cpu limit: may burst into idle CPU
# kubectl top pod -l app=api
# NAME CPU(cores) MEMORY(bytes)
# api-6f-x1 180m 430Mi
# kubectl get pod api-6f-x1 -o jsonpath='{.status.qosClass}' # BurstableRemember
- Requests drive scheduling; limits are enforced at runtime
- Memory over limit = OOMKilled; CPU over limit = throttled
- QoS: Guaranteed, Burstable, BestEffort (evicted first)
Watch out for
- Setting no requests at all: pods get packed onto nodes and are evicted or starved under load.