Back to Blog

Node Allocatable & the Kubelet Resource Managers for the CKA Exam: kube-reserved, system-reserved, Eviction Thresholds, enforceNodeAllocatable, CPU Manager Static Policy, Memory Manager & Topology Manager

A hands-on CKA guide to why a node's Allocatable is smaller than its Capacity and what the kubelet does with the difference — the Allocatable formula (Capacity − kubeReserved − systemReserved − evictionHard), configuring kubeReserved/systemReserved/reservedSystemCPUs in KubeletConfiguration, what enforceNodeAllocatable (pods, kube-reserved, system-reserved and the -compressible variants) actually enforces through cgroups, the worked 16-CPU/32Gi example, reading Capacity vs Allocatable vs Allocated resources in kubectl describe node, then the CPU Manager static policy (exclusive CPUs for Guaranteed integer-CPU Pods, the shared pool, cpu_manager_state, full-pcpus-only and strict-cpu-reservation), the Memory Manager Static policy and reservedMemory formula, and the Topology Manager scopes/policies including the TopologyAffinityError Terminated Pods that the scheduler never retries.

By Sailor Team , October 19, 2026

Run kubectl describe node on any worker and you will see two blocks that never match: Capacity and Allocatable. On a kubeadm node with 16 CPUs and 32 GiB, Allocatable may read 15.5 CPU and a little over 31 GiB. On a managed cloud node the gap is far larger. Most CKA candidates know the one-line explanation (“the kubelet reserves resources for itself and the OS”) and stop there, which is fine until a troubleshooting task hands you a Pod stuck Pending with Insufficient cpu on a node that is “obviously” half empty, or a Pod that was scheduled, started, and then went Terminated with a reason you have never seen.

This guide is about that gap and the kubelet machinery around it. The first half covers Node Allocatable: the formula, the KubeletConfiguration fields that set it, what enforceNodeAllocatable actually does to cgroups, and how to read the numbers. The second half covers the three kubelet resource managers that decide which CPUs and which memory a container gets once it has been admitted: the CPU Manager, the Memory Manager and the Topology Manager. All of it sits in the CKA’s Cluster Architecture, Installation & Configuration and Troubleshooting domains, because every piece is a kubelet configuration change on a node you administer. Feature stages below are from the current Kubernetes documentation (v1.37); every feature discussed is GA unless noted.

Capacity, Allocatable and Allocated: Three Numbers That Get Confused

Start by reading a node properly:

kubectl describe node worker-1 | sed -n '/^Capacity:/,/^Events:/p'
Capacity:
  cpu:                16
  ephemeral-storage:  102687672Ki
  hugepages-2Mi:      0
  memory:             32780152Ki
  pods:               110
Allocatable:
  cpu:                15500m
  ephemeral-storage:  94636830047
  hugepages-2Mi:      0
  memory:             32063352Ki
  pods:               110
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests      Limits
  --------           --------      ------
  cpu                12250m (79%)  14 (90%)
  memory             20Gi (65%)    28Gi (90%)
FieldWho sets itMeaning
Capacitykubelet, from the hardware (cAdvisor)Everything the machine has
Allocatablekubelet, from Capacity minus reservationsWhat the scheduler may hand to Pods; the scheduler never oversubscribes it
Allocated resourcescomputed by kubectl describe from Pod requests on that nodeHow much of Allocatable is already promised; percentages are of Allocatable

The scheduler’s Insufficient cpu message compares a Pod’s requests to Allocatable minus the sum of existing requests, never to live usage and never to Capacity. That single sentence resolves most “but the node is idle” confusion: a node whose containers are idle but request 15.3 of 15.5 allocatable CPUs cannot take a 500m Pod. Our workloads and scheduling guide covers the request/limit side; this guide covers where the 15.5 came from.

The Node Allocatable Formula

Allocatable = Capacity − kubeReserved − systemReserved − evictionHard
  • kubeReserved: resources for Kubernetes system daemons (kubelet, container runtime). Not for daemons that run as Pods; those are ordinary Pods with requests.
  • systemReserved: resources for OS daemons (sshd, udev, journald) and kernel memory, which is not charged to Pods.
  • evictionHard: the hard eviction thresholds. Only memory and ephemeral-storage evictions exist, so the eviction term is subtracted from those two resources only. CPU has no eviction threshold and so no eviction term.

