Gateway API – Part 2 – API Resources

Having introduced the Gateway API in part one of this series, we will now explore some Gateway API types and field specifications, and how they all fit together.

Installing the Gateway API Addon

You might be wondering, “Why don’t I see Gateway API resources on my Kubernetes cluster?” or “Which Kubernetes cluster version includes the Gateway API?” The answer is — you will need to install the Gateway API custom resource definitions (CRDs) yourself!

Although the Gateway API provides external traffic routing similar to Kubernetes’ built-in Ingress, its scope is much broader and is maintained separately from the core Kubernetes project. This allows the Gateway API project to be developed independently of the Kubernetes version. For this reason, Gateway API is classified as a Kubernetes addon and can be installed by following the official guide.

# example installation command
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml

Note that there are different “channels” (flavors) of Gateway API installations: standard and experimental. Which version to choose will depend on your needs. For production use, I would recommend the standard channel.

Another alternative is install a Gateway API controller of your choice, since many controllers also bundle the Gateway API CRDs in the same software package.

Basic Building Blocks

GatewayClass

The first resource we will talk about is the GatewayClass. This resource is an abstraction of a Gateway API controller. A Gateway resource specifies a GatewayClass to use. This allows multiple Gateway API controllers to run on a single cluster. If you’ve been working with Kubernetes for a while, you’re likely familiar with StorageClass and IngressClass, which follow a similar concept.

The following GatewayClass example defines a Gateway API controller implemented with the HAProxy Ingress controller:

   apiVersion: gateway.networking.k8s.io/v1alpha2
   kind: GatewayClass
   metadata:
     namespace: default
     name: haproxy-ingress-gatewayclass
   spec:
     controllerName: haproxy.org/gateway-controller

A GatewayClass implemented with the Traefik Proxy would look like this:

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

Gateway

Once a GatewayClass resource is created, we can proceed to create a Gateway, which is the foundational object upon which all other components are built. It usually represents a load balancer or the mechanism through which incoming traffic enters the Kubernetes cluster.

Here is an example referencing the Traefik Proxy GatewayClass we defined earlier:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: traefik
  namespace: default
spec:
  gatewayClassName: traefik
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - name: secret-tls
            namespace: default
      allowedRoutes:
        namespaces:
          from: All
    - name: tcp
      protocol: TCP
      port: 3000
      allowedRoutes:
        namespaces:
          from: Same

This example shows just few of the many configuration options available. Note, however, that unlike an Ingress resources, no routes are defined in a Gateway. Routes are defined using a separate route resource, such as HTTPRoute or TCPRoute.

In the example above, the listeners entries define an (optional) allowedRoutes, which defines which namespaces are allowed to attach routes to the Gateway. Some listeners allow routes from any namespace (“All“) while others require the route to be in the same namespace as the Gateway (“Same“).

HTTPRoute

An HTTPRoute, as you might have guessed, describes the routing for requests to backend services. The are other route types, including TCPRoute, TLSRoute and UDPRoute.

An example route associated with a Traefik Proxy Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-http
  namespace: default
spec:
  parentRefs:
    - name: traefik
      sectionName: http
      kind: Gateway
  hostnames:
    - example.com
  rules:
     - matches:
        - path:
            type: PathPrefix
            value: /
       backendRefs:
        - name: example-app
          namespace: default
          port: 80

A Route resource can reference the parent Gateway using the ParentRefs attribute, or other methods such as using label selectors with the allowedRoutes attribute on the gateway. A route will only be attached to the Gateway, however, if it is allowed — the default behavior requiring the route and gateway to be in the same namespace.

A Route can also specify which listener to associate the route with by referring to the name (with sectionName) or port (with port):

# refer to a listener by name
spec:
  parentRefs:
    - name: traefik
      sectionName: http
      kind: Gateway

# refer to a listener by port
spec:
  parentRefs:
    - name: traefik
      port: 80
      kind: Gateway

An HTTPRoute can match against a set of hostnames and are matched before any routes. Optionally, hostnames can also be defined for a listener in the Gateway resource. However, if they are defined both in an HTTPRoute and Gateway, you will need to make sure that both hostname definitions match for the route to work correctly. See the official HTTPRoute spec for more details.

Advanced Features

I decided to call this section “Advanced Features” because now we’ll be exploring some of capabilities that extend beyond those offered by the Ingress resource.

Request Header Matching

A very useful feature is the ability of an HTTPRoute is to route traffic to backends based on request headers. This feature can be used to perform a canary release, sending some traffic to a separate backed if a certain request header is matched.

In this example, if the header canary has the value “yes”, it will be matched first and routed to the app-svc-canary backend. All other traffic will be directed to the app-svc default backend:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "bar.example.com"
  rules:
  - matches:
    - headers:
      - type: Exact
        name: canary
        value: yes
    backendRefs:
    - name: app-svc-canary
      port: 8080
  - backendRefs:
    - name: app-svc
      port: 8080

Redirects and Rewrites

Redirects are very commonly used for redirecting traffic from HTTP to HTTPS. In an HTTPRoute, filters are used to configure the redirect status code:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: http-filter-redirect
spec:
  parentRefs:
  - name: redirect-gateway
    sectionName: http
  hostnames:
  - redirect.example
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

For this redirect to work, you will need to have a Gateway with listeners on HTTP (port 80) and HTTPS (port 443).

It is also possible to redirect entire paths or parts of a path, also using the filter attribute.

TLS / HTTPS Handling

With Gateway API, TLS configuration of downstream (connection to client) and upstream connections (to backend) are managed independently. You choose from various strategies:

  • Pass encypted traffic to the backend (TLS passthrough)
  • Terminate HTTPS traffic at the Gateway
  • Terminate HTTPS and then re-encrypted upstream traffic at the Gateway

More examples can be found on the official Gateway API docs

Traffic Splitting

This is a feature I really wish existed in the Ingress API. To implement weighted traffic with the AWS Load Balancer Controller, you needed to use an ugly annotation syntax. This is a prime example of why using annotations to extend functionality breaks portability and is burdensome for the user.

A typical Ingress with weighted target groups with the AWS Load Balancer Controller using annotations:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: default
  name: ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/actions.forward-multiple-tg: >
      {"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"service-1","servicePort":"http","weight":20},{"serviceName":"service-2","servicePort":80,"weight":20},{"targetGroupARN":"arn-of-your-non-k8s-target-group","weight":60}],"targetGroupStickinessConfig":{"enabled":true,"durationSeconds":200}}}
...

I don’t know about you, but that’s very hard to read. No thanks!

This is how weighted backend traffic looks like in Gateway API:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: simple-split
spec:
  rules:
  - backendRefs:
    - name: foo-v1
      port: 8080
      weight: 80
    - name: foo-v2
      port: 8080
      weight: 20

Much cleaner, and vendor agnostic.

Conclusion

These are just some of the basic types and features available with the Gateway API. It’s impossible to cover all the features in one article. You can find more guides, examples and API documentation on the official gateway api docs.

The extensibility of the API almost guarantees there will be some vendor-specific additions. It will be very interesting to see more real use-cases and software developer around the Gateway API in the future.

For now, I hope this article demystifies the Gateway API and helps you get started experimenting with it.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top