Guía de diseño de Listeners en Envoy Gateway: patrones multi-puerto, multi-hostname y multi-ingress

5 min read

Mucha gente que aprende Gateway API trata Listener como "ah, es solo el campo port: 80." Pero en despliegues reales, Listener está lejos de ser un elemento secundario — es más bien el centro de control de tráfico de toda tu capa de ingreso.

Un listener no es solo abrir un puerto. Define:

  • Qué protocolo aceptar
  • Qué hostname aceptar
  • Si realizar TLS o no
  • Qué tipos de ruta pueden adjuntarse

En otras palabras, un listener es un contrato de ingreso. Escribe contratos limpios y las rutas downstream, la propiedad del equipo y la complejidad del debugging se vuelven más claras por asociación.

Construye el modelo mental correcto: un Listener = una ranura de ingreso

Toma este Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
    - name: https
      protocol: HTTPS
      port: 443
      hostname: app.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: app-cert
    - name: grpc
      protocol: HTTPS
      port: 8443
      hostname: grpc.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: grpc-cert

Esto no es solo "un Gateway con tres agujeros." Piénsalo así:

  • listener http: para HTTP en texto plano
  • listener https: para app.example.com
  • listener grpc: una ranura de ingreso dedicada para servicios gRPC

Una vez que piensas de esta forma, muchas decisiones de diseño se vuelven inmediatamente claras. No estás ensamblando YAML — estás diseñando fronteras de ingreso.

Tres patrones comunes de división

Patrón 1: dividir por protocolo

El enfoque más intuitivo:

  • HTTP en un listener
  • HTTPS en un listener
  • gRPC en un listener
  • TCP/TLS passthrough dividido por separado

Esto otorga responsabilidad clara. Aunque gRPC y las APIs web generales podrían correr sobre HTTP/2, sus modelos de servicio difieren. Mantenerlos separados evita que las rutas interfieran entre sí.

Patrón 2: dividir por hostname

Si tienes múltiples dominios, un patrón común es un listener por hostname principal:

listeners:
  - name: app
    hostname: app.example.com
    protocol: HTTPS
    port: 443
  - name: admin
    hostname: admin.example.com
    protocol: HTTPS
    port: 443

Esto funciona bien para:

  • Separar la gobernanza pública y de administración
  • Diferentes hostnames que necesitan diferentes certificados
  • Mantener limpias las fronteras de adjunción de rutas

Patrón 3: dividir por responsabilidad de equipo o entorno

Para configuraciones multi-equipo, los listeners pueden servir como fronteras de gobernanza:

  • public-api para tráfico externo del producto
  • internal-api para sistemas internos
  • partner-api para integraciones con socios

Aquí, el valor del listener va más allá de ser configuración técnica — se convierte en una frontera de permisos y responsabilidades. Este patrón marca una diferencia real en equipos medianos y grandes, porque reduce el desastre de "un ingreso donde todos pueden adjuntar lo que quieran."

sectionName y allowedRoutes: dos campos críticos

Una ruta se adjunta a un listener específico a través de parentRefs.sectionName:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: eg
      sectionName: https
  hostnames:
    - "app.example.com"
  rules:
    - backendRefs:
        - name: app-service
          port: 8080

Esta ruta solo buscará el listener https. Sin especificar sectionName, el sistema aplica reglas para encontrar listeners adjuntables — pero en configuraciones multi-listener, ser explícito es casi siempre más seguro.

El otro campo crítico es allowedRoutes:

listeners:
  - name: public
    protocol: HTTPS
    port: 443
    allowedRoutes:
      namespaces:
        from: Same

Esto significa que solo las rutas en el mismo namespace que el Gateway pueden adjuntarse. Valores comunes:

  • Same: solo el mismo namespace
  • All: cualquier namespace
  • Selector: solo namespaces que coincidan con un selector de etiquetas

Si quieres ser deliberado sobre las fronteras de la plataforma, este campo es muy poderoso. Decide "quién puede adjuntarse a este ingreso" en el nivel del listener mismo.

Consejos prácticos: cómo estructurar los listeners sin arrepentimientos

Un principio muy útil:

Cada listener lleva una responsabilidad claramente definida.

Por ejemplo:

  • Un listener sirve a un grupo principal de hostnames
  • Un listener maneja un tipo de protocolo principal
  • Un listener está abierto a un tipo de ruta o un conjunto fijo de namespaces

No empaquetes todo el tráfico en un listener multiusos y esperes que las rutas se auto-organicen aguas abajo. Ese patrón parece eficiente al principio, pero tres meses después parece el cajón misterioso de cables que se acumula en cada casa — todo está ahí dentro, nada se puede encontrar.

Sugerencias específicas:

  • Los nombres de los listeners deben ser semánticos: https-public, grpc-internal
  • En configuraciones multi-listener, usa siempre sectionName
  • No mezcles hostnames importantes en un solo listener
  • Por defecto, restringe allowedRoutes primero — no empieces con from: All

Resumen en una línea

Trata Listener como un contrato de ingreso, no solo como una configuración de puerto. Fronteras de listener limpias significan que las rutas se adjuntan correctamente, los equipos gestionan sus áreas de forma natural y el debugging se siente menos como perseguir un gato por un laberinto.

Siguiente paso

Una vez que el diseño de listeners está definido, la siguiente pregunta común es: En distintos namespaces y equipos, ¿quién puede adjuntar rutas y quién puede referenciar qué?

El próximo artículo cubre los patrones multi-namespace y la propiedad de equipos: 👉 Multi-Namespace y propiedad de equipos