Each reservation supports cpu, memory, ephemeral-storage and pid. The documentation’s worked example is worth memorising because its arithmetic is exactly what an exam question can ask you to reproduce:

InputValue
Capacity16 CPU, 32Gi memory, 100Gi ephemeral storage
kubeReservedcpu: 1000m, memory: 2Gi, ephemeral-storage: 1Gi
systemReservedcpu: 500m, memory: 1Gi, ephemeral-storage: 1Gi
evictionHardmemory.available: 500Mi, nodefs.available: 10%
Allocatable CPU16 − 1 − 0.5 = 14.5
Allocatable memory32 − 2 − 1 − 0.5 = 28.5Gi
Allocatable storage100 − 1 − 1 − 10 = 88Gi

Consequences: the scheduler keeps total Pod memory requests at or below 28.5Gi and storage at or below 88Gi; the kubelet evicts Pods when Pod memory usage exceeds 28.5Gi or disk exceeds 88Gi (that is the pods enforcement explained next); and if every process on the node burned all the CPU it could, Pods collectively could not exceed 14.5 CPUs.

If kubeReserved and systemReserved are not enforced (the default) and the daemons overshoot their reservations, Pods still get evicted, but only when node-level memory usage crosses 31.5Gi (Capacity minus the 500Mi eviction threshold) or disk crosses 90Gi. In other words the reservations shape what the scheduler promises, and the eviction threshold is the actual safety line at the node level. The eviction ordering (who dies first) is covered in our KCSA denial-of-service guide; here we only need the thresholds.

The Linux defaults for evictionHard, which apply when you set nothing, are:

evictionHard:
  memory.available: "100Mi"
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
  imagefs.available: "15%"

So even a node with no explicit reservations has Allocatable memory 100Mi below Capacity and Allocatable storage 10% below Capacity. That is the small gap you see on a stock kubeadm node.

Configuring Reservations in KubeletConfiguration

Everything lives in the kubelet’s config file, which kubeadm writes to /var/lib/kubelet/config.yaml from the kubelet-config ConfigMap in kube-system. A complete, conservative reservation block looks like this:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
kubeReserved:
  cpu: "500m"
  memory: "1Gi"
  ephemeral-storage: "1Gi"
  pid: "1000"
systemReserved:
  cpu: "500m"
  memory: "1Gi"
  ephemeral-storage: "1Gi"
  pid: "1000"
evictionHard:
  memory.available: "200Mi"
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
  imagefs.available: "15%"
enforceNodeAllocatable:
  - pods

Apply it the way you would in the exam:

sudo vim /var/lib/kubelet/config.yaml        # edit the fields above
sudo systemctl restart kubelet
kubectl get node worker-1 -o jsonpath='{.status.allocatable}{"\n"}'

Allocatable updates on the next node status report. The pid reservation is worth noticing: it reduces the number of process IDs available to Pods, which is how you leave headroom for the kubelet and runtime during a fork storm (the KCSA guide’s PID-exhaustion section explains the attack this defends against).

A quick way to confirm what the running kubelet believes, without reading files on the node:

kubectl get --raw "/api/v1/nodes/worker-1/proxy/configz" | python3 -m json.tool | grep -A4 -E 'kubeReserved|systemReserved|evictionHard|enforceNodeAllocatable'

What enforceNodeAllocatable Actually Does

The reservations above change a number (Allocatable). Enforcement is separate: it is the kubelet applying cgroup limits so that groups of processes cannot exceed their share. enforceNodeAllocatable is a list, and its valid values are:

