19 puntos por xguru 2020-08-10 | 3 comentarios | Compartir por WhatsApp
<p>Un artículo que explica muy bien las ventajas de Envoy frente a Nginx desde la experiencia de Dropbox, que maneja decenas de millones de conexiones concurrentes, cientos de millones de requests por segundo y ancho de banda a escala de terabytes.<br /> <br /> Antes: nginx (versión open source) + python2 + Jinja2 + YAML <br /> → aunque cambiara una sola cosa, había que volver a desplegar todo<br /> → las partes dinámicas se desarrollaban con Lua<br /> → la lógica compleja se procesaba en Bandaid, un proxy basado en Go<br /> <br /> Funcionó bien durante casi 10 años, pero ya no encajaba bien con el entorno actual.<br /> → las APIs internas y externas (privadas) están migrando gradualmente de REST a gRPC, así que se necesita capacidad de transcoding en el proxy<br /> → Protocol Buffers se convirtió en el estándar interno para definir servicios <br /> → todo el software se compila y prueba con Bazel sin importar el lenguaje<br /> → empleados de la empresa participan bastante activamente en las comunidades open source de los principales proyectos de infraestructura<br /> <br /> Nginx también era costoso de mantener desde el punto de vista operativo.<br /> → la lógica de generación de configuración es demasiado flexible y está repartida entre YAML, Jinja2 y Python<br /> → el monitoreo es una mezcla de Lua / parsing de logs / monitoreo basado en sistema<br /> → al aumentar la dependencia de módulos de terceros, se afectaban la estabilidad y el rendimiento, y además se generaban costos por upgrades frecuentes <br /> → el despliegue y la gestión de procesos de nginx son muy distintos a los de otros servicios; depende mucho de cosas ajenas al sistema base como syslog, logrotate, etc.<br /> <br /> Por eso decidieron buscar un reemplazo para Nginx por primera vez en 10 años.<br /> <br /> * ¿Por qué no migrar a Bandaid (el proxy basado en Go desarrollado por Dropbox)? * <br /> → Go consume más recursos que C/C++. <br /> → el stack TLS de Go no soporta FIPS (Federal Information Processing Standards de EE. UU.)<br /> → al ser una herramienta interna, no puede apoyarse en una comunidad externa <br /> <br /> Ahora: migración hacia una infraestructura de tráfico basada en Envoy <br /> <br /> ----- Por qué Envoy resultó mejor que Nginx ------<br /> <br /> * Rendimiento *<br /> <br /> La arquitectura de Nginx es event-driven y multiproceso. Soporta SO_REUSEPORT y EPOLLEXCLUSIVE.<br /> Aunque está basada en event loop, no es completamente non-blocking. Al abrir archivos o escribir logs, el event loop puede detenerse. (Incluso habilitando aio, aio_write y threadpool).<br /> Esto provoca tail latency y, a veces, retrasos de varios segundos.<br /> <br /> Envoy también usa una arquitectura similar basada en eventos, pero basada en threads en lugar de procesos. <br /> Soporta SO_REUSEPORT (con soporte de filtros BPF) y event loop a través de libevent. <br /> No hay blocking I/O en el event loop. El event logging también está implementado de forma non-blocking.<br /> <br /> En teoría parecía que ambos tendrían características de rendimiento parecidas, y en la práctica también fueron similares en la mayoría de las pruebas de cargas de trabajo.<br /> Sin embargo, Nginx mostró más latencia en la cola larga. La razón era que el event loop se detenía cuando había mucho I/O.<br /> <br /> Sin recolección de estadísticas, Nginx rendía de forma parecida a Envoy, pero la herramienta interna de recolección de métricas hecha en Lua hacía que Nginx fuera 3 veces más lento en pruebas de alto RPS. (Esto se debía a `lua_shared_dict`, sincronizado con mutex). Aunque había problemas con la forma en que Dropbox hacía esa recolección, decidieron no reescribirla eficientemente. (Esperaban que instrumentar dentro de Nginx haría más difícil actualizarlo en el futuro).<br /> <br /> En fin, como estos problemas no existían en Envoy, después de la migración pudieron liberar hasta el 60% de los servidores que usaba la infraestructura anterior basada en Nginx.<br /> <br /> * Observabilidad *<br /> <br /> La versión gratuita de Nginx solo ofrece 7 estadísticas mediante el módulo stub status. <br /> Obviamente eso no era suficiente, así que agregaban un handler `log_by_lua` para exponer más métricas.<br /> También existía un parser de `error.log` para exportar información de errores, además de un exporter aparte para exponer estados internos de nginx.<br /> <br /> Una instalación básica de Envoy entrega miles de métricas distintas en formato Prometheus. <br /> Desde información del tráfico del proxy hasta estados internos del servidor,<br /> incluyendo estadísticas por clúster / upstream / virtual host, y también métricas downstream TCP/HTTP/TLS por listener, etc.<br /> <br /> Junto con toda esta variedad de estadísticas, Envoy permite conectar providers de tracing como plugins.<br /> Eso resulta útil no solo para el equipo de tráfico, sino también para los desarrolladores de aplicaciones.<br /> <br /> Por último, Envoy puede hacer streaming de access logs a través de gRPC.<br /> Eso reduce la carga de mantener el bridge syslog-to-hive del equipo de tráfico.<br /> Ejecutar un servicio gRPC convencional es mucho más fácil y seguro que conectar listeners TCP/UDP personalizados.<br /> <br /> * Integración *<br /> <br /> La integración de Nginx es muy al estilo Unix. La configuración es muy estática.<br /> Depende de archivos para el config, certificados TLS, allowlist/blocklist, etc.<br /> Es simple y compatible hacia atrás, así que se puede automatizar con unos cuantos shell scripts,<br /> pero a medida que el sistema crece, la capacidad de prueba y la estandarización se vuelven cada vez más importantes.<br /> <br /> Envoy tiene su propia forma de abordar esta integración.<br /> Ofrece una API llamada xDS que fomenta el uso de protobuf y gRPC.<br /> Envoy consulta ese xDS para descubrir recursos dinámicos.<br /> <br /> - Este xDS ya está evolucionando más allá de Envoy para intentar convertirse, bajo el nombre de Universal Data Place API (UDPA), en el estándar de facto para balanceadores de carga L4/L7; según nuestra experiencia, va por buen camino. Incluso están intentando usar UDPA también en Katran, el balanceador L4 basado en eBPF/XDP, no solo en Envoy.<br /> <br /> Como Dropbox ya integra internamente sus servicios vía gRPC, esto les resulta mucho mejor.<br /> <br /> * Configuración *<br /> <br /> Nginx tiene una gran ventaja: sus archivos de configuración son fáciles de leer para humanos. <br /> Pero esa ventaja se va perdiendo conforme la configuración se vuelve más compleja y pasa a generarse automáticamente.<br /> Como en Dropbox se generaba con Python2, Jinja2 y YAML, el modelo de datos terminó enredándose y volviéndose complejo.<br /> <br /> Envoy tiene un modelo de datos unificado para la configuración. Todos los valores de configuración están definidos en Protocol Buffer. Eso resuelve problemas de modelado de datos y añade información de tipos a la configuración.<br /> Como protobuf ya se usa mucho dentro de Dropbox, la integración es más fácil. <br /> <br /> * Extensibilidad * <br /> <br /> Para extender Nginx hay que escribir módulos en C. Para hacerlo de manera segura se necesitan desarrolladores senior. También ofrece interfaces en Perl / JS para módulos más ligeros, pero son muy limitadas. Por eso, el método más común es usar `lua-nginx-module`. <br /> <br /> El principal mecanismo de extensión de Envoy es mediante plugins en C++; la documentación no está tan bien trabajada como la de nginx, pero es bastante simple. Eso se debe a una interfaz limpia y bien comentada, además de C++14 y la biblioteca estándar. <br /> <br /> Una gran diferencia de Envoy frente a otros web servers es su soporte para WebAssembly (WASM).<br /> Eso permite desarrollar extensiones en varios lenguajes, como Rust. <br /> Dropbox aún no usa WASM, pero eso podría cambiar algún día si llega soporte para Go SDK en proxy-wasm.<br /> <br /> * Building and Testing *<br /> <br /> Nginx usa por defecto configuración personalizada basada en shell y un build basado en make. Es simple y excelente, pero integrarlo en un monorepo compilado con Bazel requiere bastante esfuerzo. <br /> Nginx tiene pruebas de integración basadas en Perl, pero no tiene unit tests.<br /> <br /> Envoy ya usa un build system basado en Bazel, y se integró fácilmente en su monorepo.<br /> También soporta unit tests basados en gtest/gmock y un framework de integration tests.<br /> <br /> * Seguridad *<br /> <br /> El código de Nginx es muy pequeño y también tiene pocas dependencias externas, así que no presenta demasiadas vulnerabilidades de seguridad.<br /> <br /> Envoy tiene mucho más código, así que naturalmente parece tener más superficie de ataque. Para compensarlo, Envoy depende bastante de prácticas modernas de seguridad como AddressSanitizer, ThreadSanitizer y MemorySanitizer. <br /> <br /> * Funcionalidades * <br /> <br /> Tomen esta parte como referencia, porque incluye bastante opinión subjetiva.<br /> <br /> Nginx comenzó como un web server para servir archivos estáticos con muy pocos recursos. <br /> Es decir, sus funciones principales son static serving, caching y range caching.<br /> Desde la perspectiva de proxy, a Nginx le faltan muchas funciones que hoy exige la infraestructura moderna. <br /> No puede conectarse por HTTP/2 a backends, tampoco manejar proxy gRPC multiplexado, ni hacer gRPC transcoding, etc.<br /> Además, su modelo de licencia open core deja algunas funciones importantes fuera de la “community version”.<br /> <br /> Envoy en cambio nació desde el principio como un proxy de ingress/egress, y se usa mucho en entornos con alta carga de gRPC.<br /> Sus funciones de web service son muy básicas. No puede servir archivos, el caching todavía está en desarrollo y tampoco soporta brotli. <br /> Para este tipo de entornos, también usan una configuración de Nginx con Envoy como upstream cluster. <br /> Esperan que cuando Envoy pueda hacer HTTP cache, también se puedan migrar esos entornos de serving estático. <br /> <br /> Envoy ofrece muchas funciones relacionadas con gRPC:<br /> - gRPC proxying<br /> - HTTP/2 hacia backends<br /> - puente gRPC → HTTP (+ reversa) <br /> - gRPC-WEB <br /> - transcoder de gRPC JSON<br /> <br /> Además, Envoy también puede usarse como outbound proxy. <br /> - Egress Proxy<br /> - service discovery de software de terceros con la librería gRPC Courier <br /> <br /> * Comunidad *<br /> <br /> El desarrollo de Nginx es centralizado y en su mayoría oculto. <br /> El desarrollo de Envoy es abierto y descentralizado. Se lleva por GitHub issues/PR y también es muy activo por mailing lists/Slack, etc.</p><p>----- Estado actual de la migración en Dropbox -----<br /> <br /> Han operado Nginx y Envoy en paralelo durante medio año, migrando gradualmente el tráfico a través de DNS. <br /> La migración no estuvo completamente libre de problemas; hubo incidentes menores, pero no se presentaron caídas graves.<br /> También recopilaron soluciones a problemas causados por comportamientos “unusual” o “non-RFC” (consulten el artículo original para los detalles).<br /> <br /> ** Próximos pasos **<br /> <br /> - HTTP/3: Envoy también comenzó a soportarlo de forma experimental. Planean probarlo cuando actualicen el kernel de Linux para acelerar UDP<br /> - balanceador interno basado en xDS y Outlier Detection<br /> - extensiones de Envoy basadas en WASM <br /> - reemplazar Bandaid (proxy basado en Go) por Envoy <br /> - aplicar Envoy Mobile también en apps móviles</p>

3 comentarios

 
baeba 2020-08-13
<p>Buen contenido, resumido de forma sencilla. <br /> Gracias por organizarlo<br /> y compartirlo.</p>
 
before30 2020-08-11
<p>Gracias. :)</p>
 
loslch 2020-08-11
<p>La explicación detallada y hasta los comentarios amables me ayudaron muchísimo a entenderlo. Gracias :)</p>