Envoy es un proxy de capa 7 de alto rendimiento, escrito en C++ y graduado por la CNCF, que se convirtió en la pieza de infraestructura sobre la que se construyen la mayoría de los service mesh modernos. Nacido en Lyft para resolver los problemas de red de una arquitectura de cientos de microservicios, hoy es el data plane de Istio, la base de gateways como Contour y Gloo, y el motor de balanceo de plataformas enteras. Entender Envoy es entender cómo viaja el tráfico en las arquitecturas cloud native actuales.

El problema que resuelve: la red ya no es confiable ni simple

En un monolito, una llamada entre módulos es una llamada de función: instantánea y confiable. Al partirlo en microservicios, cada una de esas llamadas se convierte en una request de red que puede fallar, demorarse o llegar duplicada. La primera generación de soluciones metió esa lógica en librerías dentro de cada aplicación (Hystrix, Ribbon), con un costo enorme: cada lenguaje necesitaba su implementación, cada actualización requería redeployar todo, y los equipos reimplementaban retries y timeouts de formas sutilmente distintas.

Envoy propone lo contrario: sacar toda esa lógica de la aplicación y ponerla en un proxy que corre al lado de cada servicio (el patrón sidecar). La aplicación habla con “localhost” y el proxy se encarga del resto: descubrimiento de servicios, balanceo, reintentos, timeouts, circuit breaking, TLS y telemetría. El resultado es que la lógica de red es uniforme, se actualiza sin tocar aplicaciones y funciona igual para un servicio en Go que para uno en Node o Python.

Arquitectura de Envoy: los conceptos que hay que dominar

La configuración de Envoy se estructura en cuatro bloques que conviene tener claros porque aparecen en cualquier debugging:

  • Listeners: dónde escucha el proxy (puerto/protocolo de entrada). Un listener acepta conexiones y les aplica una cadena de filtros.
  • Filters: la unidad de procesamiento. El más usado es el HTTP connection manager, que interpreta HTTP/1.1, HTTP/2 y gRPC. Acá viven también rate limiting, autenticación externa y filtros WASM propios.
  • Routes: las reglas que deciden a qué destino va cada request según host, path, headers o peso (la base de los canary releases).
  • Clusters: los destinos de tráfico (grupos de endpoints), con su política de balanceo, health checks, límites de conexiones y configuración TLS.

La pieza que hace especial a Envoy es la xDS API (discovery service): toda esta configuración puede actualizarse dinámicamente vía API, sin reiniciar el proxy ni cortar conexiones. Un control plane le empuja a cada Envoy sus listeners, rutas, clusters y endpoints en caliente. Esta API es exactamente lo que un service mesh explota.

# Configuración estática mínima: escuchar en :8080 y rutear todo a un cluster
static_resources:
  listeners:
  - address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: servicio_pagos, timeout: 3s, retry_policy: { retry_on: "5xx", num_retries: 2 } }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: servicio_pagos
    type: STRICT_DNS
    lb_policy: LEAST_REQUEST
    load_assignment:
      cluster_name: servicio_pagos
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: pagos.internal, port_value: 8000 }

Ese fragmento ya muestra el valor operativo: timeout y retries declarados en el proxy, no en el código de la aplicación.

De Envoy al service mesh: qué agrega Istio

Un Envoy solo es un proxy excelente. Un service mesh es el resultado de poner un Envoy al lado de cada workload y coordinarlos todos desde un control plane. En Istio, el control plane (istiod) hace tres cosas: traduce la configuración de alto nivel (VirtualServices, DestinationRules) a xDS y se la empuja a cada sidecar; emite y rota los certificados de cada workload; y agrega la identidad de servicio sobre la que se montan las políticas.

Lo que se obtiene, sin tocar una línea de código de aplicación:

  • mTLS automático entre todos los servicios: cada pod recibe una identidad criptográfica (SPIFFE) y el tráfico este-oeste viaja cifrado y autenticado. El requisito de “cifrado en tránsito interno” de cualquier auditoría se resuelve por plataforma.
  • Gestión de tráfico fina: canary por porcentaje, routing por headers, mirroring de tráfico a una versión nueva, fault injection para probar resiliencia.
  • Resiliencia uniforme: timeouts, retries con backoff, circuit breaking y outlier detection (expulsar del pool a los endpoints que fallan) declarados por servicio.
  • Observabilidad homogénea: métricas RED (rate, errors, duration) por servicio, trazas distribuidas y logs de acceso, generados por el proxy con el mismo formato para todo el mesh.
