Kubernetes Architecture: Control Plane and Node

K
Kai··5 min read

Before typing the first command, we should know what we're talking to. A Kubernetes cluster splits into two clear parts: the control plane — the brain that makes decisions, and the nodes — where applications actually run. Understand this diagram once and every kubectl command from here on has a place to sit.

This series doesn't dig deep into each component's internals (how etcd reaches consensus, how the scheduler scores nodes...) — that's for a later deep-dive series. Here we grasp the role of each piece and how they coordinate.

The big picture

                         ┌──────────────── CONTROL PLANE (the brain) ────────────┐
                         │                                                       │
   kubectl  ──(YAML)──►  │   kube-apiserver  ◄──► etcd (stores cluster state)    │
                         │        ▲                                              │
                         │        │  scheduler (picks a node for the pod)        │
                         │        │  controller-manager (keeps desired state)    │
                         └────────┼──────────────────────────────────────────────┘
                                  │ (commands down to nodes)
            ┌─────────────────────┼─────────────────────┐
            ▼                     ▼                     ▼
      ┌─── NODE 1 ───┐      ┌─── NODE 2 ───┐      ┌─── NODE 3 ───┐
      │ kubelet      │      │ kubelet      │      │ kubelet      │
      │ kube-proxy   │      │ kube-proxy   │      │ kube-proxy   │
      │ [container]  │      │ [container]  │      │ [container]  │
      └──────────────┘      └──────────────┘      └──────────────┘

The golden rule to remember: everything goes through kube-apiserver. kubectl never talks directly to a node; it sends requests to the API server. The other components do the same. The API server is the single gateway in and out of the cluster.

Control plane: the brain

Four main components, each with one job:

  • kube-apiserver — the cluster's front door. Every command (kubectl, the controllers, kubelet) goes through here. It receives requests, authenticates, validates, then writes to etcd. It's the only component that talks directly to etcd.
  • etcd — the key-value database that stores the entire state of the cluster: what you declared, what's currently running. If the cluster is a body, etcd is its memory. Lose etcd and you lose the cluster — so in production it's backed up carefully.
  • kube-scheduler — when a new pod hasn't been assigned a node, the scheduler decides which node it runs on, based on free resources, constraints, affinity... It only picks; it doesn't start the container itself.
  • kube-controller-manager — runs the control loops (remember Article 0): continuously comparing desired state with actual state and acting to reconcile them. For example, the ReplicaSet controller ensures "want 3 copies, always have 3".

(In the cloud there's also a cloud-controller-manager to talk to the provider's infrastructure — creating load balancers, volumes... On minikube we don't need to worry about it.)

Node: where applications run

A node is a machine (real or virtual) that runs your workloads. Each node has:

  • kubelet — Kubernetes' "agent" on each node. It gets the list of pods to run on its node from the API server, then instructs the container runtime (Docker/containerd) to start the containers, and reports health back to the control plane.
  • kube-proxy — handles networking: it maintains the network rules so a Service (Article 5) routes traffic to the right pods, even when the pods change. This is the piece that makes "one stable address for many pods" work.
  • container runtime — the software that actually runs containers (containerd, CRI-O, or Docker). Kubernetes talks to it through the CRI standard.

A nice detail: the control plane components themselves in minikube also run as pods — Kubernetes runs itself. We can see that right away.

Inspecting the real architecture in minikube

kubectl cluster-info shows where the control plane is running:

Kubernetes control plane is running at https://127.0.0.1:50639
CoreDNS is running at https://127.0.0.1:50639/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

And here's the interesting part — listing the pods in the kube-system namespace (where the system components live), we see exactly the names we just learned:

kubectl get pods -n kube-system
NAME                               READY   STATUS    RESTARTS   AGE
coredns-7d764666f9-4hhdj           1/1     Running   0          37s
etcd-minikube                      1/1     Running   0          43s
kube-apiserver-minikube            1/1     Running   0          43s
kube-controller-manager-minikube   1/1     Running   0          44s
kube-proxy-npmxl                   1/1     Running   0          37s
kube-scheduler-minikube            1/1     Running   0          43s
storage-provisioner                1/1     Running   0          42s

The whole brain sits right here: etcd, kube-apiserver, kube-controller-manager, kube-scheduler, kube-proxy. Two more worth mentioning:

  • coredns — the cluster's internal DNS. Thanks to it, a pod can call another service by name (my-service) instead of by IP. We'll see this clearly in Article 5.
  • storage-provisioner — a minikube addon that automatically provisions disk when you ask for it (Article 8).

Since minikube is single-node, that node is both the control plane and where workloads run. A production cluster keeps them separate: control plane nodes don't run user applications. But the components' roles are identical.

How a request travels through the system

Let's tie it together with an example — when you say "run 3 copies of the application":

1. kubectl sends the YAML up   ──►  kube-apiserver
2. apiserver writes "want 3 copies"  ──►  etcd
3. controller-manager sees: want 3, have 0  ──►  creates 3 pods (written via apiserver)
4. scheduler sees 3 pods with no node  ──►  assigns each pod to a node
5. kubelet on that node asks apiserver "any pods of mine?"  ──►  starts the containers
6. kube-proxy updates the network rules so they're reachable

No component "commands" another directly. They all read/write state through the API server and react — a loosely coupled but resilient architecture. This is also why Kubernetes self-heals well: each controller quietly pulls its own piece of work back to the desired state.

Wrap-up

A Kubernetes cluster consists of a control plane (the brain) and nodes (the muscle). The control plane has kube-apiserver (the single gateway, everything goes through it), etcd (stores all state), the scheduler (picks a node for the pod) and the controller-manager (runs control loops to keep desired state). Each node has kubelet (runs & reports pods), kube-proxy (networking for Services) and a container runtime. In minikube, these very components run as pods in kube-system — we can see them with our own eyes. The point to burn into memory: every interaction goes through the API server, and the system operates through state-reconciliation loops.

Enough theory. In Article 2 we install minikube + kubectl properly and run our first commands on a cluster of our own.