Kubernetes Gateway API: van Ingress-opvolger tot productierouting

Gateway API is de officiële Kubernetes-opvolger van de Ingress API. Het scheidt infrastructuur van applicatierouting, ondersteunt geavanceerd verkeersmanagement zonder annotations, en is sinds Kubernetes 1.29 GA. Deze tutorial loopt door het resourcemodel, het opzetten van Envoy Gateway, het configureren van HTTPRoutes met path matching en traffic splitting, TLS-terminatie met cert-manager, en het koppelen van het rollenmodel aan je team's RBAC.

Inhoudsopgave

Leerdoel

Aan het einde van deze tutorial heb je een werkende Gateway API-setup op een Kubernetes-cluster, met Envoy Gateway als data plane, HTTPRoutes die path-based en header-based routing afhandelen, TLS-certificaten beheerd door cert-manager, en RBAC-rollen waarmee platform-engineers de Gateway beheren terwijl applicatieontwikkelaars zelfstandig hun routes aanpassen.

Vereisten

  • Een Kubernetes-cluster met v1.29 of nieuwer. Gateway API bereikte GA-status in v1.0, gelijktijdig met Kubernetes 1.29. Oudere clusters missen de benodigde CRD-ondersteuning.
  • kubectl geconfigureerd met cluster-admin-rechten (nodig voor CRD-installatie en de eerste Gateway-configuratie)
  • Helm 3.x lokaal geïnstalleerd
  • Minimaal één applicatie achter een ClusterIP Service. Gateway API routeert verkeer naar Services, niet direct naar pods. Het gelinkte artikel behandelt Service-types en wanneer je welke gebruikt.
  • DNS-records voor je domein(en) die je na de setup naar het externe IP van de Gateway kunt laten wijzen
  • Bekendheid met het Kubernetes Ingress-model is handig maar niet vereist. De secties hieronder leggen uit waar Gateway API afwijkt.

Waarom Gateway API Ingress vervangt

De Ingress API was ontworpen voor één ding: simpele HTTP host- en path-routing. Alles daarbuiten (rewrites, rate limiting, timeouts, canary-deployments) werd doorgeschoven naar controller-specifieke annotations. ingress-nginx verzamelde er meer dan 100. Die annotations zijn ongevalideerde strings. Een typfout faalt stilletjes. Overstappen van ingress-nginx naar Traefik betekent alle annotations opnieuw schrijven.

Drie problemen maken het erger:

  1. Geen protocolondersteuning buiten HTTP/HTTPS. gRPC, TCP-passthrough en WebSocket-upgrades vereisen annotation-hacks of apart gereedschap. Er is geen first-class API voor niet-HTTP-protocollen.
  2. Geen rolscheiding. Ingress combineert infrastructuurzaken (welke load balancer, welke poorten, welke TLS-certificaten) en applicatiezaken (welke paden naar welke Services) in één resource. In multi-tenant-clusters betekent dat ofwel te brede RBAC ofwel tickets heen en weer tussen platform- en appteams.
  3. De ingress-nginx-pensionering. Op 24 maart 2026 werd de kubernetes/ingress-nginx-repository gearchiveerd als read-only. Geen securitypatches meer, geen bugfixes. Google's Open Source Blog bevestigde de transitie weg van ingress-nginx.

Gateway API lost alle drie op. Routingfeatures zoals header matching, traffic splitting en request mirroring zitten in de spec, niet in annotations. Getypeerde resources met schema's betekenen dat kubectl apply --dry-run=server je config daadwerkelijk valideert. En het resourcemodel is van meet af aan ontworpen rond organisatorische rollen.

Wel even een verduidelijking: de Ingress API zelf is niet deprecated. Alleen de nginx-gebaseerde referentie-implementatie is stopgezet. Onderhouden controllers als Traefik en HAProxy ondersteunen Ingress-resources gewoon nog. Maar de richting is duidelijk.

Het resourcemodel: GatewayClass, Gateway, HTTPRoute

