Gateway#
A Gateway resource exposes services to the outside world and is used often in production environments. From the docs:
A Gateway describes an instance of traffic handling infrastructure. It defines a network endpoint that can be used for processing traffic, i.e. filtering, balancing, splitting, etc. for backends such as a Service. For example, a Gateway may represent a cloud load balancer or an in-cluster proxy server that is configured to accept HTTP traffic.

In the diagram above, the “client” can be anything. It doesn’t live inside k8s. It might just be a web browser or a mobile app. The “Gateway-managed load balancer” can be a bit confusing, we’ll talk about it more later. For now, just know that it’s a load balancer that lives outside the cluster and routes traffic through the Gateway to a service.
Gateway is the newer alternative to Ingress which you might still come across in production systems - the classic Ingress object and its controllers are in the previous chapter.
Assignment#
The Gateway API is a spec that has different implementations. We’re gonna use the Envoy Gateway, which you can install with this command:
kubectl apply --server-side -f https://github.com/envoyproxy/gateway/releases/download/v1.5.1/install.yamlThen, create a file that we’ll call app-gatewayclass.yaml because we want to create a Gateway for the entire synergychat application, not just a specific service.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: app-gatewayclass
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controllerWe need this GatewayClass because:
- Any Gateway we create has to be associated with a GatewayClass
- The
controllerNameis set to the one created while installing the Envoy Gateway
Next, create the actual Gateway in new file named app-gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: app-gateway
spec:
gatewayClassName: app-gatewayclass
listeners:
- name: http
protocol: HTTP
port: 80Next, we need to define the routing rules for our Gateway with HTTPRoute resources. Set a rule for the web service, which we could save in a file named web-httproute.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-httproute
spec:
parentRefs:
- name: app-gateway
hostnames:
- "synchat.internal"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80… and another for the API service in api-httproute.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-httproute
spec:
parentRefs:
- name: app-gateway
hostnames:
- "synchatapi.internal"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: api-service
port: 80This says that any traffic to the synchat.internal domain name should be routed to the web-service and any traffic to synchatapi.internal domain name should be routed to the api-service.
DNS#
Now that we’ve configured the Gateway to route the domains:
synchat.internalto theweb-servicesynchatapi.internalto theapi-service
We need to configure our local machine to resolve those domains to the Gateway load balancer. We won’t be setting up global DNS so that anyone on the internet can access our app! We’ll just be configuring our local machine to resolve those domains to the Gateway load balancer.
Example#
There is a file called /etc/hosts on your local machine that is used to resolve domain names to IP addresses. We can add entries to that file to resolve our domains to the Gateway load balancer.
Open /etc/hosts in your favorite text editor and add the following lines:
127.0.0.1 synchat.internal
127.0.0.1 synchatapi.internalYou’ll probably need to provide your password to save the file. Now both synchat.internal and synchatapi.internal should resolve to the IP address 127.0.0.1 (which is just localhost).
To make sure it’s working, run:
ping synchat.internalYou should see something like:
PING synchat.internal (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.104 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.150 ms
...You can kill the ping with ctrl+c.
If you get “cannot resolve host”, then you did something wrong. Be sure to ping the synchatapi.internal domain as well.
Linux#
You may need to use the local IP address used by minikube instead of localhost in the /etc/hosts file. Find it by running:
minikube ipTunnel#
In production, once you have a Gateway configured and have pointed your domain name to it (and perhaps its load balancer), you can access your application from anywhere in the world. The trouble is, we’re using Minikube and the cluster is running on our local machine, and not only that, it’s running in an isolated virtual machine.
Fear not! Minikube has a command that will forward the Gateway to your local machine.
might need#
Open a tunnel to your cluster (You might need to enter your password):
minikube tunnel -cWe’ll be using the tunnel a lot in the rest of the course, I recommend you keep it open in a separate terminal window.
The tunnel should expose the Gateway controller’s load balancer to your local machine on 127.0.0.1:80, which we mapped to the synchat.internal and synchatapi.internal domains in the previous exercise.
While the tunnel is open, navigate to http://synchat.internal in your browser. You should see the web app! The JSON API should also be available at http://synchatapi.internal (the root of the API should return a 404, but /healthz should return a 200).
Once you’ve confirmed that you can access the web app via a web browser, answer the question on the right.
Linux users may need to specify the IP address: minikube tunnel --bind-address="127.0.0.1" -c
Gateway Types#
You may have noticed that at the top of all our resources we have this in the YAML:
apiVersion: v1This is the API version of the resource, and because those resources are core to Kubernetes, they’re in the standard v1 API group.
However, Gateway isn’t a core Kubernetes resource, it’s an extension of sorts. That’s why it has:
apiVersion: gateway.networking.k8s.io/v1You can think of the networking.k8s.io API group as a core extension. It’s not third-party (it’s on k8s.io for heaven’s sake), but it’s not part of the core Kubernetes API either.
Annotations#
The core Kubernetes API is intentionally kept small, but there are a lot of things that people want to do with Kubernetes that aren’t part of the core API. So, instead of adding a bunch of new fields to the core API, Kubernetes allows you to add arbitrary annotations to your resources, and then various extensions can read those annotations and do things with them.
For example, the Boot.dev Kubernetes cluster uses a Gateway extension specific to Google Cloud Platform. We use the following annotation so that our controller knows which SSL certificate to use:
annotations:
networking.gke.io/certmap: certmap-name-hereIf you’re curious about the specifics, the docs are here. In a nutshell, however, the important take-away is that in most production deployments you’ll be using annotations specific to the cloud provider you’re using. Each major cloud provider has their own products, so you need to use k8s annotations and extensions specific to that cloud provider.
Now that you understand the basic concepts of Gateway, in the future, it’s just a matter of following the documentation for your cloud provider to get it set up.
TLS is the one thing deliberately left off this HTTP-only Gateway. Making it real - a
Certificate, a CA chain,certificateRefson the listeners - is the cert-manager chapter in the v2 notes.
Chat#
Now that everything is accessible via Gateway (at least while the tunnel is open), let’s connect the web application front-end to the API.
example#
- Create a new ConfigMap for the web service. Add two new environment variables:
- WEB_PORT:
8080(this was already the default, now we’re just making it explicit) - API_URL:
http://synchatapi.internal
- WEB_PORT:
- Update the web application’s deployment to use the new ConfigMap.
Once all that’s applied to your cluster, you should be able to open the web application and actually use the chat interface (notice it’s “synchat”, not “synchatapi”):
http://synchat.internalMake sure that you can enter a username and a message, and send a message as that user! Assuming it works, that’s because the webpage is now sending messages via fetch requests to the api service. The api service saves those messages locally in memory.
To see what I mean, try the following:
- Create a few messages as different users.
- Refresh the page
- The messages should still be there because they’re saved in the server’s memory.
- Now, delete the
apipod - Once k8s replaces the deleted pod with a new one, refresh the page again. for more namespaces