Ein Kubernetes Ingress ist ein Objekt, das HTTP- und HTTPS-Anfragen von außerhalb des Clusters anhand von Hostname und Pfad an Services im Cluster weiterleitet. Das Objekt selbst ist nur eine Regelbeschreibung: Erst ein Ingress Controller wie ingress-nginx oder Traefik liest diese Regeln und baut daraus einen laufenden Reverse Proxy. Ohne Controller tut ein Ingress nichts, und genau daran scheitert der erste Versuch bei vielen.
Ich betreibe Kubernetes-Cluster in Produktion und habe das Kubernetes-Praxisbuch bei Rheinwerk geschrieben (2024). In fast jedem Cluster, den ich aufgesetzt habe, war der Ingress die Stelle, an der die Anwendung zum ersten Mal für echte Nutzer erreichbar wurde, und die Stelle, an der die ersten 404 und 503 auftauchten. Was ich dabei gelernt habe, steht hier in Kurzform. Alle meine Beiträge zu Kubernetes findest du gesammelt auf der Seite Kubernetes.
Was der Ingress macht und was der Service macht
Innerhalb des Clusters sorgt der Service dafür, dass deine Pods eine stabile Adresse haben. Er kennt alle Replikas eines Deployments, verteilt Anfragen darauf und bekommt einen DNS-Namen, den andere Pods ansprechen können. Wie die Service-Typen ClusterIP, NodePort und LoadBalancer funktionieren, erkläre ich in Kubernetes Service Types erklärt. Der Service beantwortet aber nicht die Frage, wie ein Browser von außen in den Cluster kommt.
Dafür gibt es den Ingress. Stell dir ein Bürogebäude vor, in dem mehrere Firmen sitzen. Der Empfang im Erdgeschoss fragt jeden Besucher, zu wem er will, und schickt ihn in den richtigen Stock. Das ist der Ingress: Er schaut auf Hostname und Pfad der Anfrage und entscheidet, welcher Service zuständig ist. Oben im Stockwerk übernimmt dann die Abteilung, also der Service, und verteilt den Besucher auf einen freien Mitarbeiter, den Pod.
Technisch arbeitet der Ingress auf Layer 7 des OSI-Modells, er versteht also HTTP. Deshalb kann er nach URL routen, Pfade umschreiben, TLS beenden und je nach Controller Anfragen authentifizieren oder begrenzen. Ein NodePort-Service arbeitet dagegen auf Layer 4 und reicht einfach alles weiter, was auf einem Port ankommt. Für HTTP-Anwendungen empfehle ich deshalb praktisch immer den Weg über den Ingress.
|
Service (ClusterIP) |
Service (NodePort) |
Ingress |
| Erreichbar von |
innerhalb des Clusters |
außen über Node-IP und Port |
außen über Hostname und Pfad |
| Arbeitet auf |
Layer 4 |
Layer 4 |
Layer 7 (HTTP) |
| Kennt Pods |
ja, über Selektor |
ja, über Selektor |
nein, verweist auf Services |
| TLS |
nein |
nein |
ja, mit Zertifikat als Secret |
| Braucht zusätzlich |
nichts |
nichts |
einen Ingress Controller |
Ingress und Service ersetzen sich also nicht, sie arbeiten hintereinander: Der Ingress leitet an einen Service weiter, nie direkt an einen Pod.
Der Ingress Controller: ingress-nginx, Traefik oder Cloud-Anbieter
Kubernetes liefert für den Ingress nur die API. Die Umsetzung übernimmt ein Ingress Controller, den du oder deine Cluster-Admins separat installieren. Der Controller beobachtet alle Ingress-Objekte im Cluster und konfiguriert daraus einen Reverse Proxy, der die Anfragen tatsächlich entgegennimmt. Diese Trennung ist bewusst so gebaut: Das Manifest bleibt gleich, egal ob dahinter ein Nginx im Cluster oder ein Load Balancer in der Cloud steht.
Die drei Varianten, die dir in der Praxis begegnen:
| Controller |
Läuft wo |
Typischer Einsatz |
| ingress-nginx |
als Pods im Cluster |
On-Premise und bei jedem Cloud-Anbieter, größte Verbreitung, viele Annotations |
| Traefik |
als Pods im Cluster |
in k3s bereits enthalten, Konfiguration über eigene CRDs oder Ingress |
| Cloud-Controller (z.B. AWS Load Balancer Controller) |
erzeugt einen Load Balancer beim Anbieter |
Managed Kubernetes in der Cloud, Proxy liegt außerhalb des Clusters |
Ein Hinweis zur Verwechslung, die mich anfangs Zeit gekostet hat: ingress-nginx ist das Projekt der Kubernetes-Community. Daneben gibt es einen NGINX Ingress Controller des Herstellers F5. Beide nutzen Nginx, haben aber andere Annotations und andere Dokumentation. Wenn du nach einer Annotation suchst, prüfe zuerst, welchen der beiden du installiert hast.
Damit der Controller weiß, welche Ingress-Objekte ihn betreffen, gibt es die IngressClass. Du setzt sie im Ingress unter spec.ingressClassName. Läuft nur ein Controller im Cluster, ist meist eine Standard-Klasse markiert, und du kannst das Feld weglassen. Sobald zwei Controller laufen, etwa ein interner und ein öffentlicher, ist die Klasse Pflicht, sonst nimmt sich keiner oder der falsche den Ingress. In meinen k3s-Clustern auf Hetzner nutze ich den mitgelieferten Traefik, in Clustern mit anderen Distributionen meist ingress-nginx. Die Manifeste für die Anwendungen sind in beiden Fällen dieselben, mit Ausnahme der Annotations.
Ingress-Regeln: Host, Pfad und pathType
Eine Ingress-Regel besteht aus einem optionalen Host und einer Liste von Pfaden, die jeweils auf einen Service und einen Port zeigen. Fehlt der Host, gilt die Regel für jede Anfrage, die den Controller erreicht. Das folgende Manifest leitet den Shop einer fiktiven Firma an ein Frontend und die API an ein zweites Deployment weiter:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
Zwei Dinge sind hier wichtig. Erstens: Bei mehreren passenden Pfaden gewinnt der längste. Ein Aufruf von /api/orders landet bei der API, obwohl / ebenfalls passen würde. Zweitens: Der Ingress reicht den kompletten Pfad an die Anwendung weiter. Die API bekommt also /api/orders und muss damit umgehen können. Erwartet sie /orders, brauchst du eine Rewrite-Regel, die je nach Controller über eine Annotation oder eine Middleware konfiguriert wird.
Der pathType bestimmt, wie streng verglichen wird:
| pathType |
Verhalten |
| Prefix |
Der aufgerufene Pfad muss mit dem definierten Pfad beginnen, verglichen wird abschnittsweise an den Schrägstrichen |
| Exact |
Der Pfad muss genau übereinstimmen, inklusive Groß- und Kleinschreibung; Exact gewinnt bei einem Treffer gegen Prefix |
| ImplementationSpecific |
Der Controller entscheidet, wie er den Pfad interpretiert |
Ich nehme fast immer Prefix und vermeide ImplementationSpecific, weil damit dasselbe Manifest bei verschiedenen Controllern unterschiedlich reagieren kann. Neben den Regeln kannst du ein defaultBackend definieren, das alle Anfragen bekommt, für die keine Regel passt. In der Praxis liegt dort eine eigene Fehlerseite; die meisten Controller bringen ansonsten eine schlichte 404-Seite mit.
Kubernetes Ingress mit TLS: HTTPS mit cert-manager
Ohne TLS ist ein Ingress heute nicht produktionstauglich. Das Zertifikat liegt in Kubernetes als Secret vom Typ kubernetes.io/tls, und der Ingress verweist unter spec.tls darauf. Der Controller beendet die TLS-Verbindung, die Kommunikation zum Service dahinter läuft dann in der Regel unverschlüsselt im Clusternetz. Wie Secrets aufgebaut sind und warum sie nicht ins Git gehören, steht in ConfigMap und Secret in Kubernetes.
Zertifikate von Hand zu erneuern ist der Weg in den Vorfall, den du dir erst Monate später einfängst. Deshalb läuft in jedem meiner Cluster cert-manager. Das Werkzeug beobachtet Ingress-Objekte, holt bei einer Zertifizierungsstelle wie Let's Encrypt ein Zertifikat, legt das Secret an und erneuert es rechtzeitig. Du brauchst dafür einmalig einen Issuer oder ClusterIssuer, der beschreibt, wo die Zertifikate herkommen, und danach im Ingress nur noch eine Annotation und den TLS-Block:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
Mit dieser Annotation erzeugt cert-manager ein Certificate-Objekt, löst die Let's-Encrypt-Prüfung aus und schreibt das Ergebnis in das Secret shop-tls. Für die verbreitete HTTP-01-Prüfung muss der Host aus dem Internet auf Port 80 erreichbar sein, weil Let's Encrypt dort eine Datei abruft. Für interne Hostnamen oder Wildcard-Zertifikate brauchst du die DNS-01-Prüfung, bei der cert-manager einen Eintrag bei deinem DNS-Anbieter setzt. Ob das Zertifikat da ist, siehst du mit kubectl get certificate -n shop; steht dort READY False, zeigt kubectl describe certificate die Ursache, meistens eine nicht erreichbare Domain.
Ingress ohne Cloud: wo der Traffic ankommt
Bis hierhin ging es um Regeln. Die Frage, die Cloud-Dokumentationen gern überspringen: Wie kommt eine Anfrage aus dem Internet überhaupt bis zum Ingress Controller? Der Controller ist selbst nur ein Deployment mit einem Service davor, meist vom Typ LoadBalancer. In einer Cloud legt der Anbieter dafür automatisch einen Load Balancer mit öffentlicher IP an, und dein DNS zeigt auf diese IP.
On-Premise gibt es diesen Automatismus nicht. Der LoadBalancer-Service bleibt dann auf <pending> stehen, bis du selbst für eine externe Adresse sorgst: mit MetalLB, mit einem vorgeschalteten Load Balancer auf einen NodePort oder mit ServiceLB, das k3s mitbringt. Welche Variante wann passt, habe ich bei den Service-Typen in Kubernetes Service Types erklärt beschrieben. Für den Ingress zählt nur eines: Vor dem Controller muss eine Adresse stehen, auf die dein DNS zeigen kann, und für Ausfallsicherheit sollte diese Adresse nicht an einer einzelnen Node hängen.
Welche Variante zu deinem Betrieb passt, hängt stark davon ab, ob du selbst hostest oder einen Anbieter nimmst. Die Abwägung dazu steht in Kubernetes on-premise oder Cloud?.
Typische Fehler beim Ingress und wie du sie findest
Die drei Fehlerbilder, die ich am häufigsten sehe, lassen sich mit wenigen Befehlen eingrenzen:
kubectl get ingress -n shop
kubectl describe ingress shop -n shop
kubectl get endpoints frontend -n shop
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=50
Ein 404 vom Controller bedeutet meist, dass keine Regel gepasst hat. Der häufigste Grund ist ein Host, der nicht mit dem aufgerufenen Namen übereinstimmt, zum Beispiel weil du die IP statt der Domain aufrufst. Steht bei kubectl get ingress keine Adresse in der Spalte ADDRESS, hat sich kein Controller für den Ingress zuständig gefühlt: Prüfe die ingressClassName.
Ein 503 oder 502 bedeutet, dass die Regel gepasst hat, aber hinter dem Service nichts antwortet. Dann lohnt der Blick auf kubectl get endpoints: Ist die Liste leer, stimmt der Selektor des Service nicht mit den Pod-Labels überein, oder die Pods sind nicht ready. Ein vertippter Service-Name oder ein falscher Port im Ingress zeigt sich im describe als Warnung.
Bleibt HTTPS rot, obwohl der Ingress läuft, liegt es fast immer am Zertifikat. kubectl describe certificate und die Events von cert-manager sagen dir, ob die Prüfung fehlgeschlagen ist. Dabei ist die eigentliche Ursache oft ein DNS-Eintrag, der noch auf die alte Adresse zeigt, oder ein Port 80, der von einer Firewall blockiert wird.
Gateway API: die weiterführende Option neben dem Ingress
Der Ingress hat eine bekannte Schwäche: Alles, was über Host und Pfad hinausgeht, lebt in Annotations, und die sind je Controller verschieden. Die Gateway API löst das mit mehreren Objekten. Ein Gateway beschreibt den Einstiegspunkt und gehört dem Cluster-Team, eine HTTPRoute beschreibt das Routing und gehört dem Anwendungsteam. Header-Matching, Traffic-Splitting und Umschreibungen sind im Standard enthalten statt in Annotations. Die offizielle Einführung in die Gateway API zeigt die Objekte im Detail.
Meine Einschätzung: Für einen neuen Cluster mit mehreren Teams lohnt sich der Blick auf die Gateway API, weil die Rollentrennung sauberer ist. Für den Standardfall, eine Anwendung unter einer Domain mit TLS, ist der Ingress weiter völlig ausreichend, wird von jedem Controller unterstützt und wird nicht abgeschaltet. Umbauen musst du also nichts, solange dir Annotations nicht im Weg stehen.
Häufige Fragen
Was ist der Unterschied zwischen Service und Ingress?
Der Service gibt Pods eine stabile Adresse innerhalb des Clusters und verteilt Anfragen auf die Replikas. Der Ingress leitet HTTP-Anfragen von außen anhand von Hostname und Pfad an Services weiter. Beide arbeiten zusammen: Der Ingress kennt keine Pods, sondern nur Services.
Was ist ein Ingress Controller?
Ein Ingress Controller ist die Software, die Ingress-Objekte liest und daraus einen laufenden Reverse Proxy konfiguriert. Kubernetes liefert ihn nicht mit; verbreitet sind ingress-nginx, Traefik und die Controller der Cloud-Anbieter. Ohne installierten Controller wird ein Ingress zwar angelegt, aber nie bedient.
ingress-nginx oder Traefik: was soll ich nehmen?
Beide sind für den Standardfall gleichwertig. ingress-nginx ist am weitesten verbreitet und hat für fast jeden Sonderfall eine Annotation. Traefik ist in k3s bereits enthalten und lässt sich über eigene CRDs feiner konfigurieren. Nimm das, was dein Cluster mitbringt, und wechsle nur, wenn dir eine konkrete Funktion fehlt.
Brauche ich für jede Anwendung einen eigenen Ingress?
Nein. Ein Ingress kann mehrere Hosts und Pfade auf verschiedene Services verteilen, und die meisten Controller führen mehrere Ingress-Objekte für denselben Host zusammen. Eine Grenze gibt es trotzdem: Ein Ingress gehört zu einem Namespace und kann nur Services aus diesem Namespace ansprechen. Anwendungen in getrennten Namespaces brauchen deshalb je ein eigenes Ingress-Objekt.
Ingress oder Gateway API für ein neues Projekt?
Für eine Anwendung mit Domain und TLS reicht der Ingress und ist überall unterstützt. Wenn mehrere Teams denselben Einstiegspunkt teilen oder du Funktionen wie Traffic-Splitting ohne Annotations brauchst, ist die Gateway API die sauberere Wahl.
Wie du weitermachst
Bevor du den Ingress anfasst, sollte der Service dahinter stehen und über kubectl get endpoints echte Pod-Adressen zeigen. Die Grundlagen dazu stehen in Kubernetes Service Types erklärt. Danach lohnt sich ein kurzer Blick in die offizielle Dokumentation zu Ingress in Kubernetes, vor allem für die Beispiele zum pathType.
Mein Vorschlag für heute: Nimm das erste Manifest von oben, tausche Hostname und Service-Namen gegen deine eigenen und rolle es aus. Dann rufe die Domain im Browser auf, danach absichtlich die IP-Adresse, und schau dir an, wie der Controller in beiden Fällen reagiert. Wenn das sitzt, kommt cert-manager dazu.
Ausführlich mit allen Beispielen, von der Kommunikation zwischen Pods über die Service-Typen bis zum Ingress mit mehreren Pfaden, steht das in Kapitel 3 meines Kubernetes-Praxisbuchs bei Rheinwerk.