Alle Rezepte
Kubernetes-Grundlagen · Abschnitt 2.3.3 · Architektur

Von kubectl zum Container:
der API Call Flow

Acht Abschnitte über den Weg, den ein Manifest durch den Cluster nimmt: zehn Schritte zwischen Request und laufendem Container, die Speichenarchitektur um den kube-apiserver und was daraus für die Fehlersuche folgt. Kernaussage: Jede Nachricht läuft über den apiserver – und genau deshalb weiß man, wo man nachschaut.

0ÜberblickKomponenten, Rollen 1Der FlowZehn Schritte, Reihenfolge 2SpeichenarchitekturHub, Spokes, Vermittlung 3Direkte KommunikationMesh, Komplexität, Grenzen 4Separation of ConcernsZuständigkeiten, Logs 5etcdKonsistenz, Schreibrecht 6Flow beobachtenEvents, Verbose, Endpoints 7DurchlaufVier Phasen, chronologisch
0

Überblick: wer am Deployment beteiligt ist

Kubernetes besteht aus vielen Komponenten, die miteinander reden müssen, damit am Ende ein Container läuft. Wer diesen Weg kennt, sucht Fehler nicht im falschen Log.

Die Beteiligten und ihre eine Aufgabe

KomponenteLäuft aufAufgabe im Flow
kube-apiserverControl Planeeinzige Tür zum Cluster, vermittelt jede Nachricht
etcdControl Planespeichert Soll- und Ist-Zustand
kube-controller-managerControl Planevergleicht Soll und Ist, fordert Änderungen an
kube-schedulerControl Planeentscheidet, auf welchem Worker ein Pod landet
kubeletjeder Workerbaut den Container und besorgt Secrets und Volumes
kube-proxyjeder Workermacht den Container über das Netz erreichbar
Container Enginejeder Workererzeugt den Container tatsächlich

Der Einstieg ist immer derselbe: Über kubectl geht ein Request mit einem YAML-Manifest an den kube-apiserver. Das Manifest enthält alles, was der Cluster wissen muss, um den Container aufzubauen.

Der apiserver ist der Empfang im Bürogebäude – jeder meldet sich dort an, niemand läuft allein in den Aktenkeller.
Hinweis: Kubernetes-Objekte wie Deployment, ReplicaSet oder Service bleiben hier bewusst ausgeklammert. Sie machen den Flow schwerer verständlich, ohne ihn zu ändern – für jedes Objekt läuft dieselbe Abfolge.
1

Der API Call Flow in zehn Schritten

Ein einzelner Container soll deployt werden. Vom abgeschickten Request bis zur Bestätigung in etcd sind zehn Nachrichten unterwegs – die meisten davon Hin- und Rückfragen über den apiserver.

Der Flow als Kreis

In der Mitte der apiserver, ringsum die übrigen Komponenten – angeordnet in der Reihenfolge, in der sie im Ablauf zum ersten Mal drankommen. Die Zeit läuft im Uhrzeigersinn: von kubectl oben über etcd, controller-manager und scheduler bis zu kubelet und kube-proxy, und am Ende zurück zu etcd. Jeder Pfeil ist eine Nachricht, und jede Nachricht beginnt oder endet im Zentrum. Durchgezogen heißt zum apiserver, gestrichelt vom apiserver weg; die Nummern entsprechen den zehn Schritten in der Liste darunter.

Control Plane Worker Zeit läuft im Uhrzeigersinn kube-apiserver Hub – jede Nachricht läuft hier durch kubectl Client etcd Speicher kube-controller-manager Soll-Ist-Abgleich kube-scheduler Platzierung kubelet Worker kube-proxy Worker Request 1 created 2 3 4 5 6 6 7 8 10 10 9 lokal, ohne apiserver an den apiserver vom apiserver Nummer = Schritt aus der Liste unten