# Canary con Istio: 90/10 entre versiones, sin tocar la aplicación
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: pagos
spec:
  hosts: ["pagos"]
  http:
  - route:
    - destination: { host: pagos, subset: v1 }
      weight: 90
    - destination: { host: pagos, subset: v2 }
      weight: 10

El costo: cuándo un mesh se justifica y cuándo es sobreingeniería

El service mesh no es gratis y decirlo es parte de saber usarlo. Los costos reales: un contenedor extra por pod (CPU/RAM del sidecar), 1-3 ms de latencia adicional por salto, y sobre todo complejidad operativa: el mesh es un sistema distribuido más que hay que actualizar, monitorear y saber depurar. Un istioctl proxy-status y leer configuración xDS pasan a ser habilidades del equipo.

La regla práctica: con pocos servicios y un equipo chico, no se justifica; timeouts en el cliente HTTP, NetworkPolicies y un buen ingress cubren la mayoría de las necesidades. El mesh empieza a pagar cuando hay decenas de servicios, múltiples equipos y requisitos duros de mTLS/autorización o de gestión de tráfico avanzada. Y el punto medio moderno existe: Istio ambient mode elimina el sidecar por pod (proxies compartidos por nodo), reduciendo el costo de recursos, y Cilium ofrece parte de estas capacidades desde eBPF en el kernel. Para quien solo necesita un buen gateway de entrada, un Envoy como edge proxy (o Contour/Emissary) da el 80% del valor sin operar un mesh completo.

Envoy en la práctica: debugging esencial

Tres herramientas cubren la mayoría de los problemas reales:

# 1. Admin interface de Envoy (puerto 15000 en Istio): el estado real del proxy
kubectl exec deploy/pagos -c istio-proxy -- pilot-agent request GET config_dump | less
kubectl exec deploy/pagos -c istio-proxy -- pilot-agent request GET clusters | grep health

# 2. Estado de sincronización del mesh: ¿los sidecars tienen la config al día?
istioctl proxy-status

# 3. Analizar por qué una request no llega: rutas efectivas de un pod
istioctl proxy-config routes deploy/pagos --name 8080 -o json

Los síntomas clásicos y su causa: errores 503 con UF (upstream failure) en los logs de acceso suelen ser endpoints unhealthy o mTLS a medio configurar; NR (no route) es una VirtualService que no matchea; latencias que aparecen de golpe suelen ser retries amplificando un backend degradado, visible en las métricas upstream_rq_retry.

Preguntas frecuentes sobre Envoy y service mesh

¿Envoy reemplaza a nginx o HAProxy?

En el rol de edge/ingress compiten y los tres son válidos. La ventaja de Envoy está en la configuración dinámica vía xDS, el soporte gRPC/HTTP2 de primera clase y la telemetría integrada, que lo hacen el estándar para tráfico este-oeste y para plataformas que necesitan reconfiguración en caliente.

¿Necesito Istio para usar Envoy?

No. Envoy funciona standalone como edge proxy o API gateway con configuración estática o un control plane simple (go-control-plane). Istio se justifica cuando querés mesh completo: mTLS, políticas y tráfico avanzado entre muchos servicios.

¿Qué overhead real agrega un sidecar?

Como orden de magnitud: decenas de MB de RAM y una fracción de core por sidecar en cargas típicas, y 1-3 ms por salto. En un mesh grande el costo agregado es real y es el argumento principal de ambient mode y de las alternativas basadas en eBPF.

¿Cómo se relaciona esto con Kubernetes Ingress y Gateway API?

Gateway API es la evolución del Ingress y varios de sus implementadores (Istio, Contour, Envoy Gateway) usan Envoy como data plane. La combinación natural moderna: Gateway API para el tráfico norte-sur y el mesh para el este-oeste, con Envoy debajo de ambos.

Conclusión

Envoy resolvió el problema correcto en el lugar correcto: sacó la lógica de red de las aplicaciones y la volvió infraestructura uniforme, dinámica y observable. Sobre esa base, el service mesh agrega identidad, mTLS y gestión de tráfico a escala de plataforma, a cambio de un costo operativo que solo se justifica con suficiente escala y requisitos. La recomendación práctica: dominar Envoy primero (como ingress o gateway), medir si los problemas que un mesh resuelve son los que realmente tenés, y recién entonces adoptar Istio, con ambient mode como el punto de entrada moderno. Para profundizar en el ecosistema, seguí con la guía de Istio y las bases de Kubernetes en producción.