Die Kubernetes-Architektur besteht aus zwei Hälften: der Control Plane, die den Cluster steuert, und den Worker Nodes, auf denen deine Container laufen. Auf der Control Plane arbeiten kube-apiserver, etcd, kube-scheduler und kube-controller-manager; auf jedem Node laufen kubelet, kube-proxy und eine Container-Runtime. Alles kommuniziert über den API-Server, und nur er darf in etcd schreiben.
Ich betreibe seit Jahren produktive Cluster, aktuell vor allem k3s auf Hetzner mit drei Control-Plane-Nodes in drei Rechenzentren. In meinem Kubernetes-Praxisbuch bei Rheinwerk (2024) ist die Architektur ein eigenes Kapitel. Hier bekommst du die Kurzfassung, die du brauchst, um Fehler im Cluster einzuordnen. Alles Weitere zu Kubernetes sammle ich auf der Seite Kubernetes.
Wenn du noch ganz am Anfang stehst, lies vorher Kubernetes einfach erklärt. Ab hier sind Container und Cluster als Begriffe gesetzt.
Kubernetes-Cluster-Architektur: Control Plane und Worker Nodes
Ein Node ist ein Server, der zum Cluster gehört, egal ob virtuelle Maschine, Blech im Rechenzentrum oder Raspberry Pi. Kubernetes teilt diese Server in zwei Rollen. Die Control-Plane-Nodes tragen die Steuerkomponenten. Sie speichern den gewünschten Zustand, nehmen deine Anfragen entgegen und entscheiden, wo etwas läuft. Die Worker Nodes führen aus: Sie starten Container, hängen Volumes ein, reichen Secrets durch und melden zurück, ob alles läuft.
Der Punkt, den ich Einsteigern immer zuerst erkläre: Auf den Worker Nodes läuft keine Verwaltungslogik. Sie sind austauschbar. Fällt einer aus, baut Kubernetes die Container auf einem anderen Node neu auf, weil der gewünschte Zustand nicht auf dem Node liegt, sondern in etcd auf der Control Plane. Das ist der Grund, warum ein Cluster einen Serverausfall überlebt.
| Komponente |
Läuft auf |
Aufgabe |
| kube-apiserver |
Control Plane |
Einziger Eingang: nimmt Anfragen an, prüft und speichert sie |
| etcd |
Control Plane |
Key-Value-Datenbank mit dem gesamten Cluster-Zustand |
| kube-scheduler |
Control Plane |
Wählt für jeden neuen Pod den passenden Node |
| kube-controller-manager |
Control Plane |
Bündel von Controllern, die Soll und Ist abgleichen |
| cloud-controller-manager |
Control Plane |
Bindet Load Balancer, Volumes und Nodes eines Cloud-Anbieters an |
| kubelet |
jeder Node |
Startet und überwacht die Container, die der API-Server ihm zuweist |
| kube-proxy |
jeder Node |
Sorgt dafür, dass Netzwerkpakete den richtigen Pod erreichen |
| Container-Runtime |
jeder Node |
Führt die Container tatsächlich aus, zum Beispiel containerd |
Eine Übersicht aller Komponenten pflegt die offizielle Dokumentation unter Kubernetes Components. Die Tabelle oben reicht als Landkarte für den Rest des Beitrags.
kube-apiserver: der einzige Eingang in den Cluster
Jede Anfrage an Kubernetes landet beim kube-apiserver. Wenn du kubectl apply tippst, spricht kubectl mit ihm. Wenn der Scheduler einen Pod platziert, geht das über ihn. Wenn das kubelet meldet, dass ein Container gestartet ist, nimmt er die Meldung entgegen. Es gibt keinen zweiten Weg.
Der API-Server macht dabei mehr als durchreichen. Er prüft, ob ein Manifest gültig ist, ob der Aufrufer die nötigen Rechte hat und ob Quotas oder Rate-Limits greifen. Erst danach schreibt er das Objekt nach etcd. Er ist die einzige Komponente, die überhaupt eine Verbindung zu etcd hat. Alle anderen Komponenten sehen die Daten nur durch seine Brille.
Dieses Design nennt sich Hub-and-Spoke. Eine neue Komponente dockt am Hub an, statt jede andere kennen zu müssen, und nichts passiert im Cluster, ohne dass der API-Server davon weiß. Darauf bauen Watch-Anfragen auf, mit denen Controller und Werkzeuge wie ArgoCD auf Änderungen reagieren.
etcd: das Gedächtnis des Clusters und warum Backup Pflicht ist
etcd ist eine verteilte Key-Value-Datenbank, die es auch außerhalb von Kubernetes gibt. In Kubernetes speichert sie jedes Objekt, das du je angelegt hast: Deployments, Services, ConfigMaps, Secrets, den Status jedes Pods. Solange etcd intakt ist, kann sich der Cluster aus fast jeder Lage selbst wieder aufbauen, weil der gewünschte Zustand vollständig dort liegt.
Für die Hochverfügbarkeit nutzt etcd den Raft-Konsens mit einem Quorum. Das heißt: Die Mehrheit der etcd-Instanzen muss sich einig sein, bevor ein Schreibvorgang gilt. Deshalb betreibst du etcd immer in ungerader Zahl. Bei drei Instanzen darf eine ausfallen, bei fünf dürfen zwei ausfallen. Mit zwei Instanzen gewinnst du nichts, denn wenn eine ausfällt, hat die andere keine Mehrheit mehr.
Die praktische Folge: etcd ist die einzige Komponente, deren Verlust wirklich wehtut. Alle anderen kannst du neu starten oder neu installieren. Ein regelmäßiger Snapshot gehört daher zu jedem Cluster, der Produktion trägt. Bei k3s mit eingebettetem etcd geht das mit einem Befehl:
k3s etcd-snapshot save --name vor-upgrade
k3s legt den Snapshot unter /var/lib/rancher/k3s/server/db/snapshots/ ab und kann ihn auf Wunsch direkt in einen S3-Bucket hochladen. Bei mir läuft das als Zeitplan, und die Snapshots landen in einem S3-Bucket außerhalb des Clusters. Ein Backup, das auf demselben Server liegt wie die Datenbank, ist keins. Für kubeadm-Cluster ist der Weg über etcdctl snapshot save in der offiziellen Anleitung zu etcd beschrieben. Übe das Zurückspielen einmal in einem Testcluster, bevor du es im Ernstfall zum ersten Mal machst.
kube-scheduler und kube-controller-manager: wer entscheidet was
Der kube-scheduler beantwortet eine einzige Frage: Auf welchem Node soll ein neuer Pod laufen? Dafür kennt er die Kapazität jedes Nodes, die bereits reservierten Ressourcen und alle Regeln, mit denen du Einfluss nimmst, etwa Node-Affinity, Taints und Tolerations. Er ordnet den Pod einem Node zu und trägt das über den API-Server ein. Mehr macht er nicht. Er startet keinen Container. Steht ein Pod lange auf Pending, ist das meist der Scheduler, der keinen passenden Node findet, und kubectl describe pod verrät dir in den Events, warum.
Der kube-controller-manager ist ein einziges Programm, in dem viele Controller stecken. Jeder Controller läuft in einer Schleife: Ist-Zustand lesen, mit dem Soll aus etcd vergleichen, Abweichung beheben. Der ReplicaSet-Controller sorgt dafür, dass die gewünschte Anzahl Pods läuft. Der Endpoints-Controller verbindet Services mit den Pods dahinter. Der Service-Account-Controller legt Standard-Konten in neuen Namespaces an.
Am wichtigsten für den Betrieb ist der Node-Controller. Jedes kubelet erneuert regelmäßig ein Lease-Objekt im Namespace kube-node-lease, ein Heartbeat. Bleibt der Heartbeat über eine konfigurierbare Frist aus, markiert der Node-Controller den Node als NotReady, und nach einer weiteren Frist werden die Pods darauf zur Neuplanung freigegeben. Die Details zu diesem Ablauf stehen in der Dokumentation zu Nodes. Wenn ein Node bei mir ausfällt, sehe ich in Prometheus zuerst den ausbleibenden Heartbeat und ein paar Minuten später die Pods auf den anderen Nodes wieder hochkommen. Das ist der ganze Mechanismus hinter der Selbstheilung, und er läuft ohne mein Zutun.
kubelet, kube-proxy und Container-Runtime auf jedem Node
Das kubelet ist der Agent auf jedem Node. Es registriert den Node beim API-Server, holt sich von dort die Pods, die auf diesem Node laufen sollen, und sorgt dafür, dass sie laufen. Dafür spricht es mit der Container-Runtime, zieht Images, startet Container, hängt Volumes ein und legt Secrets und ConfigMaps als Dateien oder Umgebungsvariablen ab. Danach überwacht es die Container über Probes und meldet den Status zurück. Das kubelet läuft übrigens auch auf den Control-Plane-Nodes, denn dort laufen ebenfalls Container: die Steuerkomponenten selbst.
Der kube-proxy kümmert sich um das Netzwerk zu den Pods. Ein Service in Kubernetes hat in der Regel eine feste virtuelle IP, aber die Pods dahinter wechseln ständig. kube-proxy pflegt auf jedem Node die Regeln, die Pakete an diese virtuelle IP zum richtigen Pod bringen, typischerweise über iptables oder IPVS. Manche Netzwerk-Plugins ersetzen kube-proxy inzwischen durch eigene Mechanismen, das Prinzip bleibt gleich.
Die Container-Runtime ist das Stück, das am Ende den Container startet. Kubernetes spricht mit ihr über das Container Runtime Interface, das CRI. Die verbreitete Runtime ist containerd, alternativ CRI-O. Docker als Runtime brauchst du dafür nicht mehr; deine Docker-Images laufen trotzdem, weil sie dem OCI-Standard entsprechen.
Was bei kubectl apply wirklich passiert
Am Durchlauf einer einzelnen Anfrage wird das Zusammenspiel konkret. Du schickst mit kubectl apply ein Pod-Manifest an den Cluster. Vereinfacht passiert dann Folgendes:
- Der kube-apiserver prüft die Anfrage, authentifiziert dich, validiert das Manifest und speichert das Pod-Objekt in etcd. Der Pod hat noch keinen Node.
- Der kube-scheduler beobachtet über den API-Server neue Pods ohne Node, wählt anhand von Ressourcen und Regeln einen aus und schreibt die Zuordnung zurück.
- Das kubelet auf dem gewählten Node sieht den neuen Pod, lädt das Image über die Container-Runtime, startet den Container und richtet Volumes, Secrets und ConfigMaps ein.
- Die kube-proxy-Instanzen aktualisieren ihre Netzwerkregeln, sobald ein Service auf den Pod zeigt und der Endpoints-Controller ihn eingetragen hat.
- Das kubelet meldet den Status des Pods an den API-Server, der ihn in etcd speichert. Ab jetzt zeigt
kubectl get pods den Pod als Running.
Kein Schritt umgeht den API-Server. Und dieser Ablauf läuft bei jeder Änderung erneut, auch wenn du nur ein Memory-Limit erhöhst. In der Praxis erzeugst du Pods fast nie direkt. Vor dem Ablauf sitzt dann der Deployment-Controller, der ein ReplicaSet anlegt, und der ReplicaSet-Controller, der daraus die Pods macht. Was ein Pod genau ist und warum du ihn nicht von Hand anlegst, steht in Kubernetes Pod: was ist das?.
Die Komponenten im eigenen Cluster finden
Auf einem mit kubeadm oder Minikube gebauten Cluster laufen die Control-Plane-Komponenten als statische Pods im Namespace kube-system. Du kannst sie dir ansehen wie jeden anderen Pod:
kubectl get pods -n kube-system
Die Ausgabe sieht auf einem kleinen Cluster mit einem Control-Plane-Node und zwei Workern etwa so aus:
NAME READY STATUS RESTARTS AGE
coredns-76f75df574-4xk2p 1/1 Running 0 14d
coredns-76f75df574-m9zvr 1/1 Running 0 14d
etcd-cp-1 1/1 Running 0 14d
kube-apiserver-cp-1 1/1 Running 0 14d
kube-controller-manager-cp-1 1/1 Running 2 14d
kube-proxy-8h2lq 1/1 Running 0 14d
kube-proxy-tj4nw 1/1 Running 0 14d
kube-proxy-x7d9c 1/1 Running 0 14d
kube-scheduler-cp-1 1/1 Running 2 14d
Du erkennst das Muster: Die Control-Plane-Komponenten tragen den Namen des Nodes, auf dem sie laufen, kube-proxy gibt es einmal je Node, weil er als DaemonSet läuft, und CoreDNS ist der Cluster-DNS, der als Add-on dazukommt. Dass Controller-Manager und Scheduler ein paar Neustarts zeigen, ist auf einem frisch gestarteten Node meist harmlos. Bei einem kubeadm-Cluster startet das kubelet alle statischen Pods der Control Plane parallel, und Controller-Manager wie Scheduler laufen dann an, bevor der API-Server antwortet, brechen ab und versuchen es erneut. Das ist der Ablauf dieser einen Aufbauvariante, kein Gesetz: Andere Distributionen ordnen den Start ihrer Komponenten anders an, wie das Beispiel k3s gleich zeigt.
Bei k3s findest du die Komponenten dort nicht. k3s packt API-Server, Scheduler, Controller-Manager und etcd in einen einzigen Prozess, der als Systemdienst läuft. Dort schaust du mit systemctl status k3s und journalctl -u k3s nach. Das kubelet ist in beiden Fällen kein Pod, sondern ein Dienst auf dem Node, denn irgendetwas muss die ersten Container ja starten, bevor es Pods gibt.
Für den Zustand eines Nodes ist kubectl describe node <name> der wichtigste Befehl. Der Abschnitt Conditions zeigt, ob das kubelet Druck auf Speicher, Platte oder Prozess-IDs meldet, und Allocatable sagt dir, wie viel CPU und Memory der Scheduler auf diesem Node noch verteilen darf. Wer sich einen lokalen Cluster zum Ausprobieren aufsetzen will, findet die Optionen in Kubernetes lokal installieren.
Hochverfügbarkeit: was der Mittelstand wirklich braucht
Ein Cluster mit einem Control-Plane-Node funktioniert, und für Entwicklung und Test reicht das. Fällt dieser Node aus, laufen die Container auf den Workern sogar weiter, weil das kubelet sie hält. Aber du kannst nichts mehr ändern, nichts neu ausrollen, und stirbt jetzt ein Worker, wird nichts neu geplant. Für Produktion ist das zu wenig.
Drei Control-Plane-Nodes sind für die meisten Firmen, die ich berate, die richtige Größe: Das Quorum verkraftet einen Ausfall, und der gleichzeitige Verlust von zwei Rechenzentren ist ein anderes Problem als ein Cluster-Problem. Fünf Nodes lohnen sich bei sehr großen Clustern mit vielen API-Aufrufen oder wenn zwei Nodes gleichzeitig in Wartung dürfen. Meine eigenen Cluster laufen mit drei Control-Plane-Nodes in drei Rechenzentren, jeder mit einer etcd-Instanz.
Und wann ist ein Managed-Kubernetes vom Cloud-Anbieter besser als der eigene Betrieb? Ehrliche Antwort: immer dann, wenn niemand im Team die Control Plane betreiben will. Der Anbieter betreibt dann API-Server, etcd und Backups, und der cloud-controller-manager bindet Load Balancer und Volumes an. Ich baue meine Cluster selbst, weil meine Kunden Datenhoheit und Kontrolle über etcd-Backups brauchen; das ist eine bewusste Entscheidung mit Betriebsaufwand. Die Abwägung im Detail steht in Kubernetes on-premise oder Cloud?.
Häufige Fragen
Master Node oder Control Plane: was ist der Unterschied?
Kein technischer. Master war die alte Bezeichnung für die Nodes mit den Steuerkomponenten; die Kubernetes-Dokumentation spricht seit einigen Jahren von Control Plane, und das Label auf den Nodes heißt entsprechend node-role.kubernetes.io/control-plane. In älteren Anleitungen, Büchern und Tools begegnet dir Master noch, gemeint ist dasselbe.
Wie viele Control-Plane-Nodes brauche ich?
Für Entwicklung einen, für Produktion drei. Die ungerade Zahl kommt vom Quorum in etcd. Fünf sind nur bei sehr großen Clustern oder besonderen Wartungsanforderungen sinnvoll.
Was passiert, wenn etcd ausfällt?
Deine Anwendungen laufen zunächst weiter, weil kubelet und Container-Runtime sie halten. Aber der Cluster ist eingefroren: Kein Deployment, kein Scaling, keine Neuplanung bei Node-Ausfall, und kubectl liefert Fehler. Verlierst du die etcd-Daten ganz, brauchst du den Snapshot. Ohne Snapshot musst du alle Manifeste neu einspielen, und Objekte, die nur im Cluster entstanden sind, etwa Zertifikate oder Secrets, sind weg.
Laufen auf der Control Plane auch normale Anwendungen?
Standardmäßig nicht. Control-Plane-Nodes tragen einen Taint, den normale Pods nicht tolerieren, damit die Steuerkomponenten immer genug Ressourcen haben. Du kannst den Taint entfernen, was bei kleinen Single-Node-Clustern und Minikube üblich ist. In Produktion lässt du ihn dran.
Was ist der Unterschied zwischen kubelet, etcd und Scheduler?
Die drei sitzen an ganz verschiedenen Stellen. etcd speichert den gewünschten Zustand auf der Control Plane. Der Scheduler entscheidet auf der Control Plane, auf welchem Node ein neuer Pod landet. Das kubelet setzt auf jedem einzelnen Node um, was für diesen Node beschlossen wurde, und meldet zurück. Merkhilfe: etcd weiß, der Scheduler plant, das kubelet macht.
Wie du weitermachst
Mit dieser Landkarte kannst du jeden Fehler im Cluster einer Komponente zuordnen: Pending zum Scheduler, NotReady zum Node-Controller und kubelet, unerreichbare Services zu kube-proxy und Endpoints, ein hängendes kubectl zum API-Server oder etcd. Der nächste Schritt ist, das selbst zu sehen: Setz dir einen lokalen Cluster auf, führ kubectl get pods -n kube-system aus und lies die Events eines Pods mit kubectl describe. Danach lohnt der Blick auf das kleinste Objekt, das durch diesen ganzen Ablauf läuft, den Pod.
Ausführlich mit allen Beispielen steht das in Kapitel 2 meines Buchs „Kubernetes: Das Praxisbuch für Entwickler und DevOps-Teams" (Rheinwerk).