Every debugging session eventually asks which component is responsible; you need the map.
A cluster has a control plane and worker nodes.
The control plane:
kube-apiserver: the front door. Every client (kubectl, controllers, kubelets) talks only to it, over HTTPS. It validates and stores objects.etcd: a consistent key-value store holding all cluster state. Only the API server talks to it.kube-scheduler: picks a node for each new pod that has no node yet.kube-controller-manager: runs the built-in controllers (Deployment, ReplicaSet, Node, Job...), each a loop that reconciles one kind of object.cloud-controller-manager(on cloud): creates load balancers and handles node lifecycle via the cloud API.
Each worker node runs:
kubelet: watches the API for pods assigned to its node and starts them through a container runtime (usually containerd) via the CRI interface.kube-proxy(or a CNI plugin that replaces it): implements Service virtual IPs.- a CNI network plugin giving every pod its own IP.
Components never call each other directly; they all watch and update objects through the API server. The scheduler writes spec.nodeName on a pod; the kubelet on that node sees it and starts the containers.
On managed services (EKS, GKE, AKS) the provider runs the control plane; you see only your nodes.
Example
kubectl cluster-info
# Kubernetes control plane is running at https://127.0.0.1:6443
kubectl get nodes -o wide
# NAME STATUS ROLES VERSION INTERNAL-IP
# control-plane Ready control-plane v1.35.0 172.18.0.2
# worker-1 Ready <none> v1.35.0 172.18.0.3
# On kubeadm/kind clusters the control plane itself runs as pods:
kubectl get pods -n kube-system
# etcd-control-plane, kube-apiserver-control-plane,
# kube-scheduler-..., kube-controller-manager-..., kube-proxy-..., coredns-...Remember
- All components talk only through the API server
- etcd stores the state; the scheduler assigns nodes; controllers reconcile
- kubelet on each node turns assigned pods into running containers
Watch out for
- Thinking the scheduler starts containers: it only sets the pod's node; the kubelet runs it.