ValueWhat the kubelet enforcesRequires
pods (default)All Pods together may not exceed Allocatable. Implemented by setting limits on the top-level Pod cgroup (kubepods.slice under systemd). When total Pod usage exceeds Allocatable, Pods are evicted.cgroupsPerQOS: true (default)
kube-reservedKubernetes daemons may not exceed kubeReservedkubeReservedCgroup set to the cgroup that contains the kubelet and runtime
system-reservedOS daemons may not exceed systemReservedsystemReservedCgroup set
kube-reserved-compressibleOnly the compressible resources (CPU) of kubeReservedkubeReservedCgroup
system-reserved-compressibleOnly the compressible resources of systemReservedsystemReservedCgroup
enforceNodeAllocatable: [pods, kube-reserved-compressible, system-reserved-compressible]
kubeReservedCgroup: /runtime.slice     # kubelet + containerd live here
systemReservedCgroup: /system.slice    # everything systemd started

Three facts about this that catch people:

  1. The kubelet does not create kubeReservedCgroup or systemReservedCgroup. They must already exist, and with the systemd driver the name must follow the systemd .slice convention. If a configured cgroup is missing or invalid, the kubelet fails to start. That is a plausible exam troubleshooting scenario: kubelet down, journalctl -u kubelet complaining about a cgroup, fix the config and restart.
  2. Enforcing system-reserved is dangerous. Memory is incompressible. If sshd, the kernel and journald collectively exceed systemReserved.memory, something in system.slice gets OOM-killed, and it might be sshd. The documentation says to do it only after exhaustive profiling, and the -compressible variants exist so you can cap CPU (which merely throttles) without risking OOM kills.
  3. The recommended rollout order is: enforce pods (default); once you have monitoring on daemon usage, add the -compressible variants; then non-compressible kube-reserved based on observed usage; and only if truly necessary, non-compressible system-reserved.

For the exam the realistic task is the first row: Allocatable is computed from reservations, pods enforcement is on by default, and the two daemon enforcements are opt-in and need pre-existing cgroups.

reservedSystemCPUs: Reserving Specific CPUs

kubeReserved.cpu and systemReserved.cpu reserve an amount of CPU. reservedSystemCPUs reserves an explicit set:

reservedSystemCPUs: "0-1"

When set, it overrides the CPU values of kubeReserved and systemReserved for the purpose of deciding which CPUs are kept out of the Pod pool, and it is the mechanism meant for latency-sensitive (telco/NFV) nodes where you want OS daemons and interrupts pinned to known cores. Note that Kubernetes only keeps Pods off those CPUs; actually moving daemons and IRQs onto them needs OS tooling such as tuned or systemd’s CPUAffinity. This option is what makes the CPU Manager’s static policy predictable, which is where the second half of this guide begins.

The CPU Manager

By default, Linux’s Completely Fair Scheduler shares CPUs among all containers: requests become CFS shares (relative weight) and limits become CFS quota (a hard cap per period). That is fine for web services and terrible for workloads sensitive to cache misses, context switches and scheduler jitter. The kubelet’s CPU Manager (GA since v1.26) addresses this with two policies.

cpuManagerPolicyBehaviour
none (default)Every container uses the shared pool; CFS shares and quota do the work
staticContainers in Guaranteed Pods that request an integer number of CPUs get exclusive CPUs (a dedicated cpuset). Everyone else uses the shared pool

Who gets exclusive CPUs

The qualification has two parts and both are tested:

  1. The Pod must be Guaranteed QoS: every container has requests equal to limits for both CPU and memory (or only limits, which Kubernetes copies to requests).
  2. The container must request an integer number of CPUs (cpu: "2", not "1500m").

A Guaranteed container with cpu: 1500m runs on the shared pool. A Burstable container with cpu: "4" runs on the shared pool. Only Guaranteed-and-integer containers are pinned.

apiVersion: v1
kind: Pod
metadata:
  name: pinned
spec:
  containers:
  - name: dpdk
    image: registry.k8s.io/pause:3.10
    resources:
      requests: { cpu: "2", memory: "1Gi" }
      limits:   { cpu: "2", memory: "1Gi" }

The shared pool is every CPU not exclusively assigned to a container. Reserved CPUs (from reservedSystemCPUs, or the kubeReserved/systemReserved CPU amounts rounded up) are taken from the shared pool first, in ascending physical core order, and are never handed out exclusively. The static policy therefore requires a non-zero CPU reservation; without one the shared pool could be emptied by pinned containers and the kubelet refuses the configuration. With the strict-cpu-reservation option (GA in v1.35) reserved CPUs are removed from the shared pool entirely, so even Burstable Pods never run on them; without it, the shared pool includes reserved CPUs for non-exclusive workloads.

