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 Gateway resource-okat
  • Az alkalmazás csapatok kezelik a HTTPRoute resource-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

  1. Gateway API CRD-k telepítése
  2. Gateway API controller indítása a meglévő Ingress controller mellett
  3. Gateway resource-ok létrehozása a közös listenerekhez
  4. Ingress-ek fokozatos konvertálása HTTPRoute-ra
  5. ReferenceGrant hozzáadása, ahol cross-namespace hozzáférés kell
  6. Tesztelés weighted DNS-sel teljes átállás előtt
  7. Régi Ingress resource-ok törlése validálás után
  8. 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.