Kubernetes Ingress configureren: extern verkeer naar services routeren

Ingress geeft je HTTP/HTTPS-routing, TLS-terminatie en host-based of path-based verkeersverdeling achter een enkele load balancer. Dit artikel loopt stap voor stap door het opzetten van Ingress: een IngressClass kiezen en installeren, host-based en path-based rules configureren, TLS termineren met cert-manager, en de meest gebruikte ingress-nginx annotations toepassen in productie.

Doel

Aan het eind van dit artikel heb je een werkende Ingress-resource die extern HTTP/HTTPS-verkeer routeert naar een of meer backend-Services via host-based en path-based rules, met automatische TLS-certificaten via cert-manager.

Vereisten

  • Een Kubernetes-cluster op v1.28 of nieuwer met kubectl-toegang en rechten om Ingress-, IngressClass- en Secret-objecten aan te maken
  • Minimaal een applicatie achter een ClusterIP Service. Ingress routeert naar Services, niet rechtstreeks naar pods. Ben je niet bekend met Service-typen? Het gelinkte artikel behandelt ClusterIP, NodePort, LoadBalancer en wanneer je welk type kiest.
  • Een Ingress controller geinstalleerd in het cluster (zie volgende sectie). Zonder controller doet een Ingress-resource niets.
  • Voor automatische TLS: cert-manager v1.16+ met een werkende ClusterIssuer. De cert-manager-documentatie beschrijft de installatie.
  • Helm 3.x lokaal geinstalleerd (voor de controller-installatievoorbeelden)
  • DNS-records die je domein(en) naar het externe IP van de Ingress controller wijzen

Ingress vs Service LoadBalancer

Ingress werkt op OSI Layer 7 (HTTP/HTTPS). Een enkele Ingress controller zit achter een cloud load balancer en routeert verkeer naar meerdere backend-Services op basis van hostnaam en URL-pad. Een Service van het type LoadBalancer werkt op Layer 4 (TCP/UDP) en maakt per Service een aparte cloud load balancer aan.

Het kostenverschil loopt snel op. Tien LoadBalancer-Services betekent tien cloud load balancers, elk met een eigen IP en maandelijkse kosten. Op AWS kost een NLB zo'n $16/maand nog voor data transfer. Ingress brengt alle HTTP/HTTPS-routing samen achter een enkele load balancer.

Criterium LoadBalancer Service Ingress
Protocol TCP, UDP, elk Alleen HTTP/HTTPS
Routinglogica Geen (IP + poort) Host-based, path-based
TLS-terminatie Nee (pass-through of elders) Ja, aan de edge
Cloud LBs aangemaakt Een per Service Een totaal
Toepassing Non-HTTP-protocols, enkele service Meerdere HTTP-services achter een IP

Gebruik LoadBalancer voor non-HTTP-protocols (raw TCP, UDP, gRPC zonder HTTP-transcoding). Gebruik Ingress voor alles wat HTTP/HTTPS is.

IngressClass en controller-selectie

Een IngressClass is een cluster-scoped resource die een Ingress koppelt aan een specifieke controller. Zonder IngressClass kan een cluster met meerdere controllers niet bepalen welke controller een bepaalde Ingress moet verwerken.

Stap 1: installeer een Ingress controller

De Kubernetes API definieert de Ingress-resource, maar levert geen controller mee. Die moet je zelf installeren. Voor clusters die ingress-nginx gebruiken: de community controller (kubernetes/ingress-nginx) is in maart 2026 gearchiveerd en krijgt geen beveiligingspatches meer. Kies voor nieuwe deployments een actief onderhouden controller.

Gangbare keuzes per april 2026:

  • Traefik (traefik.io): dynamische service discovery, ingebouwde Let's Encrypt-ondersteuning, eenvoudige migratie vanuit ingress-nginx
  • HAProxy Ingress (haproxy-ingress.github.io): hoogste ruwe doorvoer in gepubliceerde benchmarks (~42.000 req/s vs ~19.000 voor Traefik), zero-downtime reloads, HTTP/3 native
  • Contour (projectcontour.io): Envoy-based, goede Gateway API-ondersteuning
  • AWS Load Balancer Controller: cloud-native op EKS

