Ingress naar Gateway API: migreren zonder big-bang (met kind-lab)

Migreer van Ingress naar Kubernetes Gateway API zonder big-bang, met een hands-on kind-lab en praktische guidance voor ownership, ReferenceGrant, observability en rollback.

Van Ingress naar Gateway API zonder big-bang

Probleemstelling in 60 seconden

In multi-team clusters loopt “classic” Ingress vaak stuk op drie heel praktische dingen: extensibility via annotations, portability en ownership.

Ingress is bewust klein gehouden: het dekt vooral TLS termination en simpele host/path routing, en alles daarbuiten komt in de praktijk neer op controller-specifieke extensies (meestal annotations). Zie de Ingress-documentatie en de Gateway API migratiegids. Kubernetes raadt Gateway aan boven Ingress, en de Ingress API is “frozen” (wel GA/stable, maar geen verdere feature-ontwikkeling). “Frozen” betekent hier niet “deprecated” of “wordt verwijderd”, maar “stabiel contract”.

Wat je dan ziet in het wild (en wat je als platform-team mag oplossen):

  • Annotations-hell / fragmentatie: elke controller heeft zijn eigen key-value strings, vaak met semantiek die niet in de API zelf zit.
  • Ownership-issues: één Ingress object mengt infra-keuzes (entrypoints, TLS, LB specifics) met app-routing. In een gedeelde cluster betekent dat vaak: óf iedereen “kan alles”, óf teams moeten tickets schieten voor elke wijziging.
  • Vendor/controller lock-in: als je afhankelijk wordt van controller-specifieke annotations, wordt wisselen duur/risicovol.

Wanneer Ingress nog prima is (en dat mag je rustig hardop zeggen):

  • Je hebt één team dat de controller én alle Ingress resources beheert (self-service is niet het probleem).
  • Je routing is simpel (host/path + TLS) en je gebruikt weinig tot geen controller-extensies. Dat is precies het feature-gebied waarvoor Ingress bedoeld is.
  • Je wilt (voor nu) bewust niet investeren in een nieuw governance-model en je hebt geen multi-tenant druk.

Realiteit check (feb 2026): als je community ingress-nginx draait, is er een harde lifecycle-driver. Kubernetes SIG Network + SRC hebben retirement aangekondigd in deze aankondiging en later verduidelijkt in een statement/statusupdate. Dat is niet “Ingress verdwijnt”, maar wel: een internet-facing component zonder security updates is een governance- en risk-discussie die je niet wint met “we laten het nog even staan”. Check daarom altijd de actuele status en release notes voordat je productiekeuzes vastzet.

Gateway API: het minimum dat je moet snappen

De kern is niet “meer features”, maar roles + duidelijke contracten. Kubernetes noemt dat expliciet als design principle: role‑oriented (infra provider / cluster operator / app developer), portable, expressive en extensible. Ook: veel dingen die bij Ingress alleen via annotations konden, zitten nu structureel in de API.

In de praktijk raak je (voor HTTP) vooral deze vier objecten:

  • GatewayClass: cluster-scoped “welke controller beheert dit?”, vergelijkbaar met IngressClass/StorageClass; je hebt er minimaal één nodig.
  • Gateway: de “entrypoint”/infra-instance (listeners: protocol/port/hostname/TLS), vaak owned door platform of cluster operators.
  • HTTPRoute: app-level routing rules (host/path/header/query matches, filters, backends).
  • ReferenceGrant: expliciete handshake voor cross-namespace references (bijv. Route → Service in andere namespace, of Gateway → Secret in andere namespace). Zonder grant is de reference ongeldig; dit is bewust een security-safeguard.

Status conditions & events veranderen je debugging

Gateway API leunt zwaar op status conditions zodat je sneller kunt zien waarom iets niet werkt: is het object geaccepteerd door de controller, konden referenties worden resolved, is de dataplane geprogrammeerd?

De SIG Network GEP over status/conditions (GEP-1364) legt uit dat Gateway API conditions positief gepolariseerd zijn voor “happy state”: Accepted, ResolvedRefs, en Programmed (en Ready als Extended). Error-conditions zijn negatief gepolariseerd en verschijnen als ze “True” zijn. De exacte set conditions kan per controller en feature-level afwijken, maar Accepted, ResolvedRefs en Programmed zie je in de praktijk vrijwel overal terug.