Enabling it, and the state-file trap

cpuManagerPolicy: static
cpuManagerReconcilePeriod: 10s          # default follows nodeStatusUpdateFrequency
kubeReserved:
  cpu: "1"                               # must be > 0 for static
cpuManagerPolicyOptions:
  full-pcpus-only: "true"

If you simply change the policy and restart the kubelet, it crashloops with:

could not restore state from checkpoint: configured policy "static" differs from
state checkpoint policy "none", please drain this node and delete the CPU manager
checkpoint file "/var/lib/kubelet/cpu_manager_state" before restarting Kubelet

The correct procedure, which is a textbook exam task, is:

kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
sudo systemctl stop kubelet
sudo rm /var/lib/kubelet/cpu_manager_state
sudo vim /var/lib/kubelet/config.yaml    # set cpuManagerPolicy: static (+ reservation)
sudo systemctl start kubelet
kubectl uncordon worker-1

The same drain-delete-restart dance is needed whenever the set of online CPUs changes, and when you add strict-cpu-reservation. Our node maintenance guide covers the drain itself.

Static policy options

Options are passed as cpuManagerPolicyOptions (or --cpu-manager-policy-options) and gated by stage: beta options are visible by default (CPUManagerPolicyBetaOptions), alpha options need CPUManagerPolicyAlphaOptions.

OptionStage (version)Effect
full-pcpus-onlyGA (1.33)Allocate whole physical cores, never a lone hyperthread sibling; prevents a neighbour’s thread from sharing your core
distribute-cpus-across-numaBeta (1.33)Spread an allocation evenly across NUMA nodes when it cannot fit in one
strict-cpu-reservationGA (1.35)Keep reserved CPUs out of the shared pool entirely
prefer-align-cpus-by-uncorecacheGA (1.36)Prefer CPUs that share an L3/uncore cache
align-by-socketAlpha (1.25)Align allocations to socket boundaries
distribute-cpus-across-coresAlpha (1.31)Spread across physical cores; incompatible with full-pcpus-only and distribute-cpus-across-numa

You will not be asked to tune these on the CKA, but full-pcpus-only is the one to recognise: it is the standard answer to “a latency-sensitive workload must not share a physical core with another container”.

Verifying pinning

# which CPUs did the container get?
kubectl exec pinned -- cat /sys/fs/cgroup/cpuset.cpus.effective
# kubelet's view of the assignments
sudo cat /var/lib/kubelet/cpu_manager_state | python3 -m json.tool

One consequence that links to a CKAD topic: Pods whose containers hold exclusive CPUs (or Memory Manager reservations) cannot be resized in place; the in-place Pod resize guide explains why.

The Memory Manager

Pinning CPUs is only half of NUMA locality: a container pinned to CPUs on socket 0 whose memory is allocated on socket 1 still pays a cross-socket penalty on every access. The Memory Manager (GA since v1.32) guarantees that Guaranteed Pods receive memory (and hugepages) from the fewest possible NUMA nodes, and it tells the Topology Manager which nodes those are.

memoryManagerPolicyBehaviour
None (default)No effect on placement; returns a neutral topology hint
Static (Linux)For Guaranteed Pods, reserves memory in its NUMA map and returns hints for the nodes where the memory can be guaranteed; enforces cpuset.mems on the Pod’s cgroup. BestEffort and Burstable Pods get the default hint and no reservation

The Static policy has a prerequisite that trips people up: you must tell the Memory Manager how the node’s reserved memory is spread across NUMA nodes with reservedMemory, and the totals must satisfy an exact equation:

Σ reservedMemory[i].memory  =  kubeReserved.memory + systemReserved.memory + evictionHard.memory.available

Because evictionHard.memory.available defaults to 100Mi, the sum is never simply the two reservations. The documentation’s example makes the arithmetic explicit:

