HTTP Routing con Envoy Gateway en la Práctica: Host, Path, Header y División de Tráfico

6 min read

Los dos primeros artículos te dieron la base de "qué es esto." Este entra en lo que realmente estarás editando día a día: HTTPRoute.

Piensa en HTTPRoute como un script de enrutamiento para el tráfico entrante — excepto que en lugar de escribir reglas de Nginx o memorizar anotaciones de controlador, estás usando recursos nativos de Kubernetes.

Una cosa que dejar clara primero: este artículo solo cubre HTTPRoute, porque maneja la semántica HTTP/HTTPS. Para gRPC, te inclinarías hacia GRPCRoute; para TCP sin procesar, TCPRoute; para TLS passthrough, TLSRoute. No fuerces todo en HTTPRoute — el YAML empieza a transmitir una vibra de "arreglándoselas."

El Enrutamiento Más Común: Host y Path

Aquí está el ejemplo más típico:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "app.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-service
          port: 8080

Lo que esto significa:

  • Solo Host: app.example.com coincidirá con esta ruta
  • El path debe comenzar con /api
  • El tráfico se reenvía a api-service:8080

Si eliminas hostnames, la ruta se convierte en "solo path, cualquier dominio."

Qué Puede Coincidir matches

matches es uno de los conceptos más centrales en HTTPRoute. Piénsalo como "qué condiciones activan esta regla."

Condiciones de coincidencia comunes:

  • path
  • headers
  • queryParams
  • method

Ejemplo:

matches:
  - method: GET
    path:
      type: PathPrefix
      value: /api
    headers:
      - name: version
        value: v2

Significado:

  • Solo solicitudes GET
  • El path comienza con /api
  • La solicitud debe incluir el header version: v2

Este patrón es ideal para pruebas de versiones de API o lanzamientos graduales a usuarios específicos.

Una Route, Múltiples Reglas

Un solo HTTPRoute puede enrutar diferentes paths a diferentes servicios:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /api
    backendRefs:
      - name: api-service
        port: 8080
  - matches:
      - path:
          type: PathPrefix
          value: /admin
    backendRefs:
      - name: admin-service
        port: 8080

Esto es excelente para dividir sub-paths bajo un mismo dominio — por ejemplo:

  • /api va al servicio de API
  • /admin va al backend de administración

La lógica de enrutamiento es inmediatamente visible. A diferencia de algunos archivos de configuración heredados donde lees hasta la mitad y empiezas a dudar de tu propia existencia.

Header, Method y Query como Condiciones

HTTPRoute no se limita a la coincidencia por path — también puede hacer coincidir por headers de solicitud, métodos y parámetros de query.

Por ejemplo, solo enrutar solicitudes con version: v2 al nuevo servicio:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: versioned-route
spec:
  parentRefs:
    - name: eg
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
          headers:
            - name: version
              value: v2
      backendRefs:
        - name: api-v2
          port: 8080
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-v1
          port: 8080

Este patrón funciona bien para:

  • Lanzamientos canary
  • Permitir que clientes específicos prueben nuevas versiones antes
  • Validación manual de nuevas características

Comparado con cambiar a un dominio completamente nuevo, el enrutamiento basado en headers es más granular y más fácil de experimentar.

filters: Procesar Solicitudes Antes de Reenviarlas

HTTPRoute no solo "hace coincidir y reenvía" — también puede aplicar transformaciones antes o después del reenvío. Para eso están los filters.

Un caso de uso común es agregar headers:

rules:
  - filters:
      - type: RequestHeaderModifier
        requestHeaderModifier:
          add:
            - name: x-env
              value: prod
    backendRefs:
      - name: api-service
        port: 8080

Esto es útil para:

  • Inyectar metadatos de rastreo antes de reenviar al backend
  • Agregar headers fijos en la capa de ingreso
  • Transformaciones de solicitud/respuesta

Dicho esto, los filters son poderosos pero no son un pase libre para convertir la capa de ingreso en un blob gigante de middleware. Demasiada lógica acumulada en las rutas hará que depurar se sienta como si necesitara su propia sesión de terapia.

División de Tráfico Ponderada: El Patrón Canary Más Común

Para enviar el 90% del tráfico a la versión anterior y el 10% a la nueva, usa weight:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "app.example.com"
  rules:
    - backendRefs:
        - name: api-v1
          port: 8080
          weight: 90
        - name: api-v2
          port: 8080
          weight: 10

Este es el canary básico / división de tráfico.

No necesitas un service mesh completo solo para ejecutar un experimento de tráfico. Para muchos servicios de API, esto es más que suficiente — y es mucho más limpio de configurar.

💡 weight es relativo, no es necesario que sume 100. 9 y 1 expresan la misma proporción que 90 y 10.

timeouts: No Dejes que las Solicitudes Queden Colgadas para Siempre

HTTPRoute admite configuración de timeout a nivel de regla. Esto importa — si la capa de ingreso no tiene límites, las solicitudes malas se quedan indefinidamente como un cliente maleducado que no se quiere ir.

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /reports
    timeouts:
      request: 10s
      backendRequest: 2s
    backendRefs:
      - name: report-service
        port: 8080

Entendiendo los dos campos:

  • request: Tiempo máximo de espera total para la solicitud del cliente
  • backendRequest: Espera máxima para una solicitud individual al backend

Ten en cuenta que backendRequest no debería exceder request — la lógica sería como decir "el viaje completo toma 10 minutos, pero un tramo en autobús puede esperar 20 minutos." El universo se confunde.

Dos Matices Comunes

1. parentRefs Puede Apuntar a un Listener Específico

Si un Gateway tiene múltiples listeners, puedes vincularte solo a uno:

parentRefs:
  - name: eg
    sectionName: http

Esto significa que la ruta solo se adjunta al listener http. En configuraciones con múltiples listeners, esto es mucho más predecible que adjuntarse a todo.

2. Sin matches = Coincide con Todo

Si una regla no tiene matches, por defecto coincide con / — esencialmente un catch-all.

Esto es conveniente, pero puede silenciosamente absorber lo que pensabas que eran reglas más específicas. No lo omitas por pereza y luego pases una hora preguntándote por qué tus reglas precisas no se están disparando.

Verificando que el Enrutamiento Funciona

Después de configurar las rutas, valida con curl:

curl -H "Host: app.example.com" http://$GATEWAY_HOST/api/users
curl -H "Host: app.example.com" -H "version: v2" http://$GATEWAY_HOST/api/users

Para revisar el status de HTTPRoute:

kubectl get httproute app-route -o yaml

Presta especial atención a:

  • status.parents
  • La condición Accepted

A veces el YAML parece correcto, pero el Gateway lo rechazó. Es como enviar una solicitud de empleo y asumir que conseguiste el trabajo porque no recibiste respuesta.

Resumen en Una Línea

El núcleo de HTTPRoute es "primero hacer coincidir, luego reenviar." Una vez que tienes cubiertos host, path, header y weight, puedes manejar alrededor del 80% de los requisitos de ingreso HTTP del día a día.

Siguiente Paso

El próximo artículo aborda algo que encontrarás en cada entorno de producción: HTTPS, certificados, terminación TLS y seguridad: 👉 TLS y Seguridad