☸️ Kubernetes Lektion 11/20 ~8 Min. Einsteiger

Services und DNS

Cluster-interne Kommunikation und Service Discovery.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du erklären, warum Pods eine stabile Adresse brauchen und wie Services dieses Problem lösen. Du kennst die Service-Typen ClusterIP, NodePort und LoadBalancer mit ihren Einsatzfällen, verstehst die Rolle von EndpointSlices und kube-proxy und weißt, wie CoreDNS aus einem Service-Namen eine IP macht. Damit kannst du die Interview-Klassiker „Was passiert, wenn ein Pod einen Service aufruft?” sicher beantworten.

Das Problem: Pods sind Eintagsfliegen

Pods bekommen bei jedem Start eine neue IP aus dem Pod-CIDR. Ein Deployment ersetzt Pods laufend — bei jedem Rollout, bei jedem Node-Ausfall, bei jedem Scale-Event. Wenn dein Frontend die IP eines Backend-Pods fest verdrahtet, ist die Verbindung nach dem nächsten Rollout tot. Du bräuchtest einen Mechanismus, der laufend die aktuellen Pod-IPs einsammelt und Anfragen darauf verteilt.

Genau das ist ein Service: eine stabile virtuelle IP (und ein DNS-Name), hinter der eine sich ständig ändernde Menge von Pods steht. Die Zuordnung läuft über einen Label-Selector — der Service spec.selector: app: backend umfasst alle Pods mit diesem Label. Welche Pods das gerade sind, pflegt Kubernetes automatisch in EndpointSlices: Der EndpointSlice-Controller beobachtet Pods, die zum Selector passen und ready sind, und schreibt deren IPs in diese Objekte. Fällt ein Pod aus der Readiness-Probe, fliegt er aus den Endpoints — und bekommt keinen Traffic mehr. Das ist der Mechanismus, der Rolling Updates ohne Verbindungsabbrüche möglich macht.

Wichtig fürs Verständnis: Die ClusterIP existiert auf keinem Interface. Kein Container lauscht darauf. Sie ist eine reine Verabredung, die erst durch kube-proxy auf jedem Node real wird.

Die drei Service-Typen und wann welcher passt

ClusterIP ist der Default: eine clusterinterne virtuelle IP, von außen nicht erreichbar. Das ist der richtige Typ für die gesamte interne Kommunikation — Frontend zu Backend, Backend zu Datenbank. Ein Spezialfall ist der Headless Service (clusterIP: None): Hier gibt es keine virtuelle IP, DNS liefert stattdessen direkt die Pod-IPs. StatefulSets nutzen das, damit Clients gezielt einzelne Instanzen ansprechen können (etwa postgres-0).

NodePort öffnet auf jedem Node denselben Port aus dem Bereich 30000–32767 und leitet ihn an den Service weiter. Damit ist die Anwendung von außen erreichbar — über jede Node-IP. In der Praxis nutzt du NodePort selten direkt: Die Ports sind unhandlich, es gibt kein TLS, kein Routing, und Clients müssten Node-IPs kennen. NodePort ist vor allem der Unterbau für den nächsten Typ.

LoadBalancer baut auf NodePort auf und bittet zusätzlich die Cloud-Umgebung um einen externen Load Balancer (AWS ELB, GCP Load Balancer, Azure LB). Der Cloud-Controller-Manager provisioniert ihn und trägt die externe IP in den Service-Status ein. Der Haken: Pro Service ein eigener Load Balancer — das wird bei vielen Services teuer. Deshalb der übliche Schnitt: ein LoadBalancer-Service für den Ingress-Controller, dahinter HTTP-Routing für alle Anwendungen (Lektion 12). On-Premises gibt es keinen Cloud-Controller, der die externe IP liefert — dort bleibt ein LoadBalancer-Service <pending>, bis du etwas wie MetalLB installierst.

Wie der Traffic wirklich fließt: kube-proxy

Die Frage „Wer macht eigentlich das Load Balancing?” trennt im Interview Auswendiglerner von Verstehern. Die Antwort: kube-proxy, ein DaemonSet auf jedem Node — aber nicht als Proxy im Datenpfad, sondern als Regel-Schreiber.

Im verbreiteten iptables-Modus übersetzt kube-proxy jeden Service in NAT-Regeln: „Pakete an die ClusterIP 10.96.x.x:80 → schreibe die Ziel-IP auf eine der Endpoint-IPs um, zufällig gewählt.” Das Load Balancing passiert also im Kernel des Nodes, auf dem der Client-Pod läuft, pro Verbindung. Konsequenzen, die du kennen solltest: Die Auswahl ist zufällig (nicht round-robin), sie gilt pro TCP-Verbindung (langlebige Verbindungen kleben an einem Pod), und bei tausenden Services werden die linearen iptables-Ketten messbar langsam. Der IPVS-Modus löst das Skalierungsproblem mit einer Hash-basierten Implementierung; CNIs wie Cilium ersetzen kube-proxy gleich ganz durch eBPF.

Merksatz für das Interview: Ein Service ist kein Prozess, sondern verteilter Zustand — API-Objekt plus EndpointSlices plus NAT-Regeln auf jedem Node.

CoreDNS: vom Namen zur ClusterIP