memoryManagerPolicy: Static
kubeReserved:   { cpu: "4", memory: "4Gi" }
systemReserved: { cpu: "1", memory: "1Gi" }
reservedMemory:
- numaNode: 0
  limits:
    memory: "3Gi"
- numaNode: 1
  limits:
    memory: "2148Mi"      # 3Gi minus 100Mi, so the total is 4Gi + 1Gi + 100Mi

If the equation does not balance, the kubelet logs an error at startup and the Memory Manager does not start. Hugepages are reserved per NUMA node the same way (hugepages-1Gi: "2Gi"), and the listed sizes must exist on the machine. The Memory Manager keeps its own checkpoint at /var/lib/kubelet/memory_manager_state, with the same drain-and-delete requirement as the CPU Manager when you change policy.

The Topology Manager

CPU Manager and Memory Manager (and the Device Manager, for GPUs and SR-IOV NICs) each make allocation decisions. Without coordination they can pick different NUMA nodes for the same container. The Topology Manager (GA since v1.27) is the kubelet component that collects hints from those hint providers (each hint is a bitmask of NUMA nodes plus a preferred flag), picks one according to a policy, and admits or rejects the Pod.

Scope

topologyManagerScopeAlignment unit
container (default)Each container is aligned independently; two containers in one Pod may land on different NUMA nodes
podAll containers in the Pod are treated as one unit and placed on a single NUMA node or a common minimal set; the Pod’s effective request is the max of the app-container sum and the largest init container

Policies

topologyManagerPolicyAdmits whenOn failure
none (default)Always; no alignmentn/a
best-effortAlways; records the preferred affinity and tries to honour itPod runs anyway
restrictedOnly if a preferred (optimal) affinity existsPod rejected at admission
single-numa-nodeOnly if every requested resource fits on one NUMA nodePod rejected at admission

The failure mode is the troubleshooting fact to burn in. A Pod rejected by restricted or single-numa-node has already been scheduled to the node; the scheduler is not topology-aware. The kubelet refuses it at admission, the Pod goes to phase Failed and shows as Terminated with reason TopologyAffinityError, and the scheduler does not reschedule it, because from its point of view the Pod was placed successfully. A bare Pod simply stays dead; a Deployment or ReplicaSet creates a replacement, which may land on the same node and fail the same way. The documentation recommends a controller (Deployment/ReplicaSet) or an external loop that redeploys Pods with that error.

kubectl get pod pinned -o jsonpath='{.status.phase} {.status.reason} {.status.message}{"\n"}'
# Failed TopologyAffinityError Resources cannot be allocated with Topology locality

Which Pods it considers

Topology Manager asks hint providers about every Pod, but what they return depends on QoS and the request shape: a Guaranteed container with integer CPUs gets real CPU hints from the static CPU Manager; a Guaranteed container with 300m gets the default hint for CPU but still gets device hints if it requests a GPU; BestEffort and Burstable containers get default CPU hints. So alignment effectively applies to Guaranteed integer-CPU workloads and to anything requesting devices.

Options and limits

  • prefer-closest-numa-nodes (GA in 1.32): with best-effort or restricted, prefer NUMA node sets that are closer together (lower NUMA distance) when more than one node is needed.
  • max-allowable-numa-nodes (GA in 1.35): the Topology Manager refuses to run on nodes with more than 8 NUMA nodes by default, because enumerating affinities explodes combinatorially; this raises the cap at your own risk.

A sensible full configuration for a NUMA-sensitive worker, then, is:

cpuManagerPolicy: static
cpuManagerPolicyOptions:
  full-pcpus-only: "true"
memoryManagerPolicy: Static
reservedMemory: [...]                 # balanced per the formula above
topologyManagerPolicy: single-numa-node
topologyManagerScope: pod
reservedSystemCPUs: "0-1"

Putting It Together: Admission Order on a Node