Voorbeeld-installatie (Traefik via Helm):

helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik \
  --namespace traefik --create-namespace

Controleer of de controller draait:

kubectl get pods -n traefik
# Verwacht: traefik-<hash> in Running-status

Stap 2: controleer de IngressClass

De meeste Helm charts maken automatisch een IngressClass aan. Check:

kubectl get ingressclass

Verwachte output:

NAME      CONTROLLER                    PARAMETERS   AGE
traefik   traefik.io/ingress-controller <none>       2m

Heeft de IngressClass van je controller de annotatie ingressclass.kubernetes.io/is-default-class: "true", dan gebruiken Ingress-resources zonder ingressClassName automatisch deze class. In clusters met meerdere controllers zet je ingressClassName altijd expliciet op elke Ingress.

Stap 3: verwijs naar de IngressClass in je Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
spec:
  ingressClassName: traefik   # moet overeenkomen met de IngressClass-naam
  rules: [...]

Is de IngressClass-naam fout of ontbreekt die, en is er geen default ingesteld? Dan negeert de controller de Ingress volledig. Geen foutmelding, geen event, gewoon stilte. Dit is de meest voorkomende setupfout.

Host-based routing

Host-based routing inspecteert de Host-HTTP-header en stuurt verkeer naar verschillende Services op basis van de hostnaam.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host
  namespace: production
spec:
  ingressClassName: traefik
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: frontend
            port:
              number: 80
  - host: api.staging.infra.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: backend-api
            port:
              number: 8080

Requests naar app.staging.infra.example.com gaan naar de frontend-Service. Requests naar api.staging.infra.example.com gaan naar backend-api.

Kubernetes ondersteunt single-level wildcard hosts: *.example.com matcht app.example.com en api.example.com, maar niet example.com (bare domain) of foo.bar.example.com (twee niveaus diep).

Een rule zonder host-veld is een catch-all die alle inkomende requests matcht, ongeacht de hostnaam.

Path-based routing en pathType

Binnen een host-blok routeren path-rules requests naar verschillende backends op basis van het URL-pad. Elke path-rule vereist een pathType-veld (verplicht sinds Kubernetes v1.18).

Prefix (meest gebruikt)

Matcht het pad element-voor-element. /api matcht /api, /api/ en /api/v1/users, maar niet /apiv1. De grens is altijd een /-karakter.

- path: /api
  pathType: Prefix
  backend:
    service:
      name: api-service
      port:
        number: 80

Exact

Matcht alleen het letterlijke pad. /api matcht /api, maar niet /api/ (trailing slash) en niet /api/v1.

- path: /healthz
  pathType: Exact
  backend:
    service:
      name: health-service
      port:
        number: 8080

Gebruik Exact spaarzaam. Een gebruiker of load balancer die een trailing slash toevoegt, breekt de match.

ImplementationSpecific

Laat de matching over aan de controller. Met ingress-nginx en de annotatie nginx.ingress.kubernetes.io/use-regex: "true" wordt het pad behandeld als een POSIX extended reguliere expressie.

Let op: use-regex inschakelen op een willekeurige Ingress voor een bepaalde host forceert case-insensitive regex matching op alle paden voor die host, ook paden op andere Ingress-resources. Dit is een cluster-breed neveneffect dat Exact- en Prefix-matching voor andere services op dezelfde host kan breken.

Padprioriteit

Als meerdere rules matchen, wint het langste matchende pad. Exact gaat voor Prefix bij gelijke padlengte.

Gecombineerd voorbeeld

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-routing
  namespace: production
spec:
  ingressClassName: traefik
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: backend-api
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80

Requests naar /api/v1/orders matchen de /api-Prefix-rule en gaan naar backend-api. Al het andere valt door naar de /-rule en gaat naar frontend.

Controleer of routing werkt:

kubectl describe ingress app-routing -n production
# Bekijk de "Rules"-sectie voor correcte host/path/backend-mappings

