☸️ Kubernetes Lektion 12/20 ~9 Min. Fortgeschritten

Ingress und Gateway API

HTTP-Routing von außen in den Cluster.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du erklären, warum ein LoadBalancer-Service pro Anwendung nicht skaliert und wie Ingress dieses Problem mit HTTP-Routing löst. Du verstehst die Arbeitsteilung zwischen Ingress-Ressource und Ingress-Controller, weißt, wie TLS mit cert-manager automatisiert wird, und kannst einordnen, warum die Gateway API als Nachfolger entwickelt wurde und was sie besser macht.

Das Problem: ein Load Balancer pro Service ist keine Lösung

Aus Lektion 11 weißt du: Ein LoadBalancer-Service provisioniert in der Cloud einen echten Load Balancer. Bei 30 Microservices wären das 30 Load Balancer — 30-mal Kosten, 30 externe IPs, 30 DNS-Einträge, und TLS-Zertifikate müsstest du trotzdem irgendwo terminieren. Dabei wollen fast alle diese Services dasselbe: HTTP-Traffic empfangen, unterschieden nach Hostname oder Pfad.

Ingress setzt genau dort an: ein Einstiegspunkt für den ganzen Cluster, der HTTP/HTTPS-Anfragen anhand von Host und Pfad auf interne Services verteilt. api.example.com geht an den API-Service, shop.example.com/checkout an den Checkout-Service — alles über dieselbe externe IP. Layer-7-Routing statt einer IP pro Anwendung.

Ressource und Controller: wer macht hier eigentlich was

Die häufigste Verständnislücke: Eine Ingress-Ressource alleine tut nichts. Sie ist nur ein Regelwerk im API-Server — „Host X, Pfad Y → Service Z”. Damit Pakete fließen, braucht es einen Ingress-Controller: einen Reverse Proxy (nginx, Traefik, HAProxy, Envoy-basierte wie Contour), der als Deployment im Cluster läuft, Ingress-Ressourcen beobachtet und seine eigene Proxy-Konfiguration daraus generiert. In Clouds gibt es auch Controller, die stattdessen externe Infrastruktur konfigurieren, etwa der AWS Load Balancer Controller, der ALBs provisioniert.

Der Traffic-Pfad beim verbreiteten ingress-nginx: Vor dem Controller-Deployment steht ein einziger LoadBalancer-Service. Externe Anfrage → Cloud-LB → nginx-Pod → der schaut auf Host-Header und Pfad → leitet an die Pod-IPs des Ziel-Service weiter (typischerweise direkt an die Endpoints, nicht über die ClusterIP, um selbst das Load Balancing zu steuern).

Weil mehrere Controller parallel laufen können, sagt das Feld ingressClassName, welcher Controller eine Ingress-Ressource übernehmen soll. Eine fehlende oder falsche IngressClass ist der Klassiker unter den „mein Ingress tut nichts”-Fehlern.

Ehrlicher Trade-off: Ingress standardisiert nur den kleinsten gemeinsamen Nenner (Host, Pfad, TLS). Alles darüber hinaus — Timeouts, Rewrites, Rate Limiting, Auth — läuft über controller-spezifische Annotations wie nginx.ingress.kubernetes.io/rewrite-target. Deine Ingress-Manifeste sind damit faktisch an den Controller gekoppelt, ein Wechsel ist Migrationsarbeit.

TLS: cert-manager macht Zertifikate zum Non-Event

TLS terminiert sinnvollerweise am Ingress-Controller: Das Zertifikat liegt als Secret im Cluster, die Ingress-Ressource referenziert es unter spec.tls, und intern läuft der Traffic dann unverschlüsselt (oder per Service Mesh mTLS) weiter.

Zertifikate manuell zu beschaffen und rechtzeitig zu erneuern ist fehleranfällig — abgelaufene Zertifikate sind eine der peinlichsten Incident-Ursachen überhaupt. cert-manager automatisiert das: Du definierst einen ClusterIssuer (z. B. für Let’s Encrypt), annotierst deinen Ingress mit cert-manager.io/cluster-issuer, und cert-manager erledigt den Rest — ACME-Challenge beantworten, Zertifikat abholen, als Secret ablegen, vor Ablauf automatisch erneuern. Für die HTTP-01-Challenge erzeugt cert-manager temporär eine eigene Ingress-Route unter /.well-known/acme-challenge/; deshalb muss der Host öffentlich erreichbar sein. Für interne Hosts oder Wildcards brauchst du die DNS-01-Challenge mit API-Zugriff auf deinen DNS-Provider.

Gateway API: warum ein Nachfolger nötig wurde

Ingress hat zwei strukturelle Probleme. Erstens das Annotation-Chaos: Alles Interessante ist herstellerspezifisch und unvalidiert — ein Tippfehler in der Annotation fällt erst zur Laufzeit auf. Zweitens fehlende Rollentrennung: Eine Ingress-Ressource vermischt Infrastruktur-Belange (welcher Load Balancer, welche Ports, welche TLS-Policy) mit Anwendungs-Belangen (welcher Pfad zu welchem Service) in einem Objekt, das meist dem Anwendungsteam gehört.

