Lab 16 - Gateway API with Traefik

Lab 16 - Gateway API with Traefik

Goal: route by host, path and header through one entry point, split traffic for a canary - as the application team, on a Gateway owned by the platform team. Chapter: Gateway API

Requires Traefik and the Gateway API CRDs from Lab 14 and the shop / wiki apps in namespace web from Lab 15.

The platform side

# 1 - what the platform team gives you
kubectl get gatewayclass
kubectl get gateway -n traefik
kubectl get gateway traefik-gateway -n traefik -o yaml | grep -A12 'listeners:'
  • GatewayClass traefik - the implementation.
  • Gateway traefik-gateway in namespace traefik, listener web on port 8000 - Traefik’s entry point, exposed on NodePort 30080.
  • allowedRoutes.namespaces.from: All - routes from any namespace may attach.
# 2 - PROGRAMMED must be True
kubectl get gateway traefik-gateway -n traefik

The application side

# 3 - one HTTPRoute per application, in the application's namespace
cd ~
cat > routes.yaml <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: web
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: traefik
  hostnames: ["shop.k8s.lab"]
  rules:
    - backendRefs:
        - name: shop
          port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: wiki
  namespace: web
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: traefik
  hostnames: ["wiki.k8s.lab"]
  rules:
    - backendRefs:
        - name: wiki
          port: 80
YAML
kubectl apply -f routes.yaml
kubectl get httproute -n web
# 4 - did the Gateway accept them?
kubectl describe httproute shop -n web | grep -A20 'Status:'

Look for Accepted: True and ResolvedRefs: True under the parent traefik-gateway. The route tells you if something is wrong - Ingress never did.

# 5 - test
curl -s -H 'Host: shop.k8s.lab' http://worker01:30080 | grep -o '<title>.*</title>'
curl -s -H 'Host: wiki.k8s.lab' http://worker01:30080

Canary release

# 6 - version 2 of the shop
kubectl create deploy shop-v2 --image httpd:2.4 -n web
kubectl expose deploy shop-v2 --port 80 -n web

Edit routes.yaml: in the shop route replace the backendRefs with two weighted backends.

    - backendRefs:
        - name: shop
          port: 80
          weight: 90
        - name: shop-v2
          port: 80
          weight: 10
# 7 - apply and count
kubectl apply -f routes.yaml
for i in $(seq 1 20); do
  curl -s -H 'Host: shop.k8s.lab' http://worker01:30080 | grep -o -E 'nginx|It works'
done | sort | uniq -c

About 18 : 2. Change the weights to 50/50, then 0/100 - a full release in one field of one object.

Discovery

Infodiscover

Task A. Header routing: requests with x-version: beta go to shop-v2, everything else to shop.

  rules:
    - matches:
        - headers:
            - name: x-version
              value: beta
      backendRefs:
        - { name: shop-v2, port: 80 }
    - backendRefs:
        - { name: shop, port: 80 }
kubectl apply -f routes.yaml
curl -s -H 'Host: shop.k8s.lab' -H 'x-version: beta' http://worker01:30080
curl -s -H 'Host: shop.k8s.lab' http://worker01:30080 | grep -o '<title>.*</title>'

The more specific match wins.

Task B. Path routing with rewrite: http://api.k8s.lab/wiki/ should show the wiki, although the wiki itself serves /.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api
  namespace: web
spec:
  parentRefs:
    - { name: traefik-gateway, namespace: traefik }
  hostnames: ["api.k8s.lab"]
  rules:
    - matches:
        - path: { type: PathPrefix, value: /wiki }
      filters:
        - type: URLRewrite
          urlRewrite:
            path: { type: ReplacePrefixMatch, replacePrefixMatch: / }
      backendRefs:
        - { name: wiki, port: 80 }
curl -s -H 'Host: api.k8s.lab' http://worker01:30080/wiki/

Task C. Break it on purpose: point the wiki route to a Service wikix that does not exist. Which condition tells you, and what does the client get?

kubectl patch httproute wiki -n web --type=json \
  -p '[{"op":"replace","path":"/spec/rules/0/backendRefs/0/name","value":"wikix"}]'
kubectl get httproute wiki -n web -o jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")]}{"\n"}'
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: wiki.k8s.lab' http://worker01:30080
kubectl apply -f routes.yaml     # repair

ResolvedRefs False, reason BackendNotFound; the client gets an error (500).

Task D. Make the hostnames work from support01 without a Host: header.

echo "$(getent hosts worker01 | awk '{print $1}') shop.k8s.lab wiki.k8s.lab api.k8s.lab" \
  | sudo tee -a /etc/hosts
curl -s http://shop.k8s.lab:30080 | grep -o '<title>.*</title>'

In production: DNS pointing at a load balancer in front of the nodes.

Clean up

kubectl delete ns web
sudo sed -i '/k8s.lab/d' /etc/hosts

Keep Traefik and the CRDs installed - later labs use them.