curl -H "Host: app.staging.infra.example.com" http://<ingress-controller-ip>/api/health
# Verwacht: response van backend-api

TLS-terminatie

TLS-terminatie betekent dat de Ingress controller HTTPS ontsleutelt aan de edge. Backend-Services ontvangen plain HTTP, wat certificaatbeheer centraliseert en cryptografische overhead van applicatiepods wegneemt.

Stap 1: maak een TLS Secret aan

Het Secret moet in dezelfde namespace staan als de Ingress. Het bevat tls.crt (PEM-certificaatketen) en tls.key (PEM-private key).

kubectl create secret tls app-tls-cert \
  --cert=fullchain.pem \
  --key=privkey.pem \
  -n production

Stap 2: verwijs naar het Secret in de Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  namespace: production
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - app.staging.infra.example.com
    secretName: app-tls-cert
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80

Elke host in spec.tls[].hosts moet ook in spec.rules staan. De Ingress API ondersteunt TLS alleen op poort 443.

Controleer of TLS actief is:

kubectl describe ingress tls-ingress -n production
# Zoek naar "TLS: app-tls-cert terminates app.staging.infra.example.com"

curl -v https://app.staging.infra.example.com/
# Verwacht: TLS-handshake met het juiste certificaat

Stap 3: automatiseer certificaten met cert-manager