Zwei Dinge werden dadurch sichtbar, die eine Aufzählung verschluckt: Keine einzige Linie verbindet zwei Komponenten direkt – wer mit wem redet, ist immer der apiserver. Und die Rückfragen haben ein Muster: Der controller-manager fragt (3) und bekommt Antwort (4), der scheduler wird gefragt (6) und antwortet (6). Schritt 9 ist der einzige Pfeil, der das Zentrum nicht berührt: Dort arbeitet das kubelet lokal mit der Container Engine. Und der Kreis schließt sich sichtbar – der letzte Pfeil landet wieder bei etcd, wo Schritt 1 begonnen hat.

Die Bestätigung an den Client geht schon nach Schritt 1 zurück, während im restlichen Kreis noch neun Nachrichten folgen. Genau das ist der Grund, warum ein grünes created nichts über den laufenden Container aussagt.

Ablauf nach dem Absenden des Manifests

  1. Request annehmenDer apiserver nimmt den Request entgegen und legt das Manifest in etcd ab.
  2. Controller informierenDer controller-manager bekommt vom apiserver die Information, dass ein neues Manifest existiert.
  3. Ist-Zustand erfragenDer controller-manager fragt zurück, ob der Container schon deployt ist und ob der Status dem Wunsch entspricht.
  4. Antwort: existiert nichtDer apiserver meldet, dass es den Container noch nicht gibt.
  5. Erstellung beauftragenDer controller-manager gibt dem apiserver den Befehl, den Container zu erstellen.
  6. Worker auswählenDer apiserver fragt den scheduler, auf welchem Worker der Container laufen kann; der scheduler antwortet mit dem Ziel.
  7. Kubelet beauftragenDer apiserver schickt die nötigen Teile des Manifests an das kubelet des ausgewählten Workers.
  8. Netz vorbereitenZusätzlich informiert der apiserver jeden kube-proxy, dass der Container auf diesem Worker verfügbar gemacht wird.
  9. Container bauenDas kubelet lässt die Container Engine den Container erzeugen und besorgt alle angeforderten Ressourcen wie Secrets oder Volumes.
  10. Erfolg zurückmeldenDas kubelet meldet das erfolgreiche Deployment an den apiserver, der die Information in etcd schreibt.
Hinweis: Diese Abfolge wird immer wieder durchlaufen, selbst bei einer Kleinigkeit wie einer erhöhten Memory-Angabe. Es gibt keinen kurzen Dienstweg für kleine Änderungen.
Achtung: kubectl apply quittiert mit created, sobald Schritt 1 durch ist – das Manifest liegt in etcd, mehr nicht. Die Schritte 2 bis 10 laufen asynchron und ohne Rückkanal zur Shell. Ein Pod kann stundenlang Pending bleiben, ohne dass der Befehl je einen Fehler ausgegeben hätte. Grüne Ausgabe heißt nur „angenommen", nie „läuft".
Der Flow ist ein Bauantrag, kein Handschlag – die Eingangsbestätigung vom Amt ist nicht das fertige Haus.
2

Speichenarchitektur: alles über den Hub

In jedem der zehn Schritte taucht der apiserver auf. Das ist kein Zufall, sondern das Bauprinzip: Kubernetes nutzt eine Speichenarchitektur, das Hub-and-Spoke-Pattern.

Hub

Ein zentraler Punkt, durch den alle Anfragen und Nachrichten fließen. Er vermittelt und steuert den Datenverkehr zwischen den Endpunkten. In Kubernetes ist das der kube-apiserver.

Spokes

Die Speichen sind die Endpunkte am Hub. Jede ist für eine bestimmte Funktion zuständig und interagiert mit den anderen ausschließlich über den Hub – scheduler, controller-manager, kubelet, kube-proxy.

