Skip to main content
RunBook Academy

KubernetesXXVIII · Node ArchitectureNode architecture

The Node object — cluster view of a worker host

Advanced⏱ ~17 minkubectl

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

Not yet marked complete on this device.

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 = 200m reserved for the kubelet, the system, and the eviction threshold.
  • memory: 64Gi - 63154880Ki ≈ 700Mi reserved for the kubelet and the system.
  • ephemeral-storage: 100Gi - 94Gi = 6Gi reserved 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:

ConditionTrue meansFalse means
ReadyKubelet is healthy and servingKubelet is failing or unresponsive
MemoryPressureMemory is fineMemory is under pressure
DiskPressureDisk is fineDisk is under pressure
PIDPressurePIDs are finePIDs are under pressure
NetworkUnavailableNetwork is configuredNetwork 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:

  1. Registering: the kubelet sends a POST to the Node API endpoint. The node controller validates the kubelet’s credentials and assigns a CIDR.
  2. Available: the node is ready to accept Pods.
  3. NotReady: the kubelet has failed to report Ready for the grace period. The cluster adds the not-ready taint.
  4. Cordoned: the operator has set spec.unschedulable: true via kubectl cordon. No new Pods are scheduled.
  5. Draining: the operator has run kubectl drain to evict all Pods and cordon the node.
  6. 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>: sets spec.unschedulable: true and adds the unschedulable NoSchedule 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/role or node-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

  1. Q1. What is the difference between a Node's `capacity` and its `allocatable`?

  2. Q2. If a Node reports equal `capacity` and `allocatable`, no resources have been reserved for the system.

  3. 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.

  4. 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 Ready condition is the primary signal. The cluster’s node controller treats Ready=False as a failure. Operators should alert on Ready=False for more than 5 minutes.
  • Allocatable is the scheduler’s capacity. A Pod’s request must fit within allocatable minus the other Pods’ requests. The capacity value 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.