Traefik Gateway API for Kubernetes
Делаю:
2026.02.06
https://www.youtube.com/watch?v=MN-k29g97ik
Install a Gateway API controller
To use the Gateway API features in Kubernetes, you need a controller that implements the above CRDs. </br> In this introduction guide I will use an existing Gateway API controller called Traefik. </br>
Note: Keep in mind that this introduction has no dependency on Traefik specifically, therefore any controller that supports Gateway API can be used. At the bottom of this guide, I will provide guides on each of the popular Gateway API implementations.
A Basic Gateway API controller install:
$ CHART_VERSION="39.0.0" # traefik version v3.6.7
$ helm repo add traefik https://helm.traefik.io/traefik
$ helm repo update
$ helm search repo traefik --versions
$ cd ~/tmp
$ cat > gateway-api-values.yaml << EOF
logs:
access:
enabled: true
ports:
web:
port: 80
websecure:
port: 443
exposedPort: 443
# we enable gateway-api features
providers:
kubernetesGateway:
enabled: true
experimentalChannel: false
# we disable gateway-api defaults because we want to provide our own
gatewayClass:
enabled: false
# we disable gateway-api defaults because we want to provide our own
gateway:
enabled: false
EOF
$ helm install traefik traefik/traefik \
--namespace traefik \
--create-namespace \
--version $CHART_VERSION \
--values gateway-api-values.yaml
As we don’t have a LoadBalancer service in kind, let’s port-forward so we can pretend we have one
# check the pods
$ kubectl -n traefik get pods
# check the logs
$ kubectl -n traefik logs -l app.kubernetes.io/instance=traefik-traefik
Install a Gateway Class
To start enabling traffic to our newly created apps, we will start with installing a Gateway Class. </br>
Note that we use a Traefik Class in our example. </br>
GatewayClass is a cluster-scoped resource defined by the infrastructure provider. This resource represents a class of Gateways that can be instantiated. </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: traefik
spec:
controllerName: traefik.io/gateway-controller
EOF
# check
$ kubectl get gatewayclass
# describe
$ kubectl describe gatewayclass
Install a Gateway
Next we need to install a Gateway that implements our Gateway Class </br> Note that we use a Traefik Gateway in our example. </br>
This gateway lives in the same namespace as the routes and applications
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-api
namespace: default
spec:
gatewayClassName: traefik
# Only Routes from the same namespace are allowed.
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same #or All or Selector
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: secret-tls
namespace: default
allowedRoutes:
namespaces:
from: Same
EOF
$ kubectl describe gateway gateway-api
***
Error while retrieving certificate: getting secret: secret "secret-tls" not found
***
Traffic Management Features: HTTP Routes
The important fields on HTTP Route we will cover:
parentRefssectionNamehostnamesrulesmatchesfilters
Feature Table:
| Feature | Example |
|---|---|
| Route by Hostname | example |
| Route by Path | example |
| Route using URL Rewrite | example |
| Header Modification | example |
| HTTPS & TLS | example |
For traffic management, we can take a look at some basic HTTP routes.</br>
# port forward for access
$ kubectl -n traefik port-forward svc/traefik 8080:80
Route by Hostname
We can route by host. </br>
This will route all traffic that matches the Host header with the hostnames field: </br>
http://example-app-python.com/ 👉🏽 http://python-svc:5000 </br> http://example-app-go.com/ 👉🏽 http://go-svc:5000 </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: python-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http #allows us to bind to a specific listener in the Gateway
kind: Gateway
hostnames:
- example-app-python.com
rules:
- backendRefs:
- name: python-svc
port: 5000
weight: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app-go.com
rules:
- backendRefs:
- name: go-svc
port: 5000
weight: 1
EOF
// Ok!
http://example-app-python.com:8080/
http://example-app-go.com:8080/
Route by Path
We can also route by host and path with different matching strategies. </br>
Exact: </br> http://example-app-python.com/ 👉🏽 http://python-svc:5000/ </br> http://example-app-go.com/ 👉🏽 http://go-svc:5000/ </br>
PathPrefix: </br> http://example-app-python.com/_ 👉🏽 http://python-svc:5000/_ </br> http://example-app-go.com/_ 👉🏽 http://go-svc:5000/_ </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: python-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app-python.com
rules:
- matches:
- path:
type: Exact # matches the exact path, only allows to visit /
value: /
backendRefs:
- name: python-svc
port: 5000
weight: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app-go.com
rules:
- matches:
- path:
type: Exact # matches the exact path, only allows to visit /
#type: PathPrefix # prefix match, allows to visit /* Passes entire URL to upstream
value: /
backendRefs:
- name: go-svc
port: 5000
weight: 1
EOF
Route using URL Rewrite
We can rewrite the hostname or URL using URL rewrite. </br> This way, we can combine our services under one domain and our controller can act as a true API gateway:
http://example-app.com/api/python 👉🏽 http://python-svc:5000/ </br> http://example-app.com/api/go 👉🏽 http://go-svc:5000/ </br> As well as: </br> http://example-app.com/api/go/status 👉🏽 http://go-svc:5000/status </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: python-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/python
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: python-svc
port: 5000
weight: 1
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/go
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: go-svc
port: 5000
weight: 1
EOF
Request\Response Header Manipulation
With Gateway API, you can modify request and response headers. </br>
This is possible with the ResponseHeaderModifier filter </br>
At the time of this recording, Gateway API does not natively support CORS. </br> Even with it in the Experimental channel, many controllers do not support it yet. </br>
Let’s do a basic CORS header modification for our Go HTTPRoute </br>
$ kubectl port-forward svc/web-app 8000:80
The Header modification:
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/go
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: X-Custom-Header
value: "CustomHeaderValue"
- name: Access-Control-Allow-Origin
value: "http://localhost:8000"
- name: Access-Control-Allow-Methods
value: "GET, POST, PUT, DELETE, OPTIONS"
- name: Access-Control-Allow-Headers
value: "Content-Type, Authorization"
- name: Access-Control-Max-Age
value: "86400" # Cache preflight for 24 hours
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: go-svc
port: 5000
weight: 1
EOF
HTTPS and TLS
In my video, I generate a test TLS cert using mkcert
$ curl -L https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64 -o mkcert && chmod +x mkcert && sudo mv mkcert /usr/local/bin/
#linux
$ export CAROOT=${PWD}/kubernetes/gateway-api/tls
$ mkcert -key-file kubernetes/gateway-api/tls/key.pem -cert-file kubernetes/gateway-api/tls/cert.pem example-app.com
$ mkcert -install
Now that we have a TLS cert, we can create a Kubernetes secret to store it:
$ kubectl create secret tls secret-tls -n default --cert kubernetes/gateway-api/tls/cert.pem --key kubernetes/gateway-api/tls/key.pem
We need to
- Adjust our Gateway, to enable the TLS Listener first!
- Then apply the TLS listener in our HTTP Route to enable TLS, using
sectionName
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
- name: gateway-api
sectionName: https
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/go
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: go-svc
port: 5000
weight: 1
EOF
Let’s port-forward to 443 since that is where TLS is exposed:
$ kubectl -n traefik port-forward svc/traefik 8081:443
Result: </br> https://example-app.com:8081/api/go 👉🏽 http://go-svc:5000/ </br>
Checkout More Official Guides on the Kubernetes Gateway API SIGs page.</br>
Middlewares
One of the key strengths of using Traefik is that they have a catalogue of middlewares that are plugins to perform popular common actions on traffic. </br>
There are many Middlewares available.
The cool thing here is that these Middlewares are now pluggable into Gateway API HTTPRoutes. </br>
In HTTPRoute objects, under filters, we can add extensions using something like:
filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: add-prefix
Prefix
Note: To use ExtensionRef in Traefik you need to enable providers.kubernetesCRD.enabled = true. We have that enabled in our values.yaml file.
Expectation: </br>
http://example-app.com/anything 👉🏽 http://go-svc:5000/prefix/anything </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: add-prefix
backendRefs:
- name: go-svc
port: 5000
weight: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-prefix
namespace: default
spec:
addPrefix:
prefix: /prefix
EOF
We can follow the log of our upstream to see the HTTP request:
kubectl logs -f -l app=go-app
Basic Auth
We can also add HTTP basic authentication to allow users to access secured resources protected by username and passwords. </br>
The Basic Auth middleware points to a secret which supports Kubernetes basic auth secret type. </br>
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: basic-auth
backendRefs:
- name: go-svc
port: 5000
weight: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: basic-auth
namespace: default
spec:
basicAuth:
secret: basic-auth
---
apiVersion: v1
kind: Secret
metadata:
name: basic-auth
type: kubernetes.io/basic-auth
stringData:
username: admin
password: t0p-Secret
EOF
Headers
Traefik allows us to manipulate headers too using Middleware component called headers </br>
We can use this to handle scenarios like CORS (Cross-Origin Resource Sharing)
Let’s access our web app directly over localhost which makes a call to example-app.com and should be blocked by CORS. </br>
$ kubectl port-forward svc/web-app 8000:80
Once we apply the Middleware update to our HTTPRoute, we can perform cross origin API calls:
$ cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: go-route
namespace: default
spec:
parentRefs:
- name: gateway-api
sectionName: http
kind: Gateway
hostnames:
- example-app.com
rules:
- matches:
- path:
type: PathPrefix
value: /api/go
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: headers
backendRefs:
- name: go-svc
port: 5000
weight: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: headers
spec:
headers:
accessControlAllowMethods:
- "GET"
- "OPTIONS"
- "PUT"
accessControlAllowHeaders:
- "*"
accessControlAllowOriginList:
- "http://localhost:8000"
accessControlMaxAge: 100
addVaryHeader: true
EOF