Was das Modell bringt

  • Ein Kontakt pro Komponente – ein neuer Spoke muss nur den Hub kennen, nicht alle anderen Dienste
  • Verteilen ist Hub-Sache – kommt ein Dienst hinzu, nimmt der Hub dessen Nachrichten entgegen und bringt sie zu den anderen
  • Überwachung an einer Stelle – alle Transaktionen laufen über den Hub, das erleichtert die Fehleranalyse
  • Zugriffskontrolle an einer Stelle – Authentifizierung, Autorisierung und Auditing sitzen dort, wo ohnehin alles vorbeikommt
Der Hub ist die Telefonzentrale – neue Mitarbeiter melden sich einmal bei der Zentrale an, statt sich vierzig Kollegen einzeln vorzustellen.
Achtung: Fällt der apiserver aus, laufen bestehende Container einfach weiter – das kubelet arbeitet mit dem, was es lokal kennt. Der Cluster wirkt gesund, weil die Anwendungen antworten, aber keine einzige Änderung kommt mehr durch: keine Rollouts, keine Skalierung, kein Neuplatzieren nach Node-Ausfall. Monitoring, das nur auf Anwendungs-Endpunkte schaut, sieht davon nichts.
3

Direkte Kommunikation und ihre Grenze

Direkt von Dienst zu Dienst wäre erst einmal schneller – ein Vermittler weniger. Bei vielen kleinen Diensten kippt der Vorteil trotzdem, und zwar rein rechnerisch.

Verbindungswege bei direkter Kommunikation

Jeder Dienst muss jedem vorhandenen Dienst bekannt gemacht werden. Die Zahl der Wege wächst quadratisch, während sie im Hub-Modell mit jedem Dienst nur um eine Anbindung steigt.

DiensteWege direktWege über den Hub
333
5105
104510
2019020

Schon bei fünf Diensten sind es zehn Verbindungswege – jeder zusätzliche Dienst erhöht die Komplexität weiter. In der Speichenarchitektur spricht jeder Spoke nur mit dem Hub, und der kümmert sich um das Verteilen.

Direkte Kommunikation ist die Telefonliste im Verein – bei fünf Mitgliedern noch machbar, bei zwanzig ruft jede Terminänderung den halben Abend zusammen.
Hinweis: Die Rechnung gilt für die Steuerebene, nicht für den Datenverkehr der Anwendungen. Pod-zu-Pod-Traffic läuft direkt über das Cluster-Netz und nicht durch den apiserver.
4

Separation of Concerns: eine Aufgabe pro Komponente

Die Menge an Nachrichten wirkt zunächst erschreckend kompliziert. Sie ist die direkte Folge davon, dass jede Komponente nur eine Sache tut – und genau das macht die Fehlersuche berechenbar.

Symptom, Zuständigkeit, Fundort

FrageZuständigWo nachsehen
Ist mein Wunsch überhaupt angekommen?apiserver, etcdObjekt zurücklesen
Warum passiert nichts weiter?controller-managerEvents, Controller-Logs
Warum bleibt der Pod Pending?schedulerEvent FailedScheduling
Warum startet der Container nicht?kubeletdescribe pod, Kubelet-Log am Node
Warum ist nichts erreichbar?kube-proxyEndpointSlices des Service
Scheduler, Kubelet, Kube-Proxy sind Platzanweiser, Bauleiter und Wegweiser – der Platzanweiser sucht den Sitzplatz, der Bauleiter stellt auf, der Wegweiser sorgt dafür, dass Besucher ankommen. Wer sich über den Weg beschwert, redet nicht mit dem Platzanweiser.
Zuständigkeit prüfen statt raten

Der schnellste Test, ob Schritt 6 schon durch ist: Ein Pod ohne Node-Zuweisung hat den scheduler noch nicht passiert – dann ist jede Suche im Kubelet-Log verschwendete Zeit.

# leer = noch keine Scheduling-Entscheidung getroffen
kubectl get pod <pod> -n <namespace> -o jsonpath='{.spec.nodeName}'

Steht dort ein Node, liegt die Zuständigkeit ab hier beim kubelet dieses Workers – und der Rest der Analyse findet dort statt.

kubectl describe pod <pod> -n <namespace>
5

