Using HttpRoutes with the Gateway API Provider on K3S
Intro
When I installed my Headlamp on my Homelab cluster I had no way to browse to the headlamp instance without port-forwarding the my-headlamp service to my localhost. To make the homelab truly useful I needed to have my services available from static URLs that do not change, without having to keep a port forward open. To do this using the Gateway API, I use HttpRoutes.
How to expose Kubernetes services to IPs
In Kubernetes, a Service is a method for exposing a network application that is running as one or more Pods in your cluster, as described in the Kubernetes documentation (https://kubernetes.io/docs/concepts/services-networking/service/). If I had a network application running in a pod with no service attached, it would be impossible to reach that network application from anywhere else within or outside of the Kubernetes cluster.
The Kubernetes documentation describes 4 Service types in Kubernetes, but I only needed to focus on two: ClusterIP, which exposes a service on a cluster-internal IP, and LoadBalancer, which exposes a service externally using an external load balancer. Frequently the external load balancer would be provided by a cloud provider, but in the case of K3S it ships with its own load balancer called ServiceLB, as described in the K3S documentation (https://docs.k3s.io/networking/networking-services#service-load-balancer). ServiceLB uses the available host ports, which in my case means the ports on the 4 Raspberry Pi nodes running the Kubernetes cluster.

The Traefik reverse proxy that ships with K3S runs a LoadBalancer service called traefik that maps to all of the available external IPs from the nodes. Headlamp installs a ClusterIP service, called my-headlamp on my install. While I could have changed the configuration of the traefik service to free up one of the IPs and assigned it to the my-headlamp service by converting it to a LoadBalancer service, I would run out of IPs pretty quickly as I kept adding new services. The way out of this conundrum is to use an HttpRoute to connect the my-headlamp service to the external IPs exposed by the traefik service.
Using HttpRoute
Because my nodes have static names (like raspberrypi1) from my initial setup, I can actually navigate to the external IPs exposed by the traefik service by navigating to the static names in the browser, http://raspberrypi1 for instance. I want to navigate to my different services by appending a root url to the static name, so that http://raspberrypi1/headlamp/ navigates to my Headlamp service. Using an HttpRoute, I can direct traffic from that endpoint to the service.
The Gateway API Documentation describes how the HttpRoute resource works. In my case, I built the following yaml file:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-headlamp-route # unique name for the route
spec:
parentRefs:
- name: traefik-gateway # the Gateway we made previously
rules:
- matches:
- path:
type: PathPrefix
value: /headlamp # match any route through the gateway with this prefix
backendRefs:
- name: my-headlamp # direct these requests to port 80 of the my-headlamp service
port: 80
The parentRefs section declares the Gateway that the HttpRoute applies to, in this case the Gateway that I made in the previous post. In the rules section I have a rule that matches every path with the prefix /headlamp and directs the request to the my-headlamp service at port 80. I could have mapped other services in this section, but I will be making separate HttpRoute files to keep those changes easier to manage.
Setting the baseURL for Headlamp
At this point, when I navigated to http://raspberrypi1/headlamp/, I got 404 error, because there is no /headlamp/ resource at the root of the Headlamp service and the /headlamp/ prefix is still being sent through to the service. I set the baseURL for the headlamp service to /headlamp, which lets the service load correctly.
helm upgrade my-headlamp headlamp/headlamp --namespace kube-system --set config.baseURL="/headlamp"