Gateway API splitst routing in drie lagen. Elke laag hoort bij een ander team of rol:

GatewayClass  (cluster-scoped, eigendom van infrastructuurprovider)
    └── Gateway  (namespaced, eigendom van platformteam)
            └── HTTPRoute / GRPCRoute  (namespaced, eigendom van appontwikkelaars)
                    └── Service  (backend)

GatewayClass

Een cluster-scoped resource, vergelijkbaar met StorageClass. Het vertegenwoordigt een categorie gateway-implementaties, geen specifiek exemplaar. Elke implementatie registreert een unieke controllerName:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

Veelgebruikte controllernames per april 2026:

Implementatie controllerName
Envoy Gateway gateway.envoyproxy.io/gatewayclass-controller
Istio istio.io/gateway-controller
Cilium io.cilium/gateway-controller
NGINX Gateway Fabric gateway.nginx.org/nginx-gateway-controller

Je maakt GatewayClass doorgaans niet zelf aan. De Helm-chart van je gekozen implementatie creëert hem bij installatie. Controleer het met kubectl get gatewayclass na het installeren van de controller.

Gateway

Een namespaced resource die een daadwerkelijke load balancer of proxy vertegenwoordigt. Het verwijst naar een GatewayClass en definieert een of meer listeners met protocol, poort, optionele hostname en TLS-configuratie:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: infra
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.staging.infra.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: wildcard-tls

Standaard accepteert een Gateway alleen Routes uit zijn eigen namespace. Om Routes uit andere namespaces toe te staan, configureer je allowedRoutes:

listeners:
- name: http
  protocol: HTTP
  port: 80
  allowedRoutes:
    namespaces:
      from: Selector
      selector:
        matchLabels:
          gateway-access: "true"    # label op toegestane namespaces

HTTPRoute

De resource die applicatieontwikkelaars het vaakst aanmaken. Je koppelt hem aan een Gateway via parentRefs en definieert routingregels:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: app-team
spec:
  parentRefs:
  - name: prod-gateway
    namespace: infra
  hostnames:
  - "api.staging.infra.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1
    backendRefs:
    - name: api-svc
      port: 8080

Route-Gateway-binding is bidirectioneel: de Route moet via parentRefs naar de Gateway verwijzen, EN de Gateway moet de Route toestaan via allowedRoutes. Beide kanten moeten toestemmen. Dat is een beveiligingseigenschap, geen bijzaak.

Checkpoint. Je snapt nu waarom er drie resources bestaan in plaats van één. GatewayClass definieert de implementatie, Gateway definieert de infrastructuur (listeners, poorten, TLS), HTTPRoute definieert de applicatierouting. Elk past bij een ander RBAC-niveau.

Gateway API opzetten met Envoy Gateway

Deze sectie gebruikt Envoy Gateway als implementatie. Envoy Gateway is een CNCF-subproject van het Envoy Proxy-project, bereikte GA in maart 2024, en slaagt voor volledige v1.4.0-conformance. Als je liever een andere implementatie gebruikt (Cilium, Istio, NGINX Gateway Fabric): de Gateway- en HTTPRoute-manifests blijven hetzelfde. Alleen de installatiestap verschilt.

Stap 1: installeer de Gateway API CRDs

Gateway API CRDs zitten niet standaard in Kubernetes. Installeer de Standard Channel CRDs (de productiestabiele set):

kubectl apply --server-side \
  -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml

De --server-side-vlag is vereist vanwege de CRD-grootte. Zonder deze vlag overschrijden de annotations de client-side apply-limiet.

Controleer:

kubectl get crd | grep gateway.networking.k8s.io

Verwachte output (minimaal vier CRDs):

gatewayclasses.gateway.networking.k8s.io      2026-04-09T10:00:00Z
gateways.gateway.networking.k8s.io             2026-04-09T10:00:00Z
httproutes.gateway.networking.k8s.io           2026-04-09T10:00:00Z
referencegrants.gateway.networking.k8s.io      2026-04-09T10:00:00Z