Praktisch verschil met Ingress-debugging: je zit minder in “controller logs + gokken” en meer in “kubectl get/describe + status.parents + events”, en pas daarna duik je de controller in.

Hands-on lab: Gateway API in kind

Doel: in <30 minuten een draaiend lab met host-based, path-based routing én weighted traffic split (canary). Dit is copy/paste, laptop-proof.

Vereisten

  • Docker
  • kind
  • kubectl
  • Helm 3
  • curl

(Je hoeft geen LoadBalancer in kind te hebben: we testen via kubectl port-forward, net als in de Envoy Gateway quickstart.)

Controller-keuze: waarom Envoy Gateway voor dit lab?

Voor een lab wil je drie dingen: snelle installatie, duidelijke docs, ondersteuning van standard features zoals weighted backendRefs.

  • Envoy Gateway: quickstart is 1 Helm install + port-forward flow, en heeft expliciete docs voor HTTP routing en traffic splitting met weight op backendRefs.
  • Alternatieven (ook prima, maar andere install ergonomics):
    • NGINX Gateway Fabric heeft een kind-get-started met NodePort mappings in kind config + Helm install.
    • Project Contour kan Gateway API via static of dynamic provisioning; prima, maar net wat meer moving parts in een “quick demo”.

Controller-specifiek vs generiek:

  • Generiek (spec): GatewayClass/Gateway/HTTPRoute/ReferenceGrant semantics, allowedRoutes, matching rules, weight, en status conditions (Accepted/ResolvedRefs/Programmed).
  • Controller-specifiek: hoe de dataplane wordt uitgerold (Deployment/DaemonSet), hoe je een “address” krijgt in kind (LB/NodePort/port-forward), welke extra Policy CRDs bestaan, en welke conformance (Core/Extended/Implementation-specific) features precies ondersteund zijn.

Stap-voor-stap

kind cluster aanmaken

kind create cluster --name gwapi-lab
kubectl cluster-info --context kind-gwapi-lab

Envoy Gateway installeren

Volgens de quickstart installeer je tegelijk de Gateway API CRDs + Envoy Gateway via Helm, en wacht je tot de deployment Available is.

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

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

Expected output (indicatief):

kubectl -n envoy-gateway-system get deploy,po

Je wilt 1 deployment Available en pods Running.

Namespaces + sample app (2 services)

We maken een infra-namespace voor de Gateway en een app-namespace met twee Services (echo-v1 en echo-v2). De echo image komt uit het Kubernetes Gateway API demo image-pad.

kubectl apply -f - <<'YAML'
apiVersion: v1
kind: Namespace
metadata:
  name: gateway-infra
---
apiVersion: v1
kind: Namespace
metadata:
  name: team-a
  labels:
    gw-access: "true"
---
apiVersion: v1
kind: Service
metadata:
  name: echo-v1
  namespace: team-a
  labels:
    app: echo
    version: v1
spec:
  selector:
    app: echo
    version: v1
  ports:
  - name: http
    port: 3000
    targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-v1
  namespace: team-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo
      version: v1
  template:
    metadata:
      labels:
        app: echo
        version: v1
    spec:
      containers:
      - name: echo
        image: registry.k8s.io/gateway-api/echo-basic:v20251204-v1.4.1
        ports:
        - containerPort: 3000
        env:
        - name: VERSION
          value: "v1"
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
---
apiVersion: v1
kind: Service
metadata:
  name: echo-v2
  namespace: team-a
  labels:
    app: echo
    version: v2
spec:
  selector:
    app: echo
    version: v2
  ports:
  - name: http
    port: 3000
    targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-v2
  namespace: team-a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo
      version: v2
  template:
    metadata:
      labels:
        app: echo
        version: v2
    spec:
      containers:
      - name: echo
        image: registry.k8s.io/gateway-api/echo-basic:v20251204-v1.4.1
        ports:
        - containerPort: 3000
        env:
        - name: VERSION
          value: "v2"
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
YAML

Expected output / checks:

kubectl -n team-a get svc,deploy

Je ziet echo-v1, echo-v2 services en deployments.

GatewayClass + Gateway (infra-owned)