etcd: nur einer darf schreiben

Die zentrale Kommunikation stellt sicher, dass nicht jeder Dienst selbst in etcd schreibt. Das trägt zur Konsistenz bei und reduziert Fehler – gerade weil etcd die kritischste Komponente im Cluster ist.

Was das Schreibmonopol bewirkt

  • Konsistenz – ein Schreiber, eine Reihenfolge, keine widersprüchlichen Zustände
  • Validierung an einer Stelle – was der apiserver ablehnt, landet nie im Speicher
  • Nachvollziehbarkeit – jede Zustandsänderung hat denselben Weg und dieselbe Protokollstelle
  • Kleinere Angriffsfläche – Komponenten brauchen kein etcd-Zugriffsrecht, nur ein apiserver-Recht
etcd ist das Grundbuch, der apiserver der Notar – Eigentum ändert sich nicht dadurch, dass zwei Leute etwas vereinbaren, sondern erst durch die Eintragung.
Achtung: etcd fällt selten hart aus, es wird langsam. Steigt die Schreiblatenz der Datenträger, quittiert der apiserver Requests weiterhin, nur eben zäh – Symptom ist ein „kubectl hängt manchmal", während alle Pods Running melden und keine Komponente rot wird. Wer nur auf Ready-Status schaut, sieht die Ursache nie.
Gesundheit der Control Plane abfragen

Vor der Suche in einzelnen Logs lohnt der Blick auf die Health-Endpunkte des apiservers: Sie listen die Teilprüfungen einzeln auf, inklusive etcd.

kubectl get --raw '/livez?verbose'
kubectl get --raw '/readyz?verbose'

Auf OpenShift laufen die etcd-Instanzen als statische Pods im eigenen Namespace – dort stehen auch die Logs mit den Latenzwarnungen.

oc get pods -n openshift-etcd -o wide
6

Den Flow selbst mitlesen

Der Ablauf ist nicht nur Theorie: Fast jeder Schritt hinterlässt eine Spur, die sich mit Bordmitteln sichtbar machen lässt.

Spur je Schritt

SchrittSichtbar als
1 · in etcd abgelegtObjekt lässt sich zurücklesen, resourceVersion gesetzt
2–5 · Controller aktivEvents wie SuccessfulCreate
6 · Worker gewähltEvent Scheduled, .spec.nodeName gefüllt
7–9 · Kubelet bautEvents Pulling, Created, Started
8 · Netz bereitEndpointSlice enthält die Pod-IP
10 · Rückmeldung.status.phase wechselt auf Running

Der Ablauf als Zeitleiste

Events sind die kompakteste Sicht auf die Schritte 2 bis 10, weil sie mit Zeitstempel zeigen, welche Komponente wann etwas gemeldet hat.

kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp

Wer sehen will, dass wirklich jeder Aufruf beim apiserver landet, lässt sich die HTTP-Requests von kubectl ausgeben – Schritt 1 des Flows, live.

kubectl get pods -n <namespace> -v=6

Für Schritt 8 zählt nicht der Service, sondern ob die Pod-IP tatsächlich in der Endpunktliste steht. Ein Service ohne passende EndpointSlice ist ein Wegweiser ins Leere.

kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service>
Achtung: Events sind kein Log. Sie verfallen nach etwa einer Stunde, und beim Neuanlegen eines Pods beginnt die Zeitleiste von vorn. Wer am nächsten Morgen nachschaut, findet ein leeres Event-Feld – und hält einen Pod für unauffällig, der nachts zehnmal neu gestartet ist. Für die Nachbetrachtung zählen .status.containerStatuses[].restartCount und ein zentrales Log-Backend, nicht die Events.
7

Kompletter Durchlauf in vier Phasen

Dieselben zehn Schritte, gruppiert nach Zuständigkeit – so lässt sich im Fehlerfall die Phase eingrenzen, bevor man Logs öffnet.

