Ezt a migrációt túl sokáig tologattam.
Valahányszor szóba került a Gateway API, mindig ugyanazt mondtam: “igen, rajta van a listán.” Aztán múlt héten végre nem csak beszéltem róla, hanem meg is csináltam: három production clustert átvittem Ingressről Gateway API-ra.
Őszintén, hamarabb kellett volna meglépnem.
Miért most vágtam bele
A végső lökést egy multi-tenant cluster adta, ahol két csapat ugyanazt a domaint használta, de eltérő TLS viselkedésre volt szükségük.
Klasszikus Ingress-szel ez nagyon gyorsan kaotikus lesz. Jönnek a controller-specifikus annotation-ök, és simán előfordul, hogy az egyik csapat változtatása elrontja a másik routingját.
A Gateway API itt sokkal tisztább modellt ad:
- A platform csapat kezeli a
Gatewayresource-okat - Az alkalmazás csapatok kezelik a
HTTPRouteresource-okat
Nekünk ez önmagában rengeteg egyeztetési problémát megszüntetett.
Mi kell hozzá
A Gateway API nincs alapból benne a Kubernetesben, ezért először fel kell tenni a CRD-ket:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
Ezután kell egy olyan controller, ami támogatja.
Nálam:
- Új clustereknél: Envoy Gateway
- Meglévő NGINX-es környezetben: NGINX Gateway Fabric
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.3.0 \
-n envoy-gateway-system --create-namespace
Így nézett ki a migráció
Egy tipikus Ingress nálunk ilyen volt:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/rate-limiting: "on"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 8080
A Gateway API-ban ez két külön resource.
Először a Gateway (platform oldali felelősség):
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: gateway-infra
spec:
gatewayClassName: eg
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true"
Utána a HTTPRoute (alkalmazás oldali felelősség):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
namespace: my-app-ns
spec:
parentRefs:
- name: shared-gateway
namespace: gateway-infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: my-app
port: 8080
A lényeg: nincs szabad szöveges annotation-halmozás. A specifikáció típusos, validálható, és jóval hordozhatóbb.
Amik nálam gondot okoztak
1. Cross-namespace hivatkozáshoz kell ReferenceGrant
Ha a route a my-app-ns namespace-ben van, de a Gateway a gateway-infra-ban, kell egy ilyen erőforrás a Gateway namespace-ébe:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-app-routes
namespace: gateway-infra
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: my-app-ns
to:
- group: ""
kind: Gateway
Ezt az első körben elfelejtettem, és jó ideig azt kerestem, miért “not accepted” minden.
2. A path matching szigorúbb
Gateway API-nál explicit meg kell mondani, hogy PathPrefix vagy Exact.
Nálunk egy service a korábbi lazább viselkedésre támaszkodott, és az átállás után azonnal eltört.
3. A header alapú routing végre kényelmes
Ami korábban sokszor hackelős annotation volt, itt tiszta route szabály:
rules:
- matches:
- headers:
- name: X-Canary
value: "true"
backendRefs:
- name: my-app-canary
port: 8080
- backendRefs:
- name: my-app-stable
port: 8080
Ezzel egyszerű canary routingot csináltunk service mesh nélkül.
4. A súlyozott forgalomelosztás be van építve
backendRefs:
- name: my-app-v1
port: 8080
weight: 90
- name: my-app-v2
port: 8080
weight: 10
Egyszerű százalékos rolloutnál ez kiváltott nálunk több extra eszközt.
Mi lesz a régi annotation funkciókkal?
Rate limit, timeout, body size limit továbbra is megoldható, csak policy resource-okon keresztül (controller-specifikusan):
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: rate-limit
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: my-app
rateLimit:
type: Global
global:
rules:
- limit:
requests: 100
unit: Second
Tehát marad némi implementációfüggés, de legalább rendes Kubernetes objektumokkal, sémával és validációval.
Ingress és Gateway API párhuzamosan
Nem kell egyszerre mindent átvágni.
Nálam kb. két hétig ment egymás mellett a kettő, weighted DNS-sel terelve a forgalmat, aztán fokozatosan vágtam át.
# Gateway állapot ellenőrzése
kubectl get gateway -A
kubectl get httproute -A
# Route elfogadás ellenőrzése
kubectl describe httproute my-app -n my-app-ns
A HTTPRoute status mezeje tényleg hasznos hibakeresésnél.
Megérte?
Igen.
A multi-tenant elkülönítés már önmagában elég érv volt. A nagyobb nyereség viszont a hordozhatóság: ha később controllert váltunk, a HTTPRoute erőforrások nagy része maradhat.
Az annotation káosz hiányzik a legkevésbé.
Gyors migrációs checklist
- Gateway API CRD-k telepítése
- Gateway API controller indítása a meglévő Ingress controller mellett
Gatewayresource-ok létrehozása a közös listenerekhez- Ingress-ek fokozatos konvertálása
HTTPRoute-ra ReferenceGranthozzáadása, ahol cross-namespace hozzáférés kell- Tesztelés weighted DNS-sel teljes átállás előtt
- Régi Ingress resource-ok törlése validálás után
- Régi Ingress controller eltávolítása, ha minden átállt
Én azt javaslom, egy nem kritikus service-szel kezdd, építs ki egy működő mintát, aztán jöhetnek a többi szolgáltatás csoportosan.