Implementing Your First Kubernetes Gateway API

Implementing Your First Kubernetes Gateway API

Just about every Kubernetes environment, in some capacity, will have an application stack that needs to be accessed from the outside world (customers) or by internal engineers (e.g., a Backstage environment). In either case, ensuring proper inbound traffic from a security and routing perspective is how your containerized workloads are reached in an efficient and safe fashion.

In this blog post, you'll learn about one way to manage north/south traffic with the Kubernetes Gateway API.

Prerequisites

If you'd like to follow along from a hands-on perspective, you'll want to have:

  1. A Kubernetes cluster up and running.
  2. Emojivoto deployed (a popular demo app), which you can find here: https://github.com/digitalocean/kubernetes-sample-apps/tree/master/emojivoto-example

If you don't have a Kubernetes cluster, that's totally fine! You can still follow along and gain the knowledge you need for when you have a Kubernetes cluster to test this on.

Gateway API vs Ingress Controllers

Any time that you hear "gateway" or "ingress", it's for north/south traffic. That means it's traffic leaving your cluster or coming into your cluster. A good example of this type of traffic is if you go to google.com or amazon.com from your web browser. It's traffic that's going into, or out of your environment.

💡
The majority of apps you'll see that use some time of Ingress/Gateway are web apps. This could be a web application that you're running within your environment as a Platform Engineer or a web app that's run for customers to reach.

The important things to remember here are:

  1. Is the traffic that's coming into your cluster supposed to be? For example, if it's an internal platform built for engineers by the Platform Engineering team, should it have a public domain/IP?
  2. Is the traffic that's coming into your cluster authorized?
  3. Is the traffic reaching only the parts of the application that are necessary? For example, is there a Gateway (more on this later) set up for HTTP routing for all Namespaces? Or only the Namespace that it needs access to?

To accomplish all of this north/south traffic, you'd use an Ingress Controller (Nginx, Traefik, etc.) or the Kubernetes Gateway API.

In terms of functionality, the Kubernetes Gateway API and Ingress Controllers do the same thing. The biggest thing is, as mentioned above, the agnostic features. You aren't tied to one method of functionality when managing routing for your public-facing (or internally facing for engineers) application.

You'll read/hear many people say that Kubernetes Gateway API is like "Ingress 2.0".

How It Works

If you've ever used RBAC within Kubernetes, you know that there are ClusterRoles/Roles and ClusterRoleBindings/RoleBindings. The difference between these two are the ClusterRoles/Roles create the permissions that you want for a particular resource, and the ClusterRoleBindings/RoleBindings are what "attach" the resource to a particular permission (or a Service Account).

Another example is with storage. You have a Storage Class, which specifies the type of storage you'll use. You then create the storage with a Persistent Volume and "attach" the Volume to a particular location (e.g - a Pod) with the Persistent Volume Claim.

Kubernetes Gateway APIs work similarly in the sense of how it's all structured.

💡
Storage and RBAC in Kubernetes has nothing to do with networking. I just used it as an example to showcase how similarly, there are multiple objects/kinds used to get a Kubernetes Gateway API working.

First, you have the GatewayClass that defines a set of gateways and configures the Controller (reconciliation loop/self-healing) for the gateways that will be associated to the class. Next, you have the Gateway object/kind which defines how the gateway will work and what it'll access. For example, it'll listen on port 8080 for a Kubernetes Service that's in a Namespace called namespacea. Lastly, you have the HTTPRoute object/kind that's used to, as the name suggests, specify what backend Kubernetes Service you want the traffic to be routed to.

Now that you know about Gateways and how they work, let's create one.

The Gateway Class

The first thing you will need to do is ensure that the Kubernetes Gateway API CRDs are installed.

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

Once complete, you can create a new GatewayClass.

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

To view the GatewayClass, run the following command:

kubectl get gatewayclass

The next step is to add the Gateway.

The Gateway

Using the Gateway object/kind, you can create a new Gateway that specifies:

  1. What port the backend app should be accessed on
  2. The selector for where the application resides
💡
You could use from: All as well which would indicate the Gateway can be used by all Namespaces, but I like being able to specify to reduce the footprint.

Apply the following Gateway configuration:

kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
  name: emojivoto
spec:
  gatewayClassName: emvoto
  listeners:
  - protocol: HTTP
    port: 8080
    name: emojivoto
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            kubernetes.io/metadata.name: emojivoto

You can check that it is deployed by running the following:

kubectl get gateway

For example, if I want to see all Gateways in all Namespaces, I can run the following and see the results:

kubectl get gateway --all-namespaces

NAMESPACE     NAME        CLASS          ADDRESS         PROGRAMMED   AGE
gloo-system   emojivoto   gloo-gateway   X.X.X.X   True         6h9m

Ensure to remember the public IP address as you'll use that to access the Emojivoto UI later.

Routing

The final step is to configure how the Gateway will flow traffic through to the backend, which is a Kubernetes Service.

In this case, the HTTPRoute is specified with the backend as the web-svc, which is the primary UI service in Emojivoto. The web-svc Kubernetes Service listens on Port 80, not to be confused with the Kubernetes Gateway API that was set up to listen on port 8080.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: emojivoto
  namespace: emojivoto
spec:
  parentRefs:
    - name: emojivoto
      namespace: default
  rules:
    - backendRefs:
        - name: web-svc
          port: 80

Once the above configuration is applied, you can see it by searching for the httproute resources.

kubectl get httproute n emojivoto

NAMESPACE   NAME        HOSTNAMES   AGE
emojivoto   emojivoto               5h58m

You can now go to the Gateway's IP to see the application running.

Congrats! You've successfully set up and configured a full Kubernetes Gateway API configuration.