Stap 2: installeer Envoy Gateway

helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.7.1 \
  -n envoy-gateway-system \
  --create-namespace

Wacht tot de deployment klaar is:

kubectl wait --timeout=5m \
  -n envoy-gateway-system \
  deployment/envoy-gateway \
  --for=condition=Available

Verwachte output:

deployment.apps/envoy-gateway condition met

Controleer of de GatewayClass is aangemaakt:

kubectl get gatewayclass
NAME            CONTROLLER                                      ACCEPTED
envoy-gateway   gateway.envoyproxy.io/gatewayclass-controller   True

ACCEPTED: True betekent dat Envoy Gateway de GatewayClass heeft gevalideerd en klaar is om Gateways te provisioneren.

Stap 3: maak een Gateway aan

# gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: infra
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All    # sta Routes uit alle namespaces toe (verscherp dit in productie)
kubectl create namespace infra
kubectl apply -f gateway.yaml

Controleer de Gateway-status:

kubectl get gateway -n infra
NAME           CLASS           ADDRESS         PROGRAMMED   AGE
prod-gateway   envoy-gateway   203.0.113.42    True         30s

PROGRAMMED: True plus een ADDRESS-waarde betekent dat het data plane klaar is om verkeer te ontvangen. Noteer het externe IP; daar richt je je DNS-records op.

Stap 4: maak een HTTPRoute aan

Stel dat je een Service genaamd frontend hebt in namespace default op poort 80:

# frontend-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: frontend-route
  namespace: default
spec:
  parentRefs:
  - name: prod-gateway
    namespace: infra
  hostnames:
  - "app.staging.infra.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: frontend
      port: 80
kubectl apply -f frontend-route.yaml

Stap 5: verifieer end-to-end

export GATEWAY_IP=$(kubectl get gateway prod-gateway -n infra \
  -o jsonpath='{.status.addresses[0].value}')

curl -H "Host: app.staging.infra.example.com" http://$GATEWAY_IP/

Je zou de response van je frontend Service moeten zien. Krijg je een 404, controleer dan of de parentRefs-namespace van de HTTPRoute overeenkomt met de namespace van de Gateway, en of de allowedRoutes van de Gateway de namespace van de HTTPRoute toestaat.

Checkpoint. Je hebt nu een werkende Gateway API-setup: CRDs geïnstalleerd, Envoy Gateway draaiend, een Gateway met een listener op poort 80, en een HTTPRoute die verkeer doorstuurt naar een backend Service.

HTTPRoute configureren: matching, filters en traffic splitting

HTTPRoute ondersteunt vier matchtypes, combineerbaar met AND-logica binnen één matchblok:

Path en header matching

rules:
- matches:
  - path:
      type: PathPrefix
      value: /api/v2
    headers:
    - type: Exact
      name: x-env
      value: canary
  backendRefs:
  - name: api-v2-canary
    port: 8080

Deze regel matcht alleen requests waarbij het pad begint met /api/v2 EN de header x-env gelijk is aan canary. Beide voorwaarden moeten waar zijn.

Meerdere rules binnen een HTTPRoute werken met OR-logica: als één rule matcht, wordt die toegepast.

Requestfilters

Filters wijzigen requests of responses inline. Geen annotations nodig.

HTTP-naar-HTTPS-redirect:

rules:
- filters:
  - type: RequestRedirect
    requestRedirect:
      scheme: https
      statusCode: 301

URL-rewrite (strip een padprefix voordat je doorstuurt):

rules:
- matches:
  - path:
      type: PathPrefix
      value: /legacy-app
  filters:
  - type: URLRewrite
    urlRewrite:
      path:
        type: ReplacePrefixMatch
        replacePrefixMatch: /
  backendRefs:
  - name: legacy-svc
    port: 8080

Requests naar /legacy-app/dashboard komen bij de backend aan als /dashboard.

Headermanipulatie:

