Ingress packed listener, routing and TLS into one object, and anything beyond host and path became a controller-specific annotation. The Gateway API splits the job into three objects with three owners.
Three objects
kubectl get gatewayclassNAME CONTROLLER ACCEPTED AGE
traefik traefik.io/gateway-controller True 12dThe same Traefik that serves Ingress implements this class.
A Gateway and a route
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: market-gateway
spec:
gatewayClassName: traefik
listeners:
- name: web
port: 80
protocol: HTTP
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop
spec:
parentRefs:
- name: market-gateway
hostnames: ["shop.example"]
rules:
- matches:
- path:
type: PathPrefix
value: /monsters
backendRefs:
- name: market-api
port: 8080
- backendRefs:
- name: market-web
port: 80The route attaches to the Gateway through parentRefs. Its hostnames filter first; then each rule's matches (path, and in full implementations headers, methods and query params) picks backendRefs. A rule with no matches matches every path. Traffic arrives the same way as before:
curl -H "Host: shop.example" http://10.0.0.10/monstersWhat it adds
Beyond what Ingress does, routes can split traffic by weight between two backends, match on headers, and be attached across namespaces when the Gateway allows it. All of that is in the spec rather than in annotations, which is why new features go here.
Reading it
kubectl get gateway,httproute
kubectl describe httproute shopNAME CLASS ADDRESS PROGRAMMED AGE
market-gateway traefik 10.0.0.10 True 1mPROGRAMMED True means the controller accepted the Gateway and is listening. A route that points at a Gateway that does not exist, or at a Service that does not exist, shows up in describe and in the controller's 404 and 503 answers, exactly like Ingress.