We maken een GatewayClass die wijst naar Envoy Gateway’s default controllerName gateway.envoyproxy.io/gatewayclass-controller (zie quickstart). Controleer in je cluster altijd of dit overeenkomt met de gebruikte versie.

Vervolgens maken we één Gateway met listeners voor *.example.test, die alleen Routes toestaat uit namespaces met label gw-access=true (dit is meteen een stukje governance: niet from: All). Dit allowedRoutes pattern sluit aan op het Gateway API security model.

kubectl apply -f - <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gw
  namespace: gateway-infra
spec:
  gatewayClassName: eg
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    hostname: "*.example.test"
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gw-access: "true"
YAML

Expected output / checks:

kubectl -n gateway-infra get gateway shared-gw
kubectl -n gateway-infra describe gateway shared-gw
kubectl get gatewayclass eg -o jsonpath='{.spec.controllerName}'; echo

Waar je op let:

  • Accepted=True op de Gateway (de controller heeft ’m geaccepteerd).
  • In kind zie je vaak dat de top-level Gateway condition Programmed=False is met reason AddressNotAssigned. Dat komt doordat Envoy Gateway standaard een Service type LoadBalancer aanmaakt voor de dataplane, en kind geen externe load balancer heeft, waardoor EXTERNAL-IP op <pending> blijft.
  • Dat betekent niet dat routing niet werkt. Check in dat geval:
    • status.listeners[].conditions bevat Programmed=True en Accepted=True, en/of
    • de proxy Service voor deze Gateway is aangemaakt (zie hieronder) en je kunt die port-forwarden.

(Optioneel) Proxy Service check:

kubectl -n envoy-gateway-system get svc \
  --selector=gateway.envoyproxy.io/owning-gateway-namespace=gateway-infra,gateway.envoyproxy.io/owning-gateway-name=shared-gw

Je ziet dan een Service met TYPE=LoadBalancer en een PORT(S) mapping zoals 80:<nodePort>/TCP. In kind blijft EXTERNAL-IP meestal <pending>.

HTTPRoute maken (host-based + path-based)

We maken één HTTPRoute in team-a die:

  • host-based werkt op app.example.test
  • path-based split doet:
    • /v1echo-v1
    • /v2echo-v2
kubectl apply -f - <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
  namespace: team-a
spec:
  parentRefs:
  - name: shared-gw
    namespace: gateway-infra
  hostnames:
  - "app.example.test"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1
    backendRefs:
    - name: echo-v1
      port: 3000
  - matches:
    - path:
        type: PathPrefix
        value: /v2
    backendRefs:
    - name: echo-v2
      port: 3000
YAML

Expected output / checks:

kubectl -n team-a get httproute app-route
kubectl -n team-a get httproute app-route -o yaml

In .status.parents[].conditions wil je in elk geval Accepted=True en ResolvedRefs=True zien. Dat model (Accepted/ResolvedRefs + duidelijke reasons zoals BackendNotFound) is een sterke troubleshooting-indicator. backendRefs wijzen naar een Service (niet naar een Deployment/Pod) en port is de Service port (niet targetPort).

Traffic splitting / canary (weighted backendRefs)

Weighted routing is onderdeel van Gateway API: spec.rules.backendRefs accepteert een lijst backends met relatieve weight. Envoy Gateway documenteert dit concreet in de traffic splitting docs.

We maken een tweede HTTPRoute op een ander hostname (canary.example.test) die 90/10 split doet tussen v1 en v2:

kubectl apply -f - <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: team-a
spec:
  parentRefs:
  - name: shared-gw
    namespace: gateway-infra
  hostnames:
  - "canary.example.test"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: echo-v1
      port: 3000
      weight: 90
    - name: echo-v2
      port: 3000
      weight: 10
YAML

Testen via port-forward

Zonder LoadBalancer in kind gebruik je port-forward naar de Envoy service. De quickstart laat deze selector-methode zien; voeg een fallback toe voor het geval de selector niets teruggeeft.

export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system \
  --selector=gateway.envoyproxy.io/owning-gateway-namespace=gateway-infra,gateway.envoyproxy.io/owning-gateway-name=shared-gw \
  -o jsonpath='{.items[0].metadata.name}')

kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80

Fallback als ${ENVOY_SERVICE} leeg blijft:

