KubernetesXXVIII · Node ArchitectureNode architecture
The Node object — cluster view of a worker host
What you'll learn
- Read a Node object and identify every field the cluster uses
- Trace the lifecycle of a Node from registration to deletion
- Distinguish addresses, capacity, allocatable, and conditions
- Identify the operator actions that change a Node object
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
The Node object is the cluster’s view of a worker host. It
is a Kubernetes resource (apiVersion: v1, kind: Node,
cluster-scoped) that records the node’s identity, addresses,
capacity, conditions, and taints. The scheduler and the
kubelet both read the Node object; the former to schedule
Pods, the latter to keep the node’s status up to date. This
lesson walks the fields and the lifecycle.
The Node object at a glance
# Substitute your own value before running:
NODE=worker-03
kubectl get node "$NODE" -o yaml
apiVersion: v1
kind: Node
metadata:
name: node-1
uid: a1b2c3d4-...
labels:
kubernetes.io/hostname: node-1
node-role.kubernetes.io/worker: ""
annotations:
node.alpha.kubernetes.io/ttl: "0"
spec:
podCIDR: 10.244.1.0/24
podCIDRs:
- 10.244.1.0/24
unschedulable: false
taints: []
status:
addresses:
- type: InternalIP
address: 10.0.5.21
- type: Hostname
address: node-1
- type: ExternalIP
address: 54.10.20.30
capacity:
cpu: "16"
memory: 64Gi
pods: "110"
ephemeral-storage: 100Gi
allocatable:
cpu: "15800m"
memory: 63154880Ki
pods: "110"
ephemeral-storage: 94Gi
conditions:
- type: Ready
status: "True"
lastHeartbeatTime: "2026-08-16T10:00:00Z"
lastTransitionTime: "2026-08-16T09:00:00Z"
reason: KubeletReady
message: kubelet is posting ready status
images:
- names: [registry.example.com/api:1.0.0]
sizeBytes: 123456789
The object has three sections: metadata (identity),
spec (cluster’s intent), and status (node’s reported
state). The metadata and spec are typically set by the
cluster (during registration) and by the operator (via
kubectl cordon or kubectl taint). The status is set
by the kubelet during its periodic sync.
The addresses
The status.addresses field is a list of typed addresses.
The cluster uses these to reach the node and to identify
the node to the cluster’s external systems.
The standard types:
InternalIP: the node’s IP address on the cluster’s internal network (the address other nodes use to reach the kubelet).ExternalIP: the node’s IP address on the public network (or the address the cloud provider exposes).Hostname: the node’s hostname, as reported by the kubelet.InternalDNS: the node’s DNS name on the cluster’s internal DNS.ExternalDNS: the node’s DNS name on the public DNS.
The cluster’s CNI plugin may use the InternalIP to
route Pod traffic. The --node-ip flag on the kubelet
overrides the InternalIP; this is useful when the node
has multiple interfaces and the cluster should route
through a specific one.
The capacity and allocatable
The status.capacity field is the node’s total resource
capacity. The status.allocatable field is the capacity
minus the kubelet’s reserved resources and the eviction
threshold.
# Substitute your own value before running:
NODE=worker-03
kubectl describe node "$NODE" | grep -A 10 "Capacity"
Capacity:
cpu: 16
memory: 64Gi
pods: 110
ephemeral-storage: 100Gi
Allocatable:
cpu: 15800m
memory: 63154880Ki
pods: 110
ephemeral-storage: 94Gi
The difference between capacity and allocatable is the
reserved resources:
cpu:16 - 15800m = 200mreserved for the kubelet, the system, and the eviction threshold.memory:64Gi - 63154880Ki ≈ 700Mireserved for the kubelet and the system.ephemeral-storage:100Gi - 94Gi = 6Gireserved for the kubelet’s housekeeping and the eviction threshold.
The scheduler uses the allocatable value, not the
capacity. The Pod’s request must fit within the
allocatable minus the requests of the existing Pods.
The conditions
The status.conditions field is a list of typed
conditions. The cluster and the operator rely on these
to make scheduling decisions.
The standard conditions:
| Condition | True means | False means |
|---|---|---|
Ready | Kubelet is healthy and serving | Kubelet is failing or unresponsive |
MemoryPressure | Memory is fine | Memory is under pressure |
DiskPressure | Disk is fine | Disk is under pressure |
PIDPressure | PIDs are fine | PIDs are under pressure |
NetworkUnavailable | Network is configured | Network is not configured |
The Ready condition is the primary signal. The cluster’s
node controller treats Ready=False as a failure and
applies the not-ready NoExecute taint after the grace
period.
# Substitute your own value before running:
NODE=worker-03
kubectl describe node "$NODE" | grep -A 5 "Conditions"
Conditions:
Type Status LastHeartbeatTime Reason
---- ------ ----------------- ------
Ready True 2026-08-16T10:00:00Z KubeletReady
MemoryPressure False 2026-08-16T10:00:00Z KubeletHasInsufficientMemory
DiskPressure False 2026-08-16T10:00:00Z KubeletHasInsufficientMemory
PIDPressure False 2026-08-16T10:00:00Z KubeletHasInsufficientPID
The taints
The spec.taints field is a list of taints applied to the
node. The cluster itself applies the well-known taints
(not-ready, pressure, unschedulable) when the corresponding
condition is set. The operator can add additional taints
via kubectl taint.
# Substitute your own value before running:
NODE=worker-03
kubectl get node "$NODE" -o jsonpath='{.spec.taints}' | jq
[
{ "key": "dedicated", "value": "prod", "effect": "NoSchedule" }
]
The scheduler reads the taints and rejects the node for any Pod that does not tolerate them.
The lifecycle
The Node object is created when the kubelet registers with the API server. The kubelet then runs a periodic sync loop that updates the node’s status (addresses, conditions, capacity, allocatable, images).
stateDiagram-v2
[*] --> Registering: kubelet starts
Registering --> Available: node controller admits
Available --> Available: status updates every 10s
Available --> NotReady: kubelet fails
NotReady --> Available: kubelet recovers
Available --> Cordoned: kubectl cordon
Cordoned --> Available: kubectl uncordon
Available --> Draining: kubectl drain
Draining --> Available: kubectl uncordon
Available --> Deleting: kubectl delete node
Deleting --> [*]
The phases:
- Registering: the kubelet sends a
POSTto the Node API endpoint. The node controller validates the kubelet’s credentials and assigns a CIDR. - Available: the node is ready to accept Pods.
- NotReady: the kubelet has failed to report Ready
for the grace period. The cluster adds the
not-readytaint. - Cordoned: the operator has set
spec.unschedulable: trueviakubectl cordon. No new Pods are scheduled. - Draining: the operator has run
kubectl drainto evict all Pods and cordon the node. - Deleting: the operator has run
kubectl delete node. The API server removes the object; the kubelet panics on its next sync.
Operator actions
The operator interacts with the Node object through well-known commands:
kubectl cordon <name>: setsspec.unschedulable: trueand adds theunschedulableNoSchedule taint.kubectl uncordon <name>: clears the field and removes the taint.kubectl drain <name>: cordons, then evicts all Pods with the eviction API.kubectl taint <name> <key>=<value>:<effect>: adds a taint.kubectl label <name> <key>=<value>: adds a label.kubectl delete node <name>: deletes the object.
The Node object is a cluster-scoped resource; deleting it removes the kubelet’s registration. The kubelet panics on its next sync and the node is gone from the cluster.
The label inventory
The Node object’s labels are the operator’s primary means of identifying a node class. The standard labels:
kubernetes.io/hostname: the node’s hostname.node.kubernetes.io/instance-type: the cloud provider’s instance type.topology.kubernetes.io/zone: the cloud provider’s zone.topology.kubernetes.io/region: the cloud provider’s region.kubernetes.io/os: the node’s OS (linux).kubernetes.io/arch: the node’s CPU architecture (amd64,arm64).node.kubernetes.io/roleornode-role.kubernetes.io/<role>: the node’s role (worker, infra, etc.).
The cluster’s NodeFeatureDiscovery (NFD) adds labels for
hardware features (CPU flags, GPU presence, kernel modules).
The label inventory is the ground truth for affinity
rules and Pod scheduling decisions.
Quiz
Knowledge check · 4 questions
Q1. What is the difference between a Node's `capacity` and its `allocatable`?
Q2. If a Node reports equal `capacity` and `allocatable`, no resources have been reserved for the system.
Q3. Recover from a Node object that was deleted from the API without the host being drained first.
`node-9` looked NotReady after a hypervisor blip and an operator ran `kubectl delete node node-9` to tidy up. The host is in fact still running: 18 Pods, including `mysql-0` with an attached ReadWriteOnce volume, are still executing on it with no Node object behind them. Replacement Pods for the same workloads are already being created elsewhere.
Q4. `kubectl cordon node-3` succeeds. Name the two things that change on the Node object as a result, and say what each one means for Pods already running there.
Passing score: 75%. Answers are checked in this browser.
Production discipline
- The
Readycondition is the primary signal. The cluster’s node controller treatsReady=Falseas a failure. Operators should alert onReady=Falsefor more than 5 minutes. - Allocatable is the scheduler’s capacity. A Pod’s
request must fit within allocatable minus the other
Pods’ requests. The
capacityvalue is the hardware’s raw capacity. - Taints are part of the audit. A node with a taint that the cluster did not apply is a forgotten taint. The audit checks every node’s taints against the cluster’s intent.
- Drain before delete. Deleting a node without draining is a data-loss risk. The data on the node (the Pods’ volumes, the container’s scratch storage) is lost.
- Use the cloud provider’s labels for affinity. The
cluster’s scheduler reads the standard labels
(
topology.kubernetes.io/zone,node.kubernetes.io/instance-type) for top-level topology decisions. A custom label scheme is a maintenance burden.