Die Gateway API trennt das in drei Ressourcen-Ebenen: GatewayClass (welche Controller-Implementierung, verwaltet vom Anbieter oder Plattform-Team), Gateway (eine konkrete Listener-Instanz mit Ports und TLS, verwaltet vom Plattform-Team) und HTTPRoute (Routing-Regeln, verwaltet von den Anwendungsteams in deren eigenen Namespaces). Eine HTTPRoute bindet sich per parentRefs an ein Gateway — und das Gateway kontrolliert über allowedRoutes, wer sich anbinden darf. Genau die Multi-Tenancy-Geschichte, die Ingress nie sauber konnte.

Dazu sind Dinge, die bei Ingress Annotations brauchten, typisierte API-Felder: Header-Manipulation, Traffic-Splitting nach Gewichten (Canary!), Redirects. Die Gateway API ist seit Ende 2023 GA und wird von allen großen Controllern unterstützt; Ingress wird nicht weiterentwickelt, bleibt aber auf Jahre der Bestand in existierenden Clustern. Im Interview ist „Ingress für Bestand, Gateway API für Neues, hier die Gründe” eine starke Position.

Praxis

Mit minikube ist ingress-nginx ein Addon — danach baust du ein Host-Routing nach:

minikube start
minikube addons enable ingress
kubectl get pods -n ingress-nginx   # Controller läuft?

# Zwei Backends mit Services
kubectl create deployment app1 --image=nginxdemos/hello:plain-text
kubectl create deployment app2 --image=nginxdemos/hello:plain-text
kubectl expose deployment app1 --port=80
kubectl expose deployment app2 --port=80
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo
spec:
  ingressClassName: nginx
  rules:
    - host: app1.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app1
                port:
                  number: 80
    - host: app2.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app2
                port:
                  number: 80
kubectl apply -f ingress.yaml
kubectl get ingress demo   # ADDRESS abwarten

# Host-Routing testen — der Host-Header entscheidet
curl --resolve app1.local:80:$(minikube ip) http://app1.local/
curl --resolve app2.local:80:$(minikube ip) http://app2.local/

Die beiden curl-Aufrufe treffen dieselbe IP, landen aber bei verschiedenen Pods — das ist Layer-7-Routing in einer Zeile sichtbar gemacht.

Typische Stolperfallen

IngressClass vergessen: Ohne ingressClassName (oder mit falschem Wert) fühlt sich kein Controller zuständig, die Ressource wird stillschweigend ignoriert. kubectl describe ingress zeigt dann keine Events vom Controller.

pathType falsch gewählt: Prefix matcht pfadsegmentweise, Exact nur exakt. Wer Exact: /api konfiguriert, wundert sich, warum /api/users einen 404 liefert.

404 vom Default-Backend statt von der App: Wenn der Host-Header nicht zu einer Regel passt (z. B. Test per IP statt Hostname), antwortet der Controller selbst mit 404. Der Fehler liegt dann nicht im Backend, sondern im Request.

cert-manager-Challenge scheitert: Bleibt das Zertifikat auf False, prüfe kubectl describe challenge. Häufigste Ursachen: Host ist von außen (und von Let’s Encrypt) nicht erreichbar, oder eine NetworkPolicy blockiert den Challenge-Pod.

Interview-Vorbereitung

Auf „Erkläre Ingress” antwortest du in dieser Linie: Problem (ein Cloud-LB pro Service skaliert nicht, alle wollen HTTP-Routing) → Lösung (ein Einstiegspunkt, L7-Routing nach Host/Pfad auf interne Services) → Architektur (Ingress-Ressource = Regeln, Ingress-Controller = Reverse Proxy, der sie umsetzt; davor ein einziger LoadBalancer-Service) → Grenze (Standard deckt nur Host/Pfad/TLS ab, Rest über controller-spezifische Annotations — daher die Gateway API als typisierter, rollengetrennter Nachfolger).

Typische Follow-ups:

  • „Unterschied Ingress vs. Service vom Typ LoadBalancer?” — LoadBalancer arbeitet auf L4 und kostet pro Service einen Cloud-LB; Ingress routet auf L7 viele Hosts/Pfade über einen Einstiegspunkt.
  • „Was verbessert die Gateway API konkret?” — Rollentrennung (GatewayClass/Gateway/HTTPRoute), typisierte Felder statt Annotations, Cross-Namespace-Routing mit expliziter Erlaubnis, Traffic-Splitting nativ.
  • „Wie läuft TLS mit Let’s Encrypt im Cluster?” — cert-manager mit ClusterIssuer, ACME-Challenge (HTTP-01 oder DNS-01), Zertifikat als Secret, automatische Erneuerung.
  • „Ingress angelegt, aber nichts passiert — erste Schritte?” — IngressClass prüfen, Controller-Logs lesen, kubectl describe ingress auf Events, Backend-Service und Endpoints checken.

Zusammenfassung

Ingress bündelt den HTTP-Zugang zum Cluster: ein LoadBalancer, ein Controller, beliebig viele Host- und Pfad-Regeln auf interne Services. Die Ressource beschreibt, der Controller setzt um — und alles jenseits des Standards lebt in Annotations, was die Gateway API mit typisierten, rollengetrennten Ressourcen (GatewayClass, Gateway, HTTPRoute) ablöst. TLS automatisierst du mit cert-manager und Let’s Encrypt, statt Zertifikate von Hand zu drehen.

Ingress regelt den Traffic von außen nach innen — wer aber kontrolliert, welcher Pod mit welchem Pod sprechen darf? Das ist Aufgabe des CNI und der NetworkPolicies in der nächsten Lektion.