Niemand will ClusterIPs auswendig kennen. CoreDNS läuft als Deployment im Cluster und beantwortet DNS-Anfragen nach dem Schema <service>.<namespace>.svc.cluster.local. Der kubelet trägt dafür in jedem Pod eine passende /etc/resolv.conf ein: Als Nameserver steht dort die ClusterIP des kube-dns-Service, dazu Search-Domains wie <eigener-namespace>.svc.cluster.local.

Diese Search-Domains erklären die Kurzformen: Ein Pod im Namespace shop erreicht den Service backend im selben Namespace einfach als backend. Für einen Service in einem anderen Namespace brauchst du mindestens backend.payments. Der voll qualifizierte Name funktioniert immer. Das ist eine beliebte Fangfrage: „Warum erreicht Pod A den Service, Pod B aber nicht — mit demselben Hostnamen?” Antwort: unterschiedliche Namespaces, unterschiedliche Search-Domains.

Praxis

Lab mit kind oder minikube — Service anlegen, Endpoints beobachten, DNS testen:

# Deployment mit 3 Replicas und Service davor
kubectl create deployment web --image=nginx --replicas=3
kubectl expose deployment web --port=80 --target-port=80

# ClusterIP und Endpoints anschauen
kubectl get svc web
kubectl get endpointslices -l kubernetes.io/service-name=web

# DNS und Erreichbarkeit aus einem Test-Pod prüfen
kubectl run client --rm -it --image=busybox:1.36 --restart=Never -- sh -c \
  "nslookup web && wget -qO- http://web | head -n 4"

# Readiness-Effekt: ein Pod fällt aus den Endpoints
kubectl scale deployment web --replicas=2
kubectl get endpointslices -l kubernetes.io/service-name=web

Lösche danach testweise das Label eines Pods (kubectl label pod <name> app-) und beobachte, wie er sofort aus dem EndpointSlice verschwindet, obwohl er weiterläuft. Damit hast du den Selector-Mechanismus einmal live gesehen — genau das Szenario hinter vielen „Service antwortet nicht”-Tickets.

Typische Stolperfallen

Selector trifft nichts: Tippfehler im Label oder Selector → der Service existiert, aber die EndpointSlices sind leer, jede Anfrage schlägt fehl. Erster Check immer: kubectl get endpointslices.

port vs. targetPort verwechselt: port ist der Port des Service, targetPort der Container-Port. Lauscht der Container auf 8080, der Service zeigt aber auf targetPort: 80, kommt „connection refused” — obwohl Selector und DNS stimmen.

Readiness-Probe fehlt oder ist falsch: Ohne Readiness-Probe gilt ein Pod ab Container-Start als ready und bekommt Traffic, bevor die Anwendung hochgefahren ist. Mit zu strenger Probe fliegen unter Last alle Pods gleichzeitig aus den Endpoints — Totalausfall durch „Schutzmechanismus”.

Langlebige Verbindungen kleben: gRPC- oder Datenbank-Verbindungen werden einmal per NAT auf einen Pod gemappt und bleiben dort. Neue Pods nach einem Scale-up bekommen kaum Traffic. Lösung: clientseitiges Load Balancing oder ein L7-Proxy.

Interview-Vorbereitung

Die Standardfrage „Was ist ein Service in Kubernetes?” beantwortest du problem-first: Pods sind kurzlebig und wechseln IPs → ein Service gibt einer Pod-Gruppe (per Label-Selector) eine stabile virtuelle IP und einen DNS-Namen → die aktuelle Pod-Liste pflegen EndpointSlices, die Umsetzung in NAT-Regeln macht kube-proxy auf jedem Node → drei Typen für drei Reichweiten: ClusterIP intern, NodePort über Node-Ports, LoadBalancer mit Cloud-Integration.

Typische Follow-ups:

  • „Wo findet das Load Balancing statt?” — Im Kernel des Client-Nodes über die NAT-Regeln von kube-proxy, pro Verbindung; kein zentraler Proxy.
  • „Was ist ein Headless Service und wofür?” — clusterIP: None, DNS liefert die Pod-IPs direkt; nötig, wenn Clients einzelne Instanzen adressieren müssen, etwa bei StatefulSets.
  • „Service hat eine IP, aber keine Antwort — wie debuggst du?” — EndpointSlices prüfen (Selector? Readiness?), dann targetPort gegen den Container-Port, dann DNS aus einem Test-Pod.
  • „Warum nicht für jede App einen LoadBalancer-Service?” — Kosten und Verwaltungsaufwand pro Cloud-LB; stattdessen ein LB vor einem Ingress-Controller.

Zusammenfassung

Services entkoppeln Aufrufer von der Vergänglichkeit der Pods: Label-Selector wählt die Pods, EndpointSlices halten die aktuelle IP-Liste, kube-proxy macht daraus NAT-Regeln auf jedem Node, CoreDNS liefert den Namen dazu. ClusterIP für intern, NodePort als roher Außenzugang, LoadBalancer für die Cloud-Integration — und Headless, wenn Clients einzelne Pods sehen müssen.

Was Services nicht können: HTTP verstehen. Routing nach Hostnamen und Pfaden, TLS-Terminierung und virtuelles Hosting — dafür brauchst du Ingress und die Gateway API, das Thema der nächsten Lektion.