When the scheduler binds a Pod to a node, the kubelet runs its own admission before anything starts:

  1. Fit check against Allocatable: do the Pod’s requests fit in Allocatable minus already-admitted Pods? (Usually yes, because the scheduler used the same numbers, but static Pods and races can fail here with OutOfcpu / OutOfmemory.)
  2. Topology Manager: collect hints from CPU Manager, Memory Manager and Device Manager; apply the policy; reject with TopologyAffinityError if required.
  3. CPU Manager: assign exclusive CPUs (static policy, Guaranteed integer requests) or leave the container in the shared pool.
  4. Memory Manager: pin cpuset.mems for Guaranteed Pods under the Static policy.
  5. cgroup setup: the Pod goes under kubepods.slice (and its QoS sub-slice), whose limits implement pods enforcement of Allocatable.

Only then does the container runtime pull images and start containers. Nothing in steps 2 to 4 is visible to the scheduler, which is why a Pod can be “scheduled” and still never run.

Troubleshooting Table

SymptomLikely causeCheck / fix
Pod Pending, Insufficient cpu, node looks idleRequests exceed Allocatable − allocated requests; usage is irrelevantkubectl describe node → compare Allocated resources to Allocatable; right-size requests or add nodes
Allocatable far below CapacityLarge kubeReserved/systemReserved/evictionHardInspect /var/lib/kubelet/config.yaml or the configz endpoint
Kubelet crashloops after enabling static CPU policyStale cpu_manager_stateDrain, stop kubelet, delete the file, start, uncordon
Kubelet fails to start after adding kube-reserved enforcementkubeReservedCgroup missing or not a .slice namejournalctl -u kubelet; create the cgroup or fix the name
Kubelet logs a Memory Manager error at startupreservedMemory totals ≠ kube + system reserved + eviction (100Mi default)Rebalance the per-NUMA values
Pod Failed / Terminated, reason TopologyAffinityErrorrestricted or single-numa-node could not satisfy affinity; scheduler will not retryReduce the request to fit one NUMA node, relax the policy, or run under a Deployment
Guaranteed Pod with cpu: 1500m not getting exclusive CPUsOnly integer CPU requests are pinnedRequest cpu: "2" (or accept the shared pool)
Pod OutOfcpu / OutOfmemory at admissionKubelet’s fit check lost a race or static Pods consumed the headroomCheck static Pod manifests in /etc/kubernetes/manifests; see the static Pods guide
Pod evicted although its own usage is modestpods enforcement: total Pod usage crossed Allocatable, and this Pod ranked first for evictionEviction ordering in the KCSA DoS guide; raise requests to Guaranteed where it matters
In-place resize rejected on a pinned PodStatic CPU/Memory manager assignments are fixed at admissionRecreate the Pod; see the CKAD resize guide

Common Mistakes

  • Reading Capacity instead of Allocatable. Every scheduling calculation uses Allocatable. Capacity is the hardware.
  • Thinking eviction thresholds reduce CPU Allocatable. Only memory and ephemeral storage have eviction thresholds; CPU Allocatable is Capacity minus the two reservations.
  • Assuming reservations are enforced. By default only pods is enforced. Daemon enforcement is opt-in and needs pre-existing cgroups.
  • Enabling the static CPU policy without a reservation. The kubelet requires kubeReserved.cpu, systemReserved.cpu or reservedSystemCPUs to be non-zero.
  • Changing CPU or Memory Manager policy without deleting the checkpoint file. Drain, delete /var/lib/kubelet/cpu_manager_state (or memory_manager_state), restart.
  • Expecting the scheduler to retry a TopologyAffinityError. It never does; only a controller recreating the Pod gets another attempt.
  • Forgetting the 100Mi default eviction threshold in the Memory Manager equation. The reservedMemory sum must include it.
  • Confusing reservedSystemCPUs with kubeReserved.cpu. One names specific CPU IDs to keep Pods away from; the other subtracts an amount from Allocatable. The former overrides the latter’s CPU value when both are set.

How to Practise for the CKA

