Március 25-én ébredtem egy Slack üzenetre a security csapattól: “Az ingress-nginx tegnaptól EOL. Mi a terv a migrációra?”

Hónapok óta halogattam. A nyugdíjazást még 2025 novemberében bejelentették, de messzinek tűnt. Most viszont valóság lett. Nincs több CVE patch. Nincs több hibajavítás. Az óra ketyeg.

Mi történt pontosan

  1. március 24-én a Kubernetes SIG Network és a Security Response Committee hivatalosan nyugdíjazta az ingress-nginx-et. A projekt lezárult. A container image-ek és Helm chartok elérhetők maradnak (nem törölnek semmit), de új release nem lesz. Ha holnap jön egy kritikus sebezhetőség, magadra vagy utalva.

Ez nem egy deprecation warning, amit három release-en át ignorálhatsz. Ez azt jelenti, hogy a karbantartók elmentek.

Felmérés

Először megnéztem, mi fut egyáltalán. Három klaszteren:

kubectl get ingress -A -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name, (.spec.rules[]?.host // "no-host")] | @tsv' | sort

Eredmény: 6 Ingress resource 4 namespace-ben. Kettőnek az annotációihoz egy éve nem nyúltam. Az egyiken egy configuration-snippet volt, amitől összeszorult a gyomrom.

kubectl get ingress -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}' | wc -l
# 6

Nem tragikus. De néhánynak komplex konfigja volt: rate limiting, egyedi headerek, CORS, SSL redirect logika annotációkba rejtve.

A csere kiválasztása

Két klaszteren már futott Gateway API (erről korábban írtam). A harmadik klaszter régebbi volt, bare metal-en futott. Három opció:

  1. Gateway API Envoy Gateway-jel - ez volt a preferenciám
  2. Traefik - stabil, jól karbantartott
  3. HAProxy Ingress - ha közel akarnék maradni az Ingress resource modellhez

Az Envoy Gateway mellett döntöttem a konzisztencia kedvéért. Ha már Traefik-en vagy Contour-on vagy, maradj ott. A cél az ingress-nginx elhagyása, nem proxy-háború.

Envoy Gateway telepítése

helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  --namespace envoy-gateway-system \
  --create-namespace

Ellenőrzés:

kubectl -n envoy-gateway-system get pods
# NAME                                    READY   STATUS    RESTARTS   AGE
# envoy-gateway-5d89f7c5b6-x2k4m         1/1     Running   0          45s

Aztán a GatewayClass és Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: envoy-gateway-system
spec:
  gatewayClassName: eg
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: wildcard-tls
    allowedRoutes:
      namespaces:
        from: All
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All

A tényleges migráció, service-ről service-re

Egyszerű service-ek (4 a 6-ból)

Négy Ingress resource egyértelmű volt: host alapú routing, TLS termination, semmi extra. Ezek tisztán leképezhetők HTTPRoute-ra:

Előtte (Ingress):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 8080

Utána (HTTPRoute):

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
spec:
  parentRefs:
  - name: main-gateway
    namespace: envoy-gateway-system
  hostnames:
  - api.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: api-svc
      port: 8080

Az SSL redirect a Gateway szinten van kezelve, nem kell hozzá annotáció. Egy dologgal kevesebb, amit el lehet felejteni.

Mind a négyet párhuzamosan alkalmaztam, teszteltem, aztán töröltem a régi Ingress resource-okat:

for route in api-route dashboard-route docs-route webhook-route; do
  kubectl apply -f "${route}.yaml"
done

# tesztelés
for host in api.example.com dash.example.com docs.example.com hooks.example.com; do
  curl -s -o /dev/null -w "%{http_code} ${host}\n" "https://${host}/healthz"
done

Mind 200-at adott vissza. Régi Ingress objektumok törölve.

A trükkös: Rate Limiting

Egyik service-en rate limiting volt nginx annotációkkal:

annotations:
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"

Envoy Gateway-nél ehhez BackendTrafficPolicy kell:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: api-ratelimit
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: public-api-route
  rateLimit:
    type: Global
    global:
      rules:
      - clientSelectors:
        - headers:
          - name: x-forwarded-for
            type: Distinct
        limit:
          requests: 10
          unit: Second

Bőbeszédűbb, de sokkal erősebb is. Tudsz header, IP vagy path alapján limitálni. Az nginx annotációs megoldás mindig olyan volt, mint a szigetelőszalag.

