Asterrr's Handbook

Gateway API

GatewayClass, Gateway and HTTPRoute (gateway.networking.k8s.io/v1), listeners and allowedRoutes, path, header and method matching, filters, weighted traffic splitting, ReferenceGrant and migrating from Ingress.

Exam tasks: 3.4 (use the Gateway API to manage ingress traffic), with 3.5 when migrating from Ingress

The decision: which Gateway listener should accept this traffic, which namespaces may attach routes to it, and how should the HTTPRoute match and split requests across backends?

Three resources, three owners

ResourceScopeSays
GatewayClassCluster"Gateways of this class are run by controller X" (spec.controllerName). Usually installed with the implementation
GatewayNamespace"Listen on these ports and hostnames, with this TLS, and accept routes from these namespaces"
HTTPRouteNamespace"For these hostnames, send requests matching these rules to these Services"
  • Gateway API is CRDs plus a controller, not part of core Kubernetes. Without an implementation (Envoy Gateway, Istio, Cilium, NGINX Gateway Fabric, Contour, Traefik and others) the objects are accepted and nothing listens.
  • GatewayClass, Gateway, HTTPRoute and GRPCRoute are gateway.networking.k8s.io/v1 in the Standard channel. ReferenceGrant, TLSRoute and ListenerSet reached v1 in Gateway API 1.5, and TCPRoute and UDPRoute in 1.6.
k get crd | grep gateway.networking.k8s.io     # are the CRDs installed?
k get gatewayclass                              # ACCEPTED should be True
# install Standard channel CRDs (version from the task or the project's releases page)
k apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml

The Gateway

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: gw-system
spec:
  gatewayClassName: envoy            # must match an existing GatewayClass
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    hostname: "*.orbit.test"
    allowedRoutes:
      namespaces:
        from: Selector               # Same (default) | All | Selector
        selector:
          matchLabels:
            gateway-access: public
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.orbit.test"
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: orbit-wildcard-tls     # kubernetes.io/tls Secret in gw-system
    allowedRoutes:
      namespaces:
        from: All
  • allowedRoutes.namespaces.from defaults to Same: only routes in the Gateway's own namespace can attach. This is the usual reason an app team's route is ignored.
  • Check k -n gw-system get gateway public: PROGRAMMED True and an ADDRESS mean the data plane is up.

The HTTPRoute

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: atlas
  namespace: atlas
spec:
  parentRefs:
  - name: public
    namespace: gw-system
    sectionName: https              # attach to one listener only (optional)
  hostnames:
  - maps.orbit.test
  rules:
  - matches:
    - path:
        type: PathPrefix            # Exact | PathPrefix | RegularExpression
        value: /tiles
    backendRefs:
    - name: atlas-v1
      port: 8080
      weight: 90
    - name: atlas-v2
      port: 8080
      weight: 10
  - matches:
    - headers:
      - name: x-beta
        value: "on"
      path:
        type: PathPrefix
        value: /tiles               # same path as above, so the header match decides
    backendRefs:
    - name: atlas-v2
      port: 8080
  • Inside one matches entry, conditions are ANDed (path and header and method). Separate entries are ORed.
  • With no matches, a rule matches everything (PathPrefix /). When several rules match, the most specific wins: Exact path, then longest prefix, then more header matches, and so on.
  • weight is relative: 90 and 10 send about 10% to v2. weight: 0 sends none. Omitted means 1.
  • backendRefs.port is the Service port, and the Service must be in the route's namespace unless a ReferenceGrant allows otherwise.

Filters

FilterUse it for
RequestRedirectHTTP to HTTPS (scheme: https, statusCode: 301), host or path redirects
URLRewriteRewrite the hostname or path prefix before forwarding (type: ReplacePrefixMatch)
RequestHeaderModifier / ResponseHeaderModifierset, add or remove headers
RequestMirrorCopy traffic to a second backend, responses ignored
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

Exam signal

A redirect rule has no backendRefs. Attach that route to the http listener with sectionName: http, and attach the real route to sectionName: https. Otherwise the HTTPS listener would redirect to itself.

Cross-namespace references

  • Route to Gateway in another namespace: the Gateway's allowedRoutes must permit the route's namespace.
  • Route to a Service in another namespace, or Gateway to a Secret in another namespace: the target namespace must create a ReferenceGrant allowing it.
apiVersion: gateway.networking.k8s.io/v1   # v1beta1 before Gateway API 1.5
kind: ReferenceGrant
metadata:
  name: allow-atlas-routes
  namespace: tiles-backend          # where the Service lives
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: atlas
  to:
  - group: ""
    kind: Service

Route created, 404 everywhere

Read the route's status before anything else: k -n atlas get httproute atlas -o yaml, under status.parents[].conditions. Accepted: False with NotAllowedByListeners means allowedRoutes excludes the namespace; NoMatchingListenerHostname means the route's hostnames don't fit the listener's. ResolvedRefs: False with RefNotPermitted means a cross-namespace backend without a ReferenceGrant, and BackendNotFound means a wrong Service name or port.

Coming from Ingress

IngressGateway API
IngressClassGatewayClass
Controller's shared listenerGateway, owned and configured by you
spec.rules[].hostHTTPRoute hostnames
pathType: Prefix / ExactPathPrefix / Exact
spec.tls on each IngressTLS on the Gateway listener (once)
Controller-specific annotations (rewrites, canaries)Portable fields: filters, weight, header matches

The ingress2gateway tool converts existing Ingress objects into Gateway API resources as a starting point.

Scenarios

Scenario
Gateway shared in namespace infra has one HTTP listener with no allowedRoutes set. The team in namespace shop creates an HTTPRoute with parentRefs name shared, namespace infra. The route never receives traffic and its status shows Accepted False. What is the cause?
Scenario
You must send 20% of requests for pay.orbit.test to Service pay-canary and the rest to pay-stable, both on port 8080 in the route's namespace, without changing either Deployment. Which HTTPRoute rule does this?
Scenario
An HTTPRoute in namespace web points backendRefs at Service search in namespace data. The route is Accepted but ResolvedRefs is False with reason RefNotPermitted. What fixes it?

Drill

kubectl config use-context lab-gw

A Gateway API implementation and the GatewayClass envoy are installed. In namespace atlas, Services atlas-v1 and atlas-v2 listen on port 8080.

  1. Create Gateway public in namespace atlas with an HTTP listener on port 80 for hostname maps.orbit.test.
  2. Create HTTPRoute atlas that sends /tiles traffic 70/30 to atlas-v1 / atlas-v2, except that /tiles requests with header x-beta: on always go to atlas-v2.

Further reading

On this page