☸ Kubernetes im Heimlabor

Kurs › Folge 12

Services: wie Pods sich finden

Pod-IPs ändern sich ständig. Der Service ist die stabile Adresse davor — und kein einziges Programm.

Das Video zu dieser Folge ist noch nicht veröffentlicht. Die Notizen stehen trotzdem schon hier.

Was du danach kannst

  • ClusterIP, NodePort und Headless unterscheiden
  • einen Service ueber Labels an Pods binden und die Endpoints lesen
  • den Weg einer Anfrage vom DNS-Namen bis zum Pod nachvollziehen
  • erklaeren, warum ein Service kein laufendes Programm ist

Das Problem

Starte einen Pod, notiere seine IP, lösche ihn, lass ihn neu anlegen — die IP ist eine andere. Bei jedem Update, jedem Neustart, jedem Knotenausfall. Auf eine Pod-IP kann sich niemand verlassen.

Der Service ist die Antwort: eine Adresse, die bleibt.

Ein Service in einem Befehl

kubectl expose deployment web --port=80

Was dabei entsteht:

  • Kubernetes nimmt die Labels der Pods (app=web) als Selektor des Service
  • Der Service bekommt eine IP aus dem Service-Bereich (10.96.0.0/12) — die ClusterIP
  • --port=80 ist der Port des Service; der targetPort am Pod wird stillschweigend genauso gesetzt

Vorderseite und Rückseite

kubectl get svc,endpoints
NAME          TYPE        CLUSTER-IP     PORT(S)
service/web   ClusterIP   10.96.134.22   80/TCP

NAME            ENDPOINTS
endpoints/web   10.244.1.5:80,10.244.1.6:80,10.244.1.7:80

Der Service ist die stabile Vorderseite. Die Endpoints sind die aktuelle Rückseite — die Liste der Pods, die gerade bedient werden.

Diese Liste pflegt ein Controller selbstständig. Er beobachtet, welche Pods zum Selektor passen und bereit sind. Genau hier hängt die Readiness-Probe aus Folge 18: Ein Pod, der nicht Ready meldet, verschwindet aus dieser Liste und bekommt keinen Verkehr mehr. Das ist der gesamte Mechanismus hinter „Update ohne Ausfall".

In neueren Versionen sind EndpointSlices das eigentliche Objekt (kubectl get endpointslices). Die alte Endpoints-Ansicht gibt es aus Gewohnheit weiterhin.

Ein Service ist kein Programm

Das ist der Punkt, an dem die meisten ein falsches Bild im Kopf haben. Es gibt keinen Service-Container, keinen Proxy-Prozess, kein Programm, das lauscht.

Die ClusterIP existiert auf keiner Netzwerkkarte. Sie ist ein Ziel für Umschreibungsregeln, die auf jedem Knoten hinterlegt sind — klassisch von kube-proxy als iptables-Regeln, bei Cilium als eBPF-Programme im Kernel.

Deshalb funktioniert ein Service auch auf einem Knoten, auf dem gar kein passender Pod läuft: Die Regel ist überall da.

DNS im Cluster

Jeder Pod bekommt eine /etc/resolv.conf in dieser Art:

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local

Deshalb genügt der kurze Name. web wird über die Suchliste zu web.default.svc.cluster.local — dem vollständigen Namen des Service:

<dienst>.<namensraum>.svc.cluster.local

Aus einem anderen Namensraum heraus brauchst du entsprechend web.default oder den vollen Namen.

Der Weg einer Anfrage

kubectl run test --rm -it --image=busybox -- wget -qO- web
  1. wget fragt DNS nach web → CoreDNS antwortet mit der ClusterIP
  2. wget verbindet sich zu 10.96.134.22:80
  3. Auf dem Knoten greift die Regel und schreibt das Ziel um auf eine Pod-IP aus der Endpoints-Liste
  4. Das Paket läuft über das Pod-Netz zu diesem Pod — womöglich auf einem anderen Rechner
  5. nginx antwortet

Die Verteilung geschieht pro Verbindung, nicht pro Anfrage. Wer im Browser zehnmal neu lädt, landet nicht zwingend zehnmal woanders.

Der Wegwerf-Pod

Das Muster --rm -it wirst du hundertmal brauchen:

Teil Bedeutung
--image=busybox winziges Image mit sh und wget
-it interaktiv, mit Terminal verbunden
--rm Pod löschen, sobald der Befehl endet
-- alles danach ist der Befehl im Container

Die drei Arten

Typ Erreichbar Wofür
ClusterIP nur im Cluster der Normalfall
NodePort fester Port auf jedem Knoten (30000–32767) Labor, einfache Zugänge
Headless (clusterIP: None) keine IP, DNS liefert die Pod-IPs direkt Datenbanken, StatefulSets

LoadBalancer fehlt hier bewusst — der braucht jemanden, der externe Adressen vergibt. Im Heimlabor macht das MetalLB, und das ist Folge 14.

Zum Nachlesen

Eine geordnete Übersicht über die offizielle Dokumentation steht unter Nachlesen.

Befehle aus dieser Folge

  • kubectl expose deployment web --port=80
  • kubectl get svc,endpoints
  • kubectl get endpointslices
  • kubectl run test --rm -it --image=busybox -- wget -qO- web
  • kubectl run dns --rm -it --image=busybox -- cat /etc/resolv.conf
  • kubectl logs -l app=web --prefix -f --tail=1

Begriffe

Service
eine Regel, keine Anwendung — die ClusterIP existiert auf keiner Netzwerkkarte
ClusterIP
nur im Cluster erreichbar, der Normalfall
NodePort
ein fester Port auf jedem Knoten, gut fuers Labor
Endpoints
die Liste der Pods, die ein Service gerade bedient — nur bereite Pods stehen darin
CoreDNS
der Namensdienst im Cluster; <dienst>.<namensraum>.svc.cluster.local
kube-proxy / eBPF
setzt die Service-Regeln auf jedem Knoten um

Übung — mach es selbst

Mach die Verteilung sichtbar.

  1. Deployment web mit 3 Repliken, Service davor.
  2. Fenster 1: kubectl logs -l app=web --prefix -f --tail=1
  3. Fenster 2: fuenfmal kubectl run test --rm -it --image=busybox -- wget -qO- web
  4. An den Praefixen ablesen, welcher Pod welche Anfrage bekommen hat.

Dann der eigentliche Test:

  1. kubectl get endpoints web — drei Adressen.
  2. Loesche einen Pod und ruf den Befehl sofort wieder auf. Wie viele Adressen stehen drin, waehrend der Ersatz startet?
  3. Und wie lautet die ClusterIP dabei? (Sie aendert sich nicht — das ist der ganze Sinn der Sache.)

Zusatz: Aendere den Service auf type: NodePort und ruf ihn von deinem Laptop aus auf: curl http://192.168.178.75:<nodeport>. Warum antwortet er auch auf der IP des Knotens, auf dem gar kein Pod laeuft?