← Learn··Updated 31 May 2026·7 min read

Kubernetes clusters: the control plane and nodes

What a Kubernetes cluster is actually made of — the control plane that decides, the worker nodes that run things, and how k3s collapses all of it into one binary.

Kubernetes
Kubernetes from scratch with k3sPart 2 of 9
#kubernetes
#k3s
#architecture
#ai-assisted

level:Beginner

ℹ️ Read-along — concepts only; nothing to install. The examples are illustrative.

In the previous part you saw what Kubernetes does: you describe the state you want, and it works to make reality match. This part opens the box and shows the machinery that actually does that work. A Kubernetes cluster is not one program — it is a small set of cooperating services, split cleanly into two halves. One half decides what should happen; the other half makes it happen. Once you can name the parts and say which half they live in, the rest of this series stops feeling like a wall of jargon and starts feeling like a system you can reason about.

What a cluster is made of

What a cluster is

A cluster is a set of machines that Kubernetes treats as one pool of capacity. You do not log into individual machines to run workloads; you talk to the cluster as a whole, and it decides where things land. Each machine in that pool is a node — usually a virtual machine in a homelab, sometimes a bare-metal box in a data centre. A cluster is just nodes plus the control software that coordinates them.

Those nodes split into two roles. A small number run the control plane — the brain that holds the cluster's desired state and makes scheduling decisions. The rest are worker nodes, which run your actual containers. The control plane manages the cluster, while the nodes run the application workloads. In a big production cluster these are separate machines; in k3s, as you will see, one machine can do both.

flowchart TD
  subgraph CP["Control plane (the brain)"]
    A["kube-apiserver<br/><i>front door</i>"]
    B["etcd<br/><i>datastore</i>"]
    C["kube-scheduler<br/><i>placement</i>"]
    D["controller-manager<br/><i>reconciliation</i>"]
  end
  subgraph W["Worker nodes (the muscle)"]
    E["Node 1<br/><i>kubelet + runtime</i>"]
    F["Node 2<br/><i>kubelet + runtime</i>"]
  end
  A --> B
  A --> C
  A --> D
  A -->|"instructs"| E
  A -->|"instructs"| F

Everything talks through the API server. The control plane decides; the worker nodes run the containers. All cluster operations transit through the API server.

The control plane

The control plane is the set of components that make global decisions about the cluster — scheduling, responding to failures, holding the source of truth. These components can run on any machine in the cluster, but for simplicity are usually started together on the same machine. There are four pieces worth knowing by name.

kube-apiserver — the front door

The kube-apiserver exposes the Kubernetes API and is the front end for the control plane. Every interaction with the cluster — your kubectl commands, the scheduler, the controllers, even the worker nodes reporting in — goes through it. Nothing in Kubernetes talks to anything else directly; it all flows through this one component. That sounds like a bottleneck, but it is what makes the system coherent: there is exactly one door, and everything that passes through it is authenticated, authorized, and validated.

etcd — the source of truth

etcd is a consistent and highly-available key-value store used as Kubernetes' backing store for all cluster data. When you create a Deployment, the fact that you want it lives in etcd. It is the only stateful component in the control plane and the single source of truth for the entire cluster — which is why backing it up is the same thing as backing up the cluster's brain.

ℹ️ Note etcd stores desired and observed state, not your application data. Your databases and files live in volumes on the worker nodes, covered in config, secrets, and storage. If etcd is wiped, the cluster forgets what it was supposed to be running — but the data on disk is untouched.

kube-scheduler — placement

The kube-scheduler watches for newly created Pods with no assigned node, and selects a node for them to run on. It does not start anything itself; it only decides where. It weighs each candidate node against the pod's requirements — requested CPU and memory, node labels, affinity rules, data locality — and writes its choice back through the API server. Placement and execution are deliberately separate jobs.

kube-controller-manager — reconciliation

The kube-controller-manager runs controller processes — control loops that watch the state of the cluster and make or request changes to move the current state toward the desired state. If you asked for three replicas and one dies, a controller notices the gap between three wanted and two running and asks for a replacement. This is the "self-healing" reflex you will meet properly in deployments and self-healing — and it is the heart of why Kubernetes feels alive rather than static.

💡 Tip A clean way to hold the four in your head: the API server is the door, etcd is the memory, the scheduler is the placement decision, and the controllers are the watchers that keep nudging reality back toward the plan. Three of them are pure decision-makers; only etcd holds state.

The worker nodes