Handmatig certificaten beheren schaalt niet. cert-manager let op Ingress-resources met de juiste annotations, maakt automatisch Certificate-objecten aan, haalt certificaten op via ACME (Let's Encrypt) HTTP-01 of DNS-01 challenges, en verlengt ze voor het verlopen.

Voeg de cert-manager-annotatie en een secretName toe die cert-manager aanmaakt en bijhoudt:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod   # verwijst naar je ClusterIssuer
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - app.staging.infra.example.com
    secretName: app-auto-tls    # cert-manager maakt dit Secret aan
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80

Controleer of het certificaat is uitgegeven:

kubectl get certificate -n production
# Verwacht: app-auto-tls met READY=True

kubectl describe certificate app-auto-tls -n production
# Check "Status > Conditions" voor "Ready: True" en de vervaldatum

Blijft het certificaat in een niet-ready status hangen, bekijk dan de cert-manager logs en de Challenge-resource:

kubectl get challenges -n production
kubectl logs -n cert-manager deploy/cert-manager

Veelgebruikte ingress-nginx annotations

Als je de community ingress-nginx controller draait (of de opvolger van NGINX Inc.), passen annotations met het prefix nginx.ingress.kubernetes.io/ het gedrag per Ingress aan. Deze zijn controller-specifiek en werken niet met Traefik, HAProxy of andere controllers.

Deze sectie behandelt de annotations die ik het vaakst tegenkom in productie. Een volledige referentie staat in de ingress-nginx annotation-documentatie.

Routing en rewrites

Annotatie Doel Voorbeeld
rewrite-target Herschrijf het pad voor forwarding; ondersteunt capture groups /$2
use-regex Schakel POSIX regex-padmatching in "true"
backend-protocol Protocol naar de backend: HTTP, HTTPS, GRPC, GRPCS "HTTPS"

Een gangbaar rewrite-patroon om een padprefix te strippen:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - path: /app1(/|$)(.*)          # capture group $2 = rest van het pad
        pathType: ImplementationSpecific
        backend:
          service:
            name: app1
            port:
              number: 80

Een request naar /app1/login komt bij de backend aan als /login.

Request-afhandeling

Annotatie Doel Default Voorbeeld
proxy-body-size Max request body; retourneert 413 bij overschrijding 1m "50m"
proxy-read-timeout Upstream read-timeout (seconden) 60 "300"
proxy-connect-timeout Connectie-timeout naar upstream (seconden) 5 "30"

Rate limiting

Annotatie Doel Voorbeeld
limit-rps Requests per seconde per IP "20"
limit-connections Gelijktijdige connecties per IP "10"

Per-replica-valkuil. Rate limit-waarden gelden per ingress-nginx replica. Drie replica's met limit-rps: "10" laat 30 requests per seconde toe van een enkel IP. Houd hier rekening mee bij autoscaling.

TLS-besturing

Annotatie Doel Voorbeeld
ssl-redirect Schakel de automatische HTTP-naar-HTTPS redirect in/uit (default: "true" bij TLS) "false"
force-ssl-redirect Forceer redirect ook als TLS extern wordt getermineerd (bijv. AWS ELB) "true"

IP-allowlisting

annotations:
  nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,203.0.113.5/32"

Requests van IP's buiten deze CIDR-ranges krijgen een 403.

Wanneer migreren naar Gateway API

Gateway API is de officiele opvolger van de Ingress API. Het bereikte v1.0 GA in oktober 2023 en staat op v1.4.0 sinds november 2025. HTTPRoute, Gateway en GatewayClass zitten allemaal in het Standard-kanaal met backward-compatibiliteitsgaranties.

De belangrijkste architectuurverbetering: Gateway API scheidt infrastructuur (Gateway, beheerd door clusteroperators) van routing (HTTPRoute, beheerd door developers). Bij Ingress zijn beide zorgen samengevoegd in een enkele resource, wat dwingt tot ofwel te brede RBAC of tickets heen en weer tussen teams.

Migreer nu als:

  • Je kubernetes/ingress-nginx (community) draait. De repository is in maart 2026 gearchiveerd. Er volgen geen beveiligingspatches meer.
  • Je een nieuw cluster opzet. Er zijn geen migratiekosten.
  • Je geavanceerde routing nodig hebt: traffic splitting, header-based matching, request mirroring of retries. Dit zit ingebouwd in de HTTPRoute-spec van Gateway API en is niet beschikbaar in standaard Ingress.

Migreer op je eigen tempo als:

  • Je een actief onderhouden controller gebruikt (Traefik, HAProxy, Contour) die Gateway API al ondersteunt als parallelle optie. Ingress- en Gateway API-resources kunnen naast elkaar bestaan.

De ingress2gateway-tool (v1.0, maart 2026) converteert Ingress-manifests naar Gateway API-resources en ondersteunt 30+ gangbare ingress-nginx annotations. Voor een volledige migratie-walkthrough, zie de handleiding voor migratie van ingress-nginx naar Gateway API.

Compleet voorbeeld: productie-klare Ingress

Dit manifest brengt host-based routing, path-based routing, TLS met cert-manager en gangbare annotations samen:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production-ingress
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "20m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
    nginx.ingress.kubernetes.io/limit-rps: "25"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.staging.infra.example.com
    - api.staging.infra.example.com
    secretName: production-tls
  rules:
  - host: app.staging.infra.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80
  - host: api.staging.infra.example.com
    http:
      paths:
      - path: /v1
        pathType: Prefix
        backend:
          service:
            name: api-v1
            port:
              number: 8080
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-v2
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-v2    # standaard naar de nieuwste versie
            port:
              number: 8080

Veelvoorkomende problemen

Symptoom Waarschijnlijke oorzaak Oplossing
Ingress bestaat maar verkeer komt nooit aan Geen controller geinstalleerd, of ingressClassName is fout/ontbreekt kubectl get ingressclass en controleer of de naam klopt
404 op paden die zouden moeten matchen Verkeerde pathType (meestal Exact waar Prefix nodig is) Schakel over naar Prefix voor sub-tree routing
TLS-handshake faalt met "secret not found" TLS Secret in een andere namespace dan de Ingress Verplaats het Secret naar de Ingress-namespace
502 Bad Gateway Backend-Service heeft geen ready endpoints kubectl get endpoints <service> om pod-IPs te controleren
Rate limits voelen te hoog limit-rps is per replica, niet cluster-breed Deel de gewenste limiet door het aantal replica's
use-regex: "true" breekt andere services op dezelfde host Regex-modus geldt voor alle paden op die host Isoleer Ingress-resources met regex op aparte hosts

Voor diepere diagnose bekijk je de controller-logs:

kubectl logs -n <controller-namespace> deploy/<controller-deployment>

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.