Lab 15 – Gateway API
Lab 15 – Gateway API
Cíl: nainstalovat Gateway controller a nasměrovat dvě aplikace podle jména hosta přes jeden port – a pak rozdělit provoz mezi dvě verze. Kapitola: Gateway API
Objekty Gateway API jsou jen data. Nejdřív musí existovat dvě věci: CRD (nejsou součástí Kubernetes) a controller, který je čte.
Instalace CRD
# 1 – standardní kanál: GatewayClass, Gateway, HTTPRoute
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
kubectl get crd | grep gateway.networking
kubectl api-resources --api-group=gateway.networking.k8s.io
Právě jste rozšířili API server o nové druhy objektů – přesně ten mechanismus CRD, který probíráme třetí den, tady potkaný v praxi. Na stránce vydání zkontrolujte, jestli není novější verze.
# 2 – explain na nich funguje okamžitě
kubectl explain gateway.spec.listeners
kubectl explain httproute.spec.rules.backendRefs
Instalace controlleru
# 3 – NGINX Gateway Fabric. Datová rovina tu nemá cloudový LoadBalancer, takže
# si vyžádáme NodePort a připíchneme port 30080 k listeneru na portu 80.
helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
--create-namespace -n nginx-gateway \
--set nginx.service.type=NodePort \
--set nginx.service.nodePorts[0].port=30080 \
--set nginx.service.nodePorts[0].listenerPort=80
kubectl get pods -n nginx-gateway -w # Ctrl-C až bude Running
Pokud helm chybí:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod +x get_helm.sh && sudo ./get_helm.sh && helm version
nodePorts je seznam dvojic {port, listenerPort}: „vystav node port 30080 pro
listener Gateway na portu 80“. Dvojice, jejíž listenerPort neodpovídá žádnému
listeneru, se tiše ignoruje – když tedy Gateway později přepnete na port 8080,
tenhle záznam přestane platit a Kubernetes přidělí náhodný port.
# 4 – co tím vzniklo?
kubectl get gatewayclass
kubectl get all -n nginx-gateway
Dvě věci k povšimnutí, protože v tomhle labu způsobují nejvíc zmatku:
- GatewayClass se jménem
nginxje to, na co bude ukazovat vaše Gateway. - Jediná Service tady je
ngf-nginx-gateway-fabrica je ClusterIP – a je to tak správně. Tohle je řídicí rovina: pod s controllerem, který přes gRPC mluví s NGINX agenty. Není to proxy a váš provoz nikdy neobsluhuje.
Zatím neexistuje žádný pod s proxy ani žádná NodePort Service.
Datová rovina neexistuje, dokud neexistuje Gateway, a nevzniká v tomhle
namespace. NGINX Gateway Fabric 2.x vytváří jeden NGINX Deployment a jednu
Service na každou Gateway, ve vlastním namespace té Gateway. Po kroku 6 je
tedy najdete v default, ne v nginx-gateway.
Když teď budete v nginx-gateway hledat NodePort Service, najdete jen ClusterIP
řídicí roviny a usoudíte, že --set nginx.service.type nic neudělal. Udělal –
jen se díváte na špatné místo ve špatnou chvíli.
Dvě aplikace
# 5 –
cd ~
kubectl create deploy shop --image nginx:1.27
kubectl create deploy wiki --image httpd:2.4
kubectl expose deploy shop --port 80
kubectl expose deploy wiki --port 80
kubectl get svc shop wiki
Obě jsou ClusterIP. Zvenčí na ně nikdo nedosáhne a ani po tomhle labu nebudou dostupné jinak než přes Gateway.
Gateway
# 6 –
cat > gateway.yaml <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web-gw
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
YAML
kubectl apply -f gateway.yaml
kubectl get gateway web-gw
kubectl describe gateway web-gw | tail -20
Sledujte PROGRAMMED. True znamená, že controller Gateway přijal a postavil
pro ni datovou rovinu; False znamená přečíst si podmínky v describe. Ingress
nic takového neměl – prostě tiše nedělal nic.
# 7 – TEĎ datová rovina existuje – v namespace té Gateway
kubectl get pods,svc -n default -l gateway.networking.k8s.io/gateway-name=web-gw
kubectl get svc -A | grep -i web-gw
V default se objevil NGINX Deployment a Service pojmenovaná podle Gateway,
a ta Service je NodePort s portem 30080. Porovnejte s nginx-gateway, kde je
pořád jen ClusterIP řídicí roviny:
# 8 – obě poloviny vedle sebe
kubectl get svc -n nginx-gateway # ClusterIP – řídicí rovina
kubectl get svc -n default | grep web-gw # NodePort – datová rovina, váš provoz
# 9 – port si přečtěte z clusteru, nespoléhejte na připíchnutí
PORT=$(kubectl get svc -n default -l gateway.networking.k8s.io/gateway-name=web-gw \
-o jsonpath='{.items[0].spec.ports[?(@.port==80)].nodePort}')
echo "node port brány: $PORT"
Vždycky si ho přečtěte zpátky. Pokud se připíchnutí neuplatnilo – jiný port
listeneru, starší chart, překlep v --set – $PORT vám i tak dá skutečnou
hodnotu a všechny curl níže budou fungovat.
Pokud je Service přesto ClusterIP, dá se to spravit bez reinstalace:
SVC=$(kubectl get svc -n default -l gateway.networking.k8s.io/gateway-name=web-gw -o name)
kubectl patch $SVC -n default -p '{"spec":{"type":"NodePort"}}'
kubectl patch $SVC -n default --type=json \
-p '[{"op":"replace","path":"/spec/ports/0/nodePort","value":30080}]'
Je to ruční zásah do objektu, který vlastní controller, takže to berte jako
náhradní řešení: správná oprava je helm upgrade se správnými hodnotami,
protože controller vám ten patch může přepsat.
Routy
# 10 – jedna routa na aplikaci, vlastní ji aplikační tým
cat > routes.yaml <<'YAML'
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop
spec:
parentRefs:
- name: web-gw
hostnames: ["shop.k8s.lab"]
rules:
- matches:
- path: { type: PathPrefix, value: / }
backendRefs:
- name: shop
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: wiki
spec:
parentRefs:
- name: web-gw
hostnames: ["wiki.k8s.lab"]
rules:
- matches:
- path: { type: PathPrefix, value: / }
backendRefs:
- name: wiki
port: 80
YAML
kubectl apply -f routes.yaml
kubectl get httproute
kubectl describe httproute shop | tail -15
Každá routa si svou Gateway pojmenuje v parentRefs; Gateway nevyjmenovává
nikoho. Přidat aplikaci znamená přidat routu, nikdy editovat sdílenou
konfiguraci.
# 11 – test: stejný port, jiná hlavička Host
curl -s -H 'Host: shop.k8s.lab' http://worker01:$PORT | grep -o '<title>.*</title>'
curl -s -H 'Host: wiki.k8s.lab' http://worker01:$PORT | head -3
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: nothing.k8s.lab' http://worker01:$PORT
Jeden port, dvě aplikace, směrování podle jména. To 404 vrací sama proxy – požadavek dostala a neměla pro něj routu.
Rozdělení provozu
Tohle Ingress bez anotací výrobce vyjádřit neuměl.
# 12 – druhá verze e-shopu
kubectl create deploy shop-v2 --image httpd:2.4
kubectl expose deploy shop-v2 --port 80
kubectl patch httproute shop --type merge -p '{"spec":{"rules":[{"matches":[{"path":{"type":"PathPrefix","value":"/"}}],"backendRefs":[{"name":"shop","port":80,"weight":90},{"name":"shop-v2","port":80,"weight":10}]}]}}'
for i in $(seq 1 20); do
curl -s -H 'Host: shop.k8s.lab' http://worker01:$PORT | grep -o -E 'nginx|It works'
done | sort | uniq -c
Zhruba 18 ku 2. Změňte váhy a spusťte znovu – to je canary release v jednom poli jednoho objektu.
Objevovací část
InfodiscoveryÚkol A. Nasměrujte http://api.k8s.lab/wiki na Service wiki, zatímco
http://api.k8s.lab/ vrátí 404.
Úkol B. Pošlete požadavky s hlavičkou x-version: beta na shop-v2
a všechny ostatní na shop. Shodu podle hlavičky Ingress nikdy neuměl.
Úkol C. Zařiďte, aby jména fungovala ze support01 bez hlavičky Host:.
Úkol D. Routa nefunguje. Zjistěte jen pomocí kubectl, jestli ji Gateway přijala, na které Services ukazuje a ve kterém namespace běží její datová rovina.
Úklid
kubectl delete -f routes.yaml -f gateway.yaml
kubectl delete deploy shop wiki shop-v2
kubectl delete svc shop wiki shop-v2
sudo sed -i '/k8s.lab/d' /etc/hosts
# smazáním Gateway zmizela i její datová rovina – ověřte
kubectl get pods,svc -n default | grep web-gw || echo "datová rovina je pryč"
Smazáním Gateway zmizel i NGINX Deployment a Service: controller je vlastní, přesně jako ReplicaSet vlastní své pody.
Controller i CRD nechte nainstalované – lab 23 je používá.