A worker node is any machine that runs your containers. The control plane never runs your application containers itself — it tells nodes what to run and the nodes do it. Worker node components run on every node, maintaining running pods and providing the Kubernetes runtime environment. Three pieces live on every node.

kubelet — the node agent

The kubelet is an agent that runs on each node and makes sure that containers are running in a Pod. It takes the pod specs the API server hands it, tells the container runtime to start those containers, then continuously reports their health back upward. The kubelet is the control plane's hands on each machine — it is the component that turns a decision into a running process.

kube-proxy — node networking

The kube-proxy is a network proxy that runs on each node, maintaining network rules that allow communication to your Pods from inside or outside the cluster. It is what makes a Service's stable virtual IP actually route to a live pod, wherever that pod happens to be running. You will see this in action in services and networking.

the container runtime — running the containers

The container runtime is the software responsible for running containers. Kubernetes does not run containers itself; it delegates to a runtime — most commonly containerd — through a standard interface called the CRI. The kubelet asks the runtime to pull an image and start a container, and the runtime does the low-level work of talking to the kernel.

⚠️ Warning "Worker node" does not mean a node has no control-plane role. In k3s, a single machine commonly runs both the full control plane and the worker components, so one box is both brain and muscle. The roles are about function, not separate hardware — keep them straight conceptually even when they share a machine.

How a request actually flows

Naming the parts is one thing; watching them cooperate is what makes the design click. Here is what happens, end to end, when you create a workload — say kubectl apply of a pod.

The request is authenticated, authorized, and run past admission controllers, then persisted to etcd. At that moment the pod exists as a record but has no node — it sits in Pending. The scheduler, which watches for unassigned pods, picks a suitable node and writes that assignment back through the API server. The kubelet on the chosen node sees the assignment, tells the container runtime to pull the image and start the containers, and reports status back up. Once at least one primary container is up, the pod moves from Pending to Running.

flowchart TD
  A["kubectl apply"] -->|"submits spec"| B["kube-apiserver"]
  B -->|"persists"| C["etcd<br/><i>pod = Pending</i>"]
  D["kube-scheduler"] -->|"watches, picks node"| B
  B -->|"assignment"| E["kubelet on node"]
  E -->|"start containers"| F["container runtime"]
  E -->|"reports Running"| B

Notice that the scheduler and kubelet never talk to each other directly — they coordinate entirely through the API server and etcd. The scheduler watches for the new pod, assigns a node, and the kubelet on that node does the rest.

This indirection is the whole trick. Because every component reads and writes the same source of truth instead of calling each other, any one of them can crash and restart without breaking the others — they just resume watching the API server where they left off.

How k3s collapses all of this

Everything above describes standard Kubernetes, where each component is a separate process you install, configure, secure, and upgrade. That is a lot of moving parts, and it is exactly the operational weight that makes a from-scratch Kubernetes install painful.

k3s encapsulates the operation of all Kubernetes control-plane components in a single binary and process, and bundles the worker pieces too. The terminology shifts slightly: in k3s a control-plane machine is called a server and a worker is called an agent. An agent node runs the k3s agent and has no datastore or control-plane components; both servers and agents run the kubelet, container runtime, and CNI. The roles are identical to upstream Kubernetes — k3s just packages them so you do not assemble them by hand.

The datastore is the clearest example of the simplification. A standard cluster stands up an etcd cluster; k3s uses SQLite as the default datastore for a single-server install, and will use it if no other datastore is configured and no embedded etcd files are present. For high availability you switch to an embedded etcd datastore across two or more server nodes — but a single server with SQLite is a complete, conformant cluster, fine well beyond just learning.

💡 Tip Mapping the words: k3s server = control-plane node, k3s agent = worker node. When you read the k3s docs and the upstream Kubernetes docs side by side, that one substitution removes most of the confusion.

A short close

A Kubernetes cluster is two halves that talk through a single door. The control plane — API server, etcd, scheduler, controller-manager — decides what should be true. The worker nodes — kubelet, kube-proxy, container runtime — make it true. Every component coordinates through the API server and the shared state in etcd, never directly with each other, which is why the whole thing is so resilient. k3s takes all of that and folds it into one binary with a server-and-agent vocabulary, so you can stand up a real cluster without assembling the parts yourself.

That is exactly what the next part does — install a k3s server, join an agent, and watch kubectl get nodes return the cluster you just read about. If you want the lighter backstory on the name, why Kubernetes is called k8s is a quick detour, and the series hub has the whole path laid out.