A ronda: Configuration Snippets

Az utolsó service-en configuration-snippet annotáció volt nyers nginx konfiggal:

annotations:
  nginx.ingress.kubernetes.io/configuration-snippet: |
    more_set_headers "X-Frame-Options: DENY";
    more_set_headers "X-Content-Type-Options: nosniff";
    if ($request_uri ~* "^/old-path") {
      return 301 https://$host/new-path;
    }    

Ettől féltem a legjobban. Az egyedi headerek egyszerűek voltak SecurityPolicy-val. A redirect-hez HTTPRoute filter kellett:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: legacy-redirect
spec:
  parentRefs:
  - name: main-gateway
    namespace: envoy-gateway-system
  hostnames:
  - app.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /old-path
    filters:
    - type: RequestRedirect
      requestRedirect:
        path:
          type: ReplaceFullPath
          replaceFullPath: /new-path
        statusCode: 301
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app-svc
      port: 8080

Egyedi headerek SecurityPolicy-val:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: security-headers
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: main-gateway
  headers:
    setHeaders:
      X-Frame-Options: "DENY"
      X-Content-Type-Options: "nosniff"

A csapda, ami két órámba került

Migráció után az egyik service időnként 503-at dobott. A logok szerint a backend egészséges volt. Kiderült, hogy a régi nginx LoadBalancer Service externalTrafficPolicy: Local beállítása és az Envoy Gateway saját Service-e között volt konfliktus.

A javítás: az új Envoy Gateway Service-nek a helyes externalTrafficPolicy-t kellett beállítani, és a node-okon ellenőrizni a health check konfigot. Ha bare metal-en vagy MetalLB-vel, nézd át az L2 advertisement konfigot a controller csere után.

kubectl -n envoy-gateway-system get svc
# Ellenőrizd az EXTERNAL-IP-t és TYPE-ot
kubectl describe svc -n envoy-gateway-system envoy-main-gateway

Takarítás

Miután 48 órán át minden rendben működött, eltávolítottam az ingress-nginx-et:

helm uninstall ingress-nginx -n ingress-nginx
kubectl delete namespace ingress-nginx

Aztán a régi IngressClass:

kubectl delete ingressclass nginx

DNS átállás

Amit az emberek elfelejtenek: ha a régi nginx LoadBalancer-nek más külső IP-je volt, mint az új Envoy Gateway-nek, frissítened kell a DNS-t. External-dns-t használok annotáció alapú szűréssel, szóval csak azt kellett biztosítani, hogy az új Gateway Service-en rajta legyenek a megfelelő annotációk:

metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: "*.example.com"

Ha manuálisan kezeled a DNS-t, frissítsd az A rekordokat az új LoadBalancer IP-re.

Mit csinálnék másként

  1. Korábban kezdeném. A novemberi bejelentés négy hónapot adott. Ebből körülbelül két napot használtam ki. Ne legyél olyan, mint én.
  2. A rate limitinget tesztelném először. Az egyszerű service-ek könnyűek. A komplex, annotáció-terhelt service-ek viszik az idő 80%-át.
  3. Mindkét controllert párhuzamosan futtatnám. Ezt csináltam, és megmentett. Hagyd futni az ingress-nginx-et, amíg validálod az új route-okat. A régi Ingress resource-okat csak akkor töröld, ha az újak bizonyítottan működnek.
  4. Ellenőrizd a monitoringot. Ha nginx-specifikus Prometheus metrikáid voltak (mint az nginx_ingress_controller_requests), azok eltűnnek. Az Envoy Gateway más metrikákat ad. Frissítsd a dashboardjaidat, mielőtt elveszíted a rálátást.

Végszó

Az ingress-nginx évekig jól szolgálta a közösséget. De most vége, és minél tovább vársz, annál kockázatosabbak a klasztereid. A migráció egy hétvégémbe került hat service-re. Ha neked tucatnyi van, kezdj el tervezni most.

A Gateway API tényleg jobb. Kifejezőbb, kompozálhatóbb, kevesebb annotáció-spagetti. Ha ez a nyugdíjazás az, ami végre ráveszi a csapatodat az átállásra, az összességében pozitív.

Ne várd meg az első foltozatlan CVE-t ahhoz, hogy ez sürgőssé váljon.