if [ -z "${ENVOY_SERVICE}" ]; then
  ENVOY_SERVICE=$(kubectl -n envoy-gateway-system get svc \
    -o custom-columns=NAME:.metadata.name,TYPE:.spec.type --no-headers \
    | awk '$2=="LoadBalancer" || $2=="NodePort" {print $1; exit}')
fi

if [ -z "${ENVOY_SERVICE}" ]; then
  echo "Geen geschikte Envoy Service gevonden. Controleer met:"
  echo "kubectl -n envoy-gateway-system get svc"
  exit 1
fi

Test host + path routing:

curl -s -H "Host: app.example.test" http://localhost:8888/v1 | head
curl -s -H "Host: app.example.test" http://localhost:8888/v2 | head

Je verwacht output met o.a. host, path, namespace, pod.

Voorbeeld (ingekort):

{"host":"app.example.test","path":"/v1","namespace":"team-a","pod":"echo-v1-6d7b6c8f9f-abcde"}

Test canary split (run dit een paar keer; je zult af en toe een andere backend-pod zien):

for i in $(seq 1 30); do
  curl -s -H "Host: canary.example.test" http://localhost:8888/ \
    | grep -o 'echo-v[12]-[^"]*' | head -n1
done

Feature-compatibility noot: traffic splitting via weight is spec-gedefinieerd, maar controllers verschillen in details en conformance. Check altijd de implementatie-matrix en test dit in jouw dataplane.

Delegation & governance: teams autonomie geven zonder cluster-admin

Gateway API is expliciet ontworpen rond personas/roles (infra provider / cluster operator / app developer) zodat je ownership kunt knippen zonder “self-service alles of niets”.

Ownership model: wie beheert wat

Pragmatisch model dat in veel mid/large orgs werkt:

  • Platform team (of cluster operator) beheert:
    • GatewayClass(es) (cluster-scoped)
    • Gateways in een “infra namespace” (listeners, TLS, allowedRoutes, policies)
  • Product/app teams beheren:
    • HTTPRoute(s) in hun eigen namespace(s)
    • Services/Deployments (backends)

In Gateway API zit TLS termination op de Gateway listener (dus typisch niet bij de app developer) en zijn HTTPRoutes app-owned.

Namespaces, RBAC en allowedRoutes: je eerste guardrail

Route attachment is bewust een handshake: routes referencen de Gateway via parentRefs, maar de Gateway listener bepaalt óf routes uit andere namespaces mogen attachen (allowedRoutes). Dit is het “bidirectional trust model” uit de Gateway API docs.

Veilige default bij shared Gateways:

  • allowedRoutes.namespaces.from: Selector met een selector op namespace labels
  • Gebruik bij voorkeur de standaard namespace-name label (kubernetes.io/metadata.name) of labels die alleen platform kan zetten.
  • Laat namespace-labels voor gateway-toegang uitsluitend door platform beheren (RBAC op namespaces update/patch) en voer dit via een onboarding-flow uit.

RBAC pattern (schets):

  • App team krijgt update/create op HTTPRoute in eigen namespace
  • Geen rechten op Gateways in gateway-infra
  • Platform team beheert Gateway/GatewayClass en labelt namespaces die mogen attachen (of beheert een “namespace onboarding” flow)

ReferenceGrant: cross-namespace zonder “open bar”

Cross-namespace references zijn default niet toegestaan; de owner van het doel-object moet expliciet grant geven. Dat is precies waarvoor ReferenceGrant bestaat.

Belangrijk nuancepunt uit de spec: “All cross-namespace references … require a ReferenceGrant” (route attachment naar Gateway is een aparte flow).

Praktisch voorbeeld (app team route → shared service):

apiVersion: gateway.networking.k8s.io/v1
kind: ReferenceGrant
metadata:
  name: allow-team-a-to-call-shared
  namespace: shared-services
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: team-a
  to:
  - group: ""
    kind: Service

Dit pattern staat letterlijk in het Gateway API security model.

Golden path: templates + policy checks (zonder vendor lock-in)

Wat “golden path” hier betekent: app teams leveren HTTPRoute intents aan binnen een strak, gevalideerd kader.

