Man hat eine Anwendung deployed. Einen Service erstellt. Im Cluster funktioniert curl http://my-app. Dann kommt die Frage: „Wie erreiche ich das im Browser?“
An dieser Stelle treffen viele Anfänger auf Ingress. Tutorials zeigen oft direkt nach Deployment und Service ein Ingress-YAML — das lässt Ingress wie den natürlichen dritten Schritt wirken. Das kann stimmen — aber nur, wenn klar ist, was Ingress über einen Service hinaus liefert und warum das Ingress-Objekt allein keine Ports öffnet.
Dieser Beitrag richtet sich an die Phase, in der ClusterIP-Services Sinn ergeben, vielleicht kubectl port-forward oder NodePort ausprobiert wurde und jetzt ein klarer HTTP-Einstieg mit Hostnamen und Pfaden gewünscht ist. Ich bleibe bescheiden im Umfang. Ingress ist nicht der einzige Weg, HTTP-Workloads zu exponieren. Gateway API, Cloud-Load-Balancer und Service Meshes existieren. Ingress ist aber noch das Muster, dem Anfänger am häufigsten begegnen — und beim Debuggen lernt man nützliche Gewohnheiten.
Was ein Service tut — und was nicht
Ein Service ist ein stabiler Vertrag über eine sich ändernde Menge von Pods. Clients nutzen einen Namen und Port. Kubernetes hält nach, welche Pod-IPs gerade ready hinter diesem Namen stehen.
Für eine typische Web-App:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
Im Cluster kann ein anderer Pod http://web oder http://web.default.svc.cluster.local aufrufen. Das ist wertvoll. Es gilt aber innerhalb des Clusters.
Ein ClusterIP-Service liefert nicht von allein:
- Eine öffentliche URL im Internet
- Routing nach Hostname (
shop.example.comvs.api.example.com) - Routing nach URL-Pfad (
/apizu einem Service,/zu einem anderen) - TLS-Terminierung an einer gemeinsamen Front Door
Man kann einen Service nach außen bringen mit kubectl port-forward, NodePort oder LoadBalancer-Typ. Für Lernen und manche Produktionsmuster reicht das. Es wird unhandlich, wenn viele HTTP-Services hinter einer externen IP und einem TLS-Zertifikat liegen sollen.
Genau diese Lücke soll Ingress schließen.
Was Ingress ergänzt
Ein Ingress ist eine deklarative Beschreibung von HTTP-Routing-Regeln. Er sagt zum Beispiel:
- Anfragen für Host
shop.example.commit Pfad/gehen an Servicewebauf Port 80. - Anfragen für Host
api.example.commit Pfad/v1gehen an Serviceapiauf Port 8080.
Beispiel:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
spec:
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Langsam lesen. Der Ingress führt kein nginx oder traefik aus. Er lauscht nicht auf Port 443. Er ist ein Regel-Objekt. Etwas anderes muss diese Regeln lesen und echtes Traffic-Handling konfigurieren.
Dieses „Etwas andere“ ist der Ingress-Controller.
Häufiger Irrtum: „Ich habe einen Ingress angewendet, also ist meine App im Internet.“ Noch nicht. Man hat Routing-Absicht deklariert. Bis ein Controller das umsetzt und DNS oder Port-Zugang auf diesen Controller zeigen, ändert sich extern nichts.
Ingress-Regeln: Host, Pfad und Backend
Die meisten Anfänger-Ingress-Manifeste nutzen spec.rules. Jede Regel kann enthalten:
- host — den HTTP-Host-Header, zum Beispiel
shop.example.com - http.paths — eine Liste aus Pfad-Matches und Backends
Jeder Pfad braucht:
- path — etwa
/oder/api - pathType — wie gematcht wird.
Prefixist üblich für „dieser Pfad und alles darunter“.Exactmatcht nur exakt diesen Pfad. - backend.service — Service-Name und Portnummer für das Weiterleiten
Zwei Services hinter einem Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
Wichtige Details, die Anfänger übersehen:
Der Backend-Port ist der Service-Port, nicht zwingend der Container-Port. Mappt der Service Port 80 auf targetPort 8080, sollte das Ingress-Backend Port 80 referenzieren.
Labels müssen passen. Der Ingress zeigt per Name auf einen Service. Der Service wählt Pods über Labels. Hat der Service keine ready Endpoints, hat Ingress nichts Gesundes, wohin Traffic gehen kann.
Default-Backend. Manche Controller unterstützen ein Default-Backend für Anfragen ohne passende Regel. Nützlich für eigene Fehlerseiten. Für erste Experimente nicht nötig.
Anwenden und prüfen:
kubectl apply -f app-ingress.yaml
kubectl get ingress
kubectl describe ingress app-ingress
kubectl describe ingress zeigt oft controller-spezifische Annotations, Events und manchmal ein Adressfeld, sobald der Controller eine IP oder einen Hostnamen zuweist.
Ein Ingress-Controller ist nötig
Kubernetes liefert die Ingress-API. Es liefert nicht in jedem Cluster einen Standard-Ingress-Controller.
Diese Unterscheidung verwirrt fast jeden mindestens einmal.
Ein Ingress-Controller ist ein Pod (oder Pod-Set), der:
- Ingress-Objekte beobachtet
- Services und Endpoints liest
- Einen Proxy oder Cloud-Load-Balancer konfiguriert (nginx, traefik, HAProxy, cloud-spezifische Implementierungen und andere)
Läuft kein Controller, sitzt der Ingress in etcd und wirkt wichtig — während niemand lauscht.
Prüfen, ob ein Controller existiert:
kubectl get pods -A | grep -i ingress
kubectl get ingressclass
Unter minikube kann man das Addon aktivieren:
minikube addons enable ingress
kubectl get pods -n ingress-nginx
Unter kind installiert man typischerweise selbst einen Controller, zum Beispiel das ingress-nginx-Manifest aus der Projektdokumentation. Der genaue Install-Befehl ändert sich; der aktuellen Doku für die Cluster-Version folgen.
Bei verwalteten Cloud-Clustern (EKS, GKE, AKS) bietet die Plattform oft einen eigenen Controller oder eine Integration. Das Muster bleibt gleich: Ingress-YAML plus laufender Controller.
IngressClass verknüpft einen Ingress mit einer konkreten Controller-Implementierung:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Hat der Cluster mehrere Controller, verhindert ingressClassName, dass der falsche die Regeln übernimmt.
TLS: kurz erwähnt
Ingress kann TLS-Zertifikate für Hostnamen beschreiben. Der Controller terminiert HTTPS und leitet meist HTTP an Services weiter.
Typisches Muster mit TLS-Secret:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress-tls
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Das Secret muss im selben Namespace wie der Ingress existieren und gültiges Zertifikat plus Key enthalten. Viele Teams nutzen cert-manager für automatische Beschaffung und Erneuerung von Let’s Encrypt oder einer internen CA. Das ist ein eigenes Thema.
Fürs lokale Lernen ist TLS optional. Erst mit HTTP testen, Routing zum Laufen bringen, dann TLS ergänzen, wenn der Pfad klar ist. Zertifikats-Automatisierung sollte das Verständnis von Host- und Pfad-Regeln nicht blockieren.
Produktionshinweis: TLS am Ingress ist verbreitet, aber nicht das einzige Design. Manche Architekturen terminieren TLS am Cloud-Load-Balancer und senden Plain HTTP in den Cluster. Andere nutzen ein Service Mesh. Ingress-TLS ist ein praktischer Einstieg, kein universelles Gesetz.
Leere Backends debuggen
Das häufigste Ingress-Symptom: Man öffnet die URL und bekommt 502 Bad Gateway, 503 Service Unavailable oder eine Default-Backend-Seite. Der Ingress existiert. Der Controller läuft. Trotzdem nichts Nützliches.
Von außen nach innen arbeiten — aber Backends früh prüfen.
Schritt 1 — Ingress existiert und hat eine Adresse:
kubectl get ingress -o wide
kubectl describe ingress app-ingress
Gibt es ADDRESS oder externen Hostnamen? Ist das Feld leer, provisioniert der Controller vielleicht noch, oder Service-Typ und Cloud-Integration brauchen Aufmerksamkeit. Lokal mappt man demo.example.com oft auf 127.0.0.1 in /etc/hosts und nutzt port-forward oder minikube tunnel — je nach Setup.
Schritt 2 — Backend-Service existiert:
kubectl get svc web api
kubectl describe svc web
Selector, Ports und Typ prüfen.
Schritt 3 — Endpoints existieren:
kubectl get endpoints web
kubectl get endpointslice -l kubernetes.io/service-name=web
Sind Endpoints leer, hat Ingress keine ready Pods. Das ist der Fall „leeres Backend“. Die Routing-Regel ist in Ordnung; die Service-Schicht nicht.
Schritt 4 — Pods sind ready:
kubectl get pods -l app=web -o wide
kubectl describe pod <pod-name>
Nach Readiness-Probe-Fehlern, Crash Loops, falschen Labels oder Pods in einem anderen Namespace als dem Service suchen.
Schritt 5 — Zuerst im Cluster testen:
kubectl run curl-test --rm -it --image=curlimages/curl -- sh
curl -v http://web
curl -v -H 'Host: demo.example.com' http://<ingress-controller-ip>/
Funktioniert Service-Zugang im Cluster, aber Ingress nicht, verdächtig sind Controller-Konfiguration, Pfad-Regeln oder Host-Header-Mismatch. Scheitert Service-Zugang im Cluster, Deployment und Service reparieren — bevor man Ingress beschuldigt.
Schritt 6 — Controller-Logs:
kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller --tail=50
Namespace und Label-Selector für den eigenen Controller anpassen.
Häufige Ursachen, die ich immer wieder sehe:
| Symptom | Wahrscheinliche Ursache |
|---|---|
| Ingress bekommt nie eine Adresse | Kein Controller, falsche IngressClass, Cloud-LB pending |
| 502 / leeres Backend | Service-Selector-Mismatch, keine ready Endpoints |
| Falsche App auf Pfad | Pfad-Reihenfolge oder pathType; spezifischere Pfade oft zuerst |
| Host funktioniert in curl, nicht im Browser | DNS oder /etc/hosts zeigt nicht auf Controller |
| TLS-Fehler | Fehlendes Secret, falscher Host im Zertifikat, abgelaufen |
Service vs Ingress: Schichten getrennt halten
Beim Debuggen die Schicht benennen:
- Deployment — laufen die richtigen Pods?
- Service — passen Selector und Ports? Gibt es Endpoints?
- Ingress — zeigen Host- und Pfad-Regeln auf den richtigen Service-Port?
- Ingress-Controller — läuft er und ist programmiert?
- DNS / TLS / externer Zugang — erreicht Traffic den Controller?
Direkt „Ingress ist kaputt“ zu sagen, wenn der Service null Endpoints hat, kostet Zeit. Das habe ich selbst getan. Die Lösung war eine Readiness-Probe, keine Annotation.
Abschließender Gedanke
Ingress ersetzt Services nicht. Es ist HTTP-Routing darüber.
Services zuerst lernen, bis Endpoint-Debugging normal wirkt. Dann Ingress-Controller hinzufügen, eine einfache Regel mit einem Host und einem Pfad anwenden und Traffic bestätigen. Zweiten Pfad ergänzen, dann TLS bei Bedarf.
Das Objektmodell ist einfacher, als es an einem schlechten Nachmittag wirkt: Ingress beschreibt, wohin HTTP-Traffic gehen soll; Services beschreiben, welche Pods ihn empfangen; der Controller macht die Beschreibung real. Scheitert etwas, prüfen, ob jede Schicht etwas Ready hat, das Traffic empfangen kann. Meist fehlt das bei einer Schicht — und das ist ein lösbares Problem, kein Grund, Ingress ganz zu meiden.