Die vier Phasen auf einen Blick

  1. Phase A · AnnehmenSchritte 1–2: apiserver validiert, schreibt nach etcd, informiert den controller-manager.
  2. Phase B · AbgleichenSchritte 3–5: controller-manager vergleicht Soll und Ist und beauftragt die Erstellung.
  3. Phase C · PlatzierenSchritt 6: scheduler wählt den Worker aus.
  4. Phase D · Bauen und meldenSchritte 7–10: kubelet baut, kube-proxy bereitet das Netz vor, der Erfolg geht zurück in etcd.

Phase A · Annehmen

Schritt 1 · Manifest kommt an und wird gespeichert

Der apiserver nimmt den Request an, prüft Authentifizierung, Autorisierung und Schema und legt das Manifest in etcd ab. Ohne diesen Schritt existiert der Wunsch nirgends – wer ihn überspringt gedanklich, sucht später Container, die nie beauftragt wurden.

# zurücklesen: was tatsächlich gespeichert wurde, nicht was gemeint war
kubectl get pod <pod> -n <namespace> -o yaml
Schritt 2 · Controller erfährt vom neuen Manifest

Der controller-manager wird nicht selbst aktiv, weil er etcd liest – er bekommt die Information vom apiserver. Das ist der Grund, warum ein überlasteter apiserver den gesamten Cluster träge macht, obwohl die Daten längst gespeichert sind.

Phase B · Abgleichen

Schritte 3 bis 5 · Soll gegen Ist

Der controller-manager fragt beim apiserver nach, ob der Container schon existiert und ob der aktuelle Status dem gewünschten entspricht. Die Antwort lautet hier: gibt es noch nicht. Daraufhin erteilt er den Auftrag zur Erstellung. Dieser Abgleich ist kein einmaliger Vorgang, sondern das Grundmuster: Kubernetes fragt permanent „Soll gleich Ist?" und handelt nur bei Abweichung.

# Spuren der Controller-Arbeit
kubectl get events -n <namespace> --field-selector reason=SuccessfulCreate

Phase C · Platzieren

Schritt 6 · Scheduler wählt den Worker

Der apiserver fragt den scheduler, auf welchem Worker der Container laufen kann, und bekommt eine Antwort. Findet der scheduler keinen passenden Node, bleibt der Pod ohne Zuweisung stehen – der Zustand ist nicht fehlerhaft, sondern unentschieden.

# Grund der Nichtplatzierung, z. B. Ressourcen oder Taints
kubectl get events -n <namespace> --field-selector reason=FailedScheduling

Phase D · Bauen und melden

Schritte 7 und 8 · Kubelet und kube-proxy bekommen ihren Teil

Der apiserver schickt die notwendigen Informationen des Manifests an das kubelet des ausgewählten Workers. Zusätzlich geht eine Netzwerkinformation an jeden kube-proxy, damit der Container auf diesem Worker verfügbar gemacht wird. Beide Wege sind unabhängig: Der Container kann laufen, während die Erreichbarkeit noch fehlt.

Schritt 9 · Container wird erzeugt

Das kubelet sorgt dafür, dass der Container in der Container Engine entsteht und alle im Manifest angeforderten Ressourcen wie Secrets oder Volumes bereitstehen. Fehlt ein Secret, scheitert nicht das Manifest, sondern der Start – sichtbar erst hier, nicht beim Anlegen.

kubectl describe pod <pod> -n <namespace>
Schritt 10 · Rückmeldung und Abschluss

Das kubelet meldet das erfolgreiche Deployment an den apiserver, der die Information in etcd speichert. Erst damit ist Ist gleich Soll – und der Kreis schließt sich genau dort, wo er begonnen hat.

kubectl get pod <pod> -n <namespace> -o jsonpath='{.status.phase}'
Der Flow ist eine Ringleitung, keine Einbahnstraße – jede Meldung endet wieder im Grundbuch, sonst hätte niemand einen verlässlichen Ist-Zustand.