Set up a two-worker kubeadm cluster (our kubeadm installation guide walks through it) and run these drills against a clock:

  1. Record Capacity and Allocatable for a worker. Add kubeReserved: {cpu: "1", memory: "1Gi"} to /var/lib/kubelet/config.yaml, restart the kubelet, and verify Allocatable dropped by exactly those amounts plus nothing else for CPU and plus the eviction threshold for memory.
  2. Create ten Pods each requesting cpu: "1" and watch the last one go Pending with Insufficient cpu while kubectl top node shows the node nearly idle. Explain the gap in one sentence.
  3. Enable cpuManagerPolicy: static the wrong way (no drain, no file deletion), read the crashloop message in journalctl -u kubelet, then fix it properly.
  4. Deploy a Guaranteed Pod with cpu: "2" and one with cpu: "1500m"; read cpuset.cpus.effective in each and confirm only the first is pinned.
  5. Break the Memory Manager equation on purpose, read the startup error, and rebalance reservedMemory.

These tasks use nothing beyond vim, systemctl, journalctl and kubectl, which is exactly the toolset the exam gives you. If you want the same drills with a timer and a grader, Sailor.sh’s Certified Kubernetes Administrator (CKA) Mock Exam Bundle runs kubelet-configuration and node-troubleshooting scenarios inside real multi-node clusters under the same remote-desktop constraints as the live exam, so you learn where your time actually goes. The CKA troubleshooting guide and the node failure lifecycle guide are the natural companions; the free CKA practice questions are a good warm-up.

Frequently Asked Questions

Why is Allocatable less than Capacity on a Kubernetes node?

Because the kubelet subtracts kubeReserved (for the kubelet and runtime), systemReserved (for OS daemons and the kernel) and, for memory and ephemeral storage, the hard eviction threshold. The scheduler only ever hands out Allocatable.

Does the scheduler look at actual CPU usage when it says Insufficient cpu?

No. It compares the Pod’s requests with Allocatable minus the requests of Pods already on the node. A node can be idle and still full.

What does enforceNodeAllocatable: [pods] do?

It makes the kubelet cap all Pods together at Allocatable through the top-level Pod cgroup and evict Pods when their combined usage exceeds it. It is the default. kube-reserved and system-reserved enforcement are separate opt-ins that require pre-existing cgroups.

Which Pods get exclusive CPUs under the static CPU Manager policy?

Containers in Guaranteed QoS Pods that request an integer number of CPUs. Fractional requests and Burstable or BestEffort Pods stay in the shared pool.

Why does the kubelet crashloop after I set cpuManagerPolicy: static?

The checkpoint file /var/lib/kubelet/cpu_manager_state still records the old policy. Drain the node, stop the kubelet, delete the file, change the config and start the kubelet again.

What is TopologyAffinityError?

The admission failure the kubelet records when the Topology Manager’s restricted or single-numa-node policy cannot satisfy a Pod’s NUMA affinity. The Pod ends up Failed/Terminated and the scheduler does not retry it; a controller must create a new Pod.

Do I need the Memory Manager to use the Topology Manager?

Only for memory and hugepage alignment. CPU alignment needs the CPU Manager static policy; device alignment needs device plugins. Topology Manager coordinates whichever hint providers are active.

Is any of this on the CKA exam?

Node Allocatable, kubectl describe node reading, kubelet configuration edits and restarts, and node troubleshooting are squarely in the Cluster Architecture, Installation & Configuration and Troubleshooting domains. The resource managers appear as kubelet configuration and as the explanation for otherwise puzzling Pod states; you are unlikely to be asked to tune NUMA policy options, but you should recognise the symptoms they cause.

Conclusion

The space between Capacity and Allocatable is where the kubelet keeps the node alive: reservations for its own daemons and the OS, an eviction threshold as the last line of defence, and cgroup enforcement that, by default, confines Pods but not daemons. Learn the formula, know the defaults, and read kubectl describe node from the Allocatable block downward, and the “idle node refuses my Pod” class of question becomes routine.

Below Allocatable sit three managers that decide which CPUs and memory a container gets. The CPU Manager pins Guaranteed integer-CPU containers, the Memory Manager keeps their memory on the same NUMA node, and the Topology Manager makes sure the two agree, rejecting Pods the scheduler cannot know are unplaceable. Their failure signatures (a kubelet crashloop over a stale checkpoint, a Memory Manager equation that will not balance, a TopologyAffinityError the scheduler never retries) are all diagnosable in two commands once you have seen them, which is the whole point of seeing them before exam day.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now