Concreet:

  • Templates: een standaard HTTPRoute skeleton (hostnames, parentRefs, filters die je toestaat) + standaard Service ports conventies.
  • Policy-as-code checks op commit/PR én bij admission:
    • “hostnames moeten onder team-domein vallen”
    • “parentRefs alleen naar approved Gateways”
    • “geen cross-namespace backendRefs zonder ReferenceGrant”
    • “no wildcard hostnames in app namespaces” Dit soort beperkingen kun je afdwingen met Kubernetes ValidatingAdmissionPolicy.

Anti-patterns (waar het misgaat)

  1. allowedRoutes.from: All op een shared Gateway in een multi-team cluster (je geeft implicit iedereen een attach-surface).
  2. Geen duidelijke domein-delegatie per namespace: dit vergroot kans op hostname/domain hijacking scenarios (first-come-first-served conflict resolution + later uitbreiden van hostnames door oudere routes).
  3. Cross-namespace refs “even toestaan” zonder ReferenceGrant governance: zonder safeguards ontstaan onnodige security-risico’s.

Observability & debugging

Je wil een standaard set signalen, onafhankelijk van controller, plus een paar controller-specifieke plekken.

Signalen die je gebruikt

  1. Status conditions op Gateway/GatewayClass/HTTPRoute
    • GEP-1364 definieert de richting: Accepted, ResolvedRefs, Programmed als positieve summary conditions, met error-conditions als ze relevant zijn. De exacte set kan per implementatie/feature-level verschillen.
  2. Events (kubectl describe) op Gateway/HTTPRoute: vaak direct “NotAllowed”, “BackendNotFound”, “NoMatchingListenerHostname”, etc.
  3. Controller logs (pas als status/events niet genoeg zijn)
    • In kind-setup met cloud-provider-kind: docker logs cloud-provider-kind.
    • In Envoy Gateway: logs in envoy-gateway-system (deployment/pods).
  4. Controller-specific metrics (verschilt per implementatie; check controller docs).
  5. Dataplane debugging gotcha: Envoy Gateway quickstart noemt expliciet dat privileged ports gemapt kunnen worden naar unprivileged intern; dat kan verwarrend zijn bij debug.

Checklist: route werkt niet

  1. Check dat je controller draait (kubectl -n envoy-gateway-system get po,deploy).
  2. kubectl get gatewayclass en check status/Accepted (implementations moeten status populaten).
  3. kubectl -n gateway-infra describe gateway shared-gw
    • Accepted=True? Programmed=True? Address gezet?
  4. kubectl -n team-a get httproute app-route -o yaml
    • status.parents[].conditions: Accepted, ResolvedRefs?
  5. Hostname match: HTTPRoute hostnames moeten matchen met de Gateway listener hostname (anders wordt het genegeerd).
  6. allowedRoutes match: komt je route-namespace door de selector? allowedRoutes semantics staan in de Gateway API spec reference.
  7. Backend bestaat + juiste port: BackendNotFound / ResolvedRefs=False is een typisch signaal.
  8. Cross-namespace backendRefs? Dan: bestaat de ReferenceGrant in target namespace? Zonder grant is de reference invalid.
  9. Controller-specific constraints: bij Envoy Gateway zijn backendRefs (nu) vooral Service-only; invalid backendRefs kunnen resulteren in HTTP 500 voor het traffic-deel dat naar die backend zou gaan (bij traffic splitting).
  10. Pas dan: controller logs + reconcile errors.

Veelvoorkomende failure modes

  • Hostname mismatch (Gateway listener vs HTTPRoute hostnames): route “lijkt” te bestaan maar wordt genegeerd.
  • Namespace niet toegestaan door allowedRoutes selector: Accepted=False / “NotAllowedByListeners” (semantics + security guidance).
  • Missing ReferenceGrant bij cross-namespace backendRefs: ResolvedRefs=False of controller reject; dit is by design.
  • Conflicts / hostname claim hijacking als je “te breed” delegate en geen policy-checks hebt.
  • Controller quirks: bijvoorbeeld Envoy Gateway’s privileged-port mapping.

Migratieplan zonder big-bang

De Gateway API migratiegids helpt goed met concept mapping en handmatige conversie, maar niet volledig met live migratie en controller-specifieke features. Daar maken migratie-aanpak en governance het verschil.

Hier is een plan dat werkt in echte clusters.