filters:
- type: RequestHeaderModifier
  requestHeaderModifier:
    add:
    - name: x-request-source
      value: "gateway"
    remove:
    - x-internal-only

Eén beperking: URLRewrite en RequestRedirect kunnen niet samenleven in dezelfde rule. Gebruik aparte rules als je beide nodig hebt.

Traffic splitting

Het weight-veld op backendRefs is een proportionele verhouding, geen percentage. Weights staan standaard op 1 als ze niet zijn ingevuld.

Een 90/10 canary-split:

rules:
- backendRefs:
  - name: app-v1
    port: 8080
    weight: 90
  - name: app-v2
    port: 8080
    weight: 10

Om de rollout af te ronden, zet je het weight van v1 op 0:

rules:
- backendRefs:
  - name: app-v1
    port: 8080
    weight: 0
  - name: app-v2
    port: 8080
    weight: 1

Vergelijk dit met Ingress, waar canary-deployments controller-specifieke annotations vereisten zoals nginx.ingress.kubernetes.io/canary-weight. Hier zit het gewoon in de spec.

Timeouts (GA sinds v1.2)

rules:
- matches:
  - path:
      type: PathPrefix
      value: /reports
  timeouts:
    request: 60s          # totale client-naar-client timeout
    backendRequest: 30s   # gateway-naar-backend timeout
  backendRefs:
  - name: reports-svc
    port: 8080

backendRequest moet kleiner dan of gelijk aan request zijn als je beide instelt. Waardes gebruiken Go-durationstrings (300ms, 30s, 5m). De timeout-feature bereikte GA-status in Gateway API v1.2 (november 2024).

Checkpoint. Je kunt nu path matching, header matching, requestfilters, traffic splitting en timeouts configureren, allemaal in getypeerde YAML in plaats van annotationstrings.

TLS-terminatie en cert-manager-integratie

Een HTTPS-listener toevoegen

TLS wordt geconfigureerd op Gateway-niveau, niet op de HTTPRoute. Voeg een HTTPS-listener toe naast de bestaande HTTP-listener:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: infra
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "app.staging.infra.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: app-staging-tls    # cert-manager maakt deze aan
    allowedRoutes:
      namespaces:
        from: All

De cert-manager.io/cluster-issuer-annotation vertelt cert-manager om automatisch certificaten uit te geven voor elke HTTPS-listener met een niet-lege hostname en tls.mode: Terminate.

cert-manager-vereisten

cert-manager moet geïnstalleerd zijn met Gateway API-ondersteuning ingeschakeld:

helm upgrade --install cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set config.enableGatewayAPI=true

Gateway API CRDs moeten geïnstalleerd zijn voordat cert-manager opstart. Als je ze in stap 1 van deze tutorial hebt geïnstalleerd, zit je goed. Was cert-manager eerst geïnstalleerd, herstart dan de pods na het installeren van de CRDs.

Het certificaat verifiëren

Na het toepassen van de geannoteerde Gateway detecteert cert-manager de HTTPS-listener, maakt een Certificate-resource aan en geeft het certificaat uit via je ClusterIssuer:

kubectl get certificate -n infra
NAME               READY   SECRET             AGE
app-staging-tls    True    app-staging-tls    45s

READY: True betekent dat het certificaat is uitgegeven en de Secret bestaat. Blijft het op False staan, controleer dan:

kubectl describe certificate app-staging-tls -n infra
kubectl get challenges -n infra
kubectl logs -n cert-manager deploy/cert-manager

HTTP-naar-HTTPS-redirect

Maak een HTTPRoute die alleen aan de HTTP-listener koppelt en alles naar HTTPS redirect:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: http-redirect
  namespace: infra
spec:
  parentRefs:
  - name: prod-gateway
    sectionName: http    # koppel alleen aan de HTTP-listener
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

Het sectionName-veld richt zich op een specifieke listener op naam. Zonder dit veld zou de route aan alle listeners koppelen en een redirect-loop veroorzaken op de HTTPS-listener.

