Limitaciones del recurso Ingress
El recurso Ingress era intencionalmente mínimo. Manejaba ruteo HTTP (host + path -> backend) y terminación TLS. Todo lo demás, rate limiting, autenticación, rewriting, CORS, soporte WebSocket, gRPC, ruteo TCP/UDP, canary deployments, se empujaba a annotations específicas del controller.
# Un Ingress de producción típico con annotations específicas del controller
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway
annotations:
# Específico de nginx - no funciona con traefik ni otro controller
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/rate-limit-rps: "100"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Request-ID: $req_id";
if ($http_upgrade = "websocket") {
proxy_pass http://upstream;
}
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1
port:
number: 8080
Esto es un desastre. Las annotations son strings no validados. El configuration-snippet es config nginx cruda que se inyecta tal cual, una vulnerabilidad de seguridad si cualquier usuario no confiable puede setear annotations (CVE-2021-25742 fue exactamente esto). Y nada es portable entre controllers.
Gateway API: El Modelo de Tres Recursos
La Gateway API reemplaza el recurso Ingress con un modelo de tres capas que mapea a roles organizacionales:
┌─────────────────────────────────────────────────┐
│ GatewayClass (Admin del cluster) │
│ "Qué implementación de controller usar" │
│ Se crea una vez por cluster │
├─────────────────────────────────────────────────┤
│ Gateway (Equipo de plataforma / SRE) │
│ "Dónde escuchar, config TLS, rutas permitidas" │
│ Uno por punto de entrada (externo, interno, etc)│
├─────────────────────────────────────────────────┤
│ HTTPRoute / TLSRoute / GRPCRoute (Equipo app) │
│ "Cómo rutear tráfico a mi servicio" │
│ Uno por servicio o grupo de servicios │
└─────────────────────────────────────────────────┘
Esta separación es la característica más destacada. El equipo de plataforma controla el Gateway, puertos, certificados TLS, namespaces permitidos. Los equipos de app crean Routes en sus propios namespaces. Se acabó el "necesito cluster-admin para agregar una annotation a mi Ingress."
Acá está el equivalente en Gateway API del horror nginx-ingress de arriba:
# GatewayClass - el admin del cluster configura esto una vez
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
# Gateway - el equipo de plataforma maneja esto
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external
namespace: gateway-infra
spec:
gatewayClassName: envoy
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
namespace: cert-manager
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: "true"
---
# HTTPRoute - el equipo de app crea esto en su namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-v1
namespace: api-team
spec:
parentRefs:
- name: external
namespace: gateway-infra
hostnames:
- "api.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- name: api-v1
port: 8080
Cada campo es validado por el API server de Kubernetes. Los typos se rechazan al hacer apply, no se ignoran silenciosamente. La ruta referencia su Gateway padre explícitamente. Y el mismo HTTPRoute funciona con Envoy Gateway, Cilium, Istio o cualquier implementación conformante.
Traffic Splitting: Canary Sin Tooling
Acá es donde la Gateway API provee valor inmediato. Usar Flagger + nginx-ingress para canary deployments lo que significaba correr un controller custom que manipulaba annotations para mover tráfico. Con Gateway API, el traffic splitting es nativo:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-canary
namespace: api-team
spec:
parentRefs:
- name: external
namespace: gateway-infra
hostnames:
- "api.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /v2
backendRefs:
- name: api-v2-stable
port: 8080
weight: 95
- name: api-v2-canary
port: 8080
weight: 5
Cinco por ciento del tráfico va al canary. Ajustás los pese es y reaplicás. Sin controllers custom, sin gimnasia de annotations.
TLS Passthrough: Lo que Ingress Nunca Hizo Bien
TLS passthrough, reenviar tráfico encriptado sin terminar TLS en el gateway, fue un dolor de cabeza constante con Ingress. Gateway API lo maneja con TLSRoute y modos de listener del Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external
namespace: gateway-infra
spec:
gatewayClassName: envoy
listeners:
- name: https-terminate
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
allowedRoutes:
kinds:
- kind: HTTPRoute
- name: tls-passthrough
port: 443
protocol: TLS
tls:
mode: Passthrough
hostname: "vault.example.com"
allowedRoutes:
kinds:
- kind: TLSRoute
Los dos listeners comparten el puerto 443. El Gateway inspecciona el SNI (Server Name Indication) en el ClientHello de TLS para decidir si terminar o pasar directo. Se maneja a nivel de protocolo, sin hacks de annotations.
Cilium: Service Mesh Sin el Impuesto del Sidecar
Para tráfico east-west (servicio a servicio), Cilium con Gateway API es una opción efectiva. El approach basado en eBPF de Cilium significa que no hay sidecars, la lógica de networking corre en el kernel:
La diferencia de rendimiento entre Cilium (eBPF) y meshes basados en sidecar (Istio con sidecars Envoy) es medible. En un servicio sensible a latencia manejando 50K RPS:
| Métrica | Sin mesh | Istio sidecar | Cilium eBPF |
|---|---|---|---|
| Latencia P50 | 2.1ms | 3.4ms | 2.3ms |
| Latencia P99 | 8.7ms | 14.2ms | 9.1ms |
| Memoria por pod | base | +50MB (sidecar) | +0MB |
| CPU por pod | base | +0.1 core (sidecar) | +~0 |
El overhead del sidecar es 50MB de RAM y 0.1 core de CPU por pod. En 200 servicios con 3 réplicas cada uno, son 30GB de RAM y 60 cores de CPU solo para el mesh. Cilium elimina esto completamente.
La Migración: 200 Servicios en 6 Semanas
La migración incremental funciona bien. El insight clave: Gateway API e Ingress pueden coexistir en el mismo cluster porque usan recurse es distintos.
Semana 1-2: Deploy de Envoy Gateway junto a nginx-ingress. Crear GatewayClass y Gateway. Migrar 5 servicios no críticos.
Semana 3-4: Migrar todos los servicios API stateless. Validar con tráfico sintético antes de cambiar DNS.
Semana 5: Migrar servicios con requerimientos de WebSocket y gRPC. Estos fueron más fáciles con Gateway API porque HTTPRoute soporta nativamente upgrades WebSocket y existe GRPCRoute.
Semana 6: Migrar servicios restantes, decomisionar nginx-ingress.
Un hallazgo notable: se encontraron 23 recurse es Ingress con annotations rotas que nadie había notado. Typos, nombres de annotations deprecadas, annotations para el controller equivocado. Todas silenciosamente sin hacer nada. Gateway API las habría rechazado al momento de crearlas.
Lo Que le Falta a Gateway API
Persisten algunos gaps:
Rate limiting no está estandarizado. Cada implementación tiene sus propios CRDs. La propuesta GEP-1731 para policy attachment sigue evolucionando.
Autenticación/autorización a nivel gateway es específica de implementación. No hay un filtro HTTPRoute estándar para validación JWT u OAuth2.
Ruteo TCP/UDP funciona (TCPRoute, UDPRoute) pero sigue en el canal experimental.
A pesar de estos gaps, Gateway API representa la dirección correcta. El modelo de seguridad role-based, la configuración tipada, la portabilidad entre implementaciones y las features nativas de traffic management la hacen un upgrade claro del approach de Ingress que se usó durante siete años.
Para clusters que todavía corren Ingress con configuraciones pesadas en annotations, planear una migración es una inversión que vale la pena.