Parallel draaien: Ingress en Gateway naast elkaar

  • Laat je bestaande Ingress controller draaien (meestal) terwijl je Gateway API controller uitrolt in parallel.
  • Gebruik de Gateway API om nieuwe routes op te zetten, of een beperkte subset van bestaande routes.
  • Belangrijk: Ingress API verdwijnt niet uit Kubernetes (frozen maar blijft GA), dus parallel runnen is een legitieme stap.

Canary routes / beperkte hostnames

Start klein met blast radius:

  • Kies een subdomein of dedicated hostname-set, bv. gw.example.com of *.gw.example.com, en route alleen dat via Gateway.
  • Of gebruik een beperkt aantal apps/teams die adopter willen zijn.

Gateway API ondersteunt traffic splitting met weights op HTTPRoute backendRefs, wat je inzet voor canary rollouts.

Als jouw controller (nog) geen weights ondersteunt:

  • Gebruik host-based canary (canary.app.example.com) of header-based routing (als ondersteund) als alternatief, en houd het “split” mechanisme buiten de data plane (bijv. DNS weighted records). (Controller-support varieert per implementatie; check implementations/conformance.)

TLS-migratie: certs en secrets

  • Verplaats TLS-terminatie expliciet naar Gateway listeners en inventariseer welke certs nu via Ingress lopen.
  • Bepaal waar TLS Secrets beheerd worden (bijv. cert-manager of handmatig) en migreer die gecontroleerd mee.
  • Gebruik ReferenceGrant als je cross-namespace Secret-references nodig hebt; zonder grant blijft zo’n reference ongeldig.
  • Valideer na migratie minimaal: SNI/hostname match, volledige cert chain, en renewal-flow.

Hostname ownership expliciet maken

  • Reserveer domein-slices per team (bijv. team-a.example.com).
  • Forceer dit met admission/policy: hostnames binnen team-scope, geen wildcard hostnames in app-namespaces, en alleen approved parentRefs.
  • Leg conflicts afhandelregels vast (wie krijgt voorrang bij overlap) voordat teams zelfstandig routes publiceren.

Rollback plan

Rollback moet “saai” zijn:

  • Houd Ingress resources intact en “toggle” traffic terug via:
    • DNS terug naar oude entrypoint, of
    • load balancer/service switch (afhankelijk van infra), of
    • hostnames terug laten wijzen naar Ingress endpoint.
  • Gebruik dezelfde acceptance checks (zie hieronder) om rollback te valideren.

Wanneer ben je “done”

Acceptance criteria (maak dit expliciet, anders blijf je zweven):

  • Functioneel:
    • Alle externe routes zijn gemigreerd en gevalideerd (host/path/TLS/redirects/etc).
    • Geen dependency meer op controller-specifieke Ingress annotations voor core routing.
  • Governance:
    • Gateway (infra) ownership en HTTPRoute (app) ownership liggen vast, inclusief RBAC en namespace onboarding flow. Dit sluit aan op het persona-model dat Gateway API expliciet maakt.
    • allowedRoutes en (waar nodig) ReferenceGrant policies staan als guardrails.
  • Operability:
    • Runbooks: “route doet het niet” checklist, standaard dashboards/alerts op gateway dataplane, en duidelijke escalatie naar platform team.
  • Decommission:
    • Als je community ingress-nginx gebruikte: plan expliciet voor de retirement datum (maart 2026) i.v.m. security posture.

Verder lezen

Conclusie

Gateway API is vooral een operational upgrade: minder “alles-in-één Ingress object”, meer duidelijke contracten tussen infra en apps, met status-driven debugging en betere multi-team guardrails.

Samenvatting in 5 bullets:

  • Ingress is stable maar “frozen”; Kubernetes raadt Gateway aan voor nieuwe capabilities en organisatie-fit.
  • Gateway API knipt ownership: GatewayClass/Gateway infra-owned, HTTPRoute app-owned; dat matcht real-world teamstructuren.
  • allowedRoutes + ReferenceGrant geven je een echte multi-tenant security handshake (niet “open bar”).
  • Status conditions (Accepted, ResolvedRefs, Programmed) zijn je primaire debug-interface.
  • Migratie zonder big-bang: run parallel, begin met beperkte hostnames/canary weights, en maak “done” meetbaar.

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