Verifieer de volledige keten:

curl -v http://app.staging.infra.example.com/
# Verwacht: 301 redirect naar https://...

curl -v https://app.staging.infra.example.com/
# Verwacht: TLS-handshake met geldig certificaat, response van frontend

Checkpoint. TLS wordt nu afgehandeld op Gateway-niveau met automatische cert-manager-integratie. Je HTTPRoutes hebben geen TLS-configuratie nodig; ze routeren gewoon verkeer dat de Gateway al heeft ontsleuteld.

Rolscheiding en RBAC

Het drielagenmodel van Gateway API past direct op Kubernetes RBAC. Hier zit de echte organisatorische waarde, zeker in multi-tenant-clusters.

Platformbeheerder: beheert GatewayClass en Gateway

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: gateway-admin
rules:
- apiGroups: ["gateway.networking.k8s.io"]
  resources: ["gatewayclasses", "gateways"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Het platformteam bepaalt welke implementatie draait (GatewayClass), welke poorten open zijn (Gateway-listeners) en welke namespaces Routes mogen koppelen (allowedRoutes). Applicatieteams raken deze resources niet aan.

Applicatieontwikkelaar: beheert HTTPRoute

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: route-developer
  namespace: app-team
rules:
- apiGroups: ["gateway.networking.k8s.io"]
  resources: ["httproutes", "grpcroutes"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Een ontwikkelaar kan een nieuwe canary-route uitrollen, een path match toevoegen of traffic weights aanpassen zonder cluster-admin-rechten. Ze kunnen de listeners, poorten of TLS-configuratie van de Gateway niet wijzigen.

Goed om te weten: in Kubernetes 1.33 bevat de ingebouwde admin ClusterRole geen permissies voor Gateway API-resources. Je moet deze Roles expliciet aanmaken. Die issue volgt het probleem.

Cross-namespace trust met ReferenceGrant

Wanneer een HTTPRoute in namespace app-team moet verwijzen naar een Service of Secret in namespace infra, moet een ReferenceGrant in de doelnamespace dat expliciet toestaan:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-routes-to-infra
  namespace: infra
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: app-team
  to:
  - group: ""
    kind: Service

Het verwijderen van de ReferenceGrant trekt de toegang onmiddellijk in. Houd grants klein: specificeer zowel de bronnamespace als het doelresourcetype.

Voor een breder overzicht van Kubernetes RBAC-patronen behandelt de RBAC-gids rolontwerp, ClusterRole-aggregatie en veelgemaakte fouten.

Wat je geleerd hebt

Deze tutorial behandelde:

  • Waarom Gateway API bestaat: de annotationwildgroei van Ingress, protocollimitaties en ontbrekende rolscheiding vormden de aanleiding voor een getypeerde, meerlaagse routing-API
  • Het resourcemodel: GatewayClass (implementatie), Gateway (infrastructuur), HTTPRoute (applicatierouting), elk op een ander RBAC-niveau
  • Hands-on setup: CRDs installeren, Envoy Gateway deployen, een Gateway met listeners aanmaken en een HTTPRoute koppelen
  • Geavanceerde routing: path matching, header matching, requestfilters, URL-rewrites, traffic splitting met gewogen backends en per-route timeouts
  • TLS: HTTPS-listeners met automatische cert-manager-certificaatuitgifte en HTTP-naar-HTTPS-redirect via sectionName-targeting
  • RBAC: aparte ClusterRoles voor platformbeheerders en namespace Roles voor ontwikkelaars, met ReferenceGrant voor cross-namespace trust

Als je een bestaande ingress-nginx-setup migreert, behandelt de ingress-nginx naar Gateway API-migratiegids de volledige conversie met ingress2gateway, parallelle operatie en zero-downtime DNS-cutover. Voor een diepere blik op Ingress-concepten behandelt de Ingress-configuratiegids host-based routing, pathType en controllerselectie.

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.