Entendiendo el DNS round robin
(blog.hyperknot.com)- El operador de OpenFreeMap agrupó varios VPS de distintas regiones en registros A del mismo subdominio para configurar DNS round robin, y probó qué servidor terminan eligiendo realmente el navegador y Cloudflare
- Sin un balanceador de carga independiente, se puede esperar distribución de carga y evasión de fallos, pero el resultado real depende mucho de cómo el cliente ordena las direcciones y reintenta
- En pruebas con 3 VPS en EE. UU., Europa y Singapur, Chrome y Firefox tendieron a fijar un servidor aleatorio al inicio, mientras que Safari y curl convergieron al servidor de la UE más cercano tras varias solicitudes
- Si algunos servidores están fuera de línea, los navegadores y curl cambian rápido a un servidor alternativo, pero las solicitudes que pasan por el proxy de Cloudflare pueden seguir usando el origin asignado por IP del cliente y provocar errores 521
- Si Cloudflare no elige correctamente un origin fuera de línea o el servidor con menor latencia, la distribución basada en DNS round robin puede terminar conectando a un servidor lento sin relación con la ubicación del usuario
La idea básica del DNS round robin
- Un sitio web típico basado en VPS agrega un solo registro A en el proveedor de DNS para enviar tráfico a una IP específica
- El DNS round robin consiste en asignar varias IP de servidor al mismo subdominio
- El ejemplo usa una configuración con varios registros A para
rr-direct.hyperknot.comyrr-cf.hyperknot.com
- El ejemplo usa una configuración con varios registros A para
- Con esta configuración, se puede esperar repartir la carga entre varios servidores y evitar los que estén fuera de línea
- En la mayoría de los proveedores de DNS, esto puede configurarse sin un balanceador de carga aparte, por lo que es un enfoque simple y casi gratuito
- Las funciones de balanceo de carga de servicios como Cloudflare pueden resultar costosas
Con qué criterio puede elegir servidor el cliente
- Como estándares relacionados aparecen RFC 8305 Happy Eyeballs y RFC 6724
- La sección de ordenamiento de direcciones de RFC 8305 explica que, si un cliente con estado mantiene un historial del tiempo estimado de ida y vuelta (RTT) para cada ruta de dirección, debería agregar reglas de selección de destino que prefieran direcciones con menor RTT
- El experimentador interpreta esto como el siguiente comportamiento
- Verificar si el servidor está en línea o fuera de línea
- Ordenar los servidores en línea según el tiempo de ping
Configuración del experimento
- Se crearon VPS en 3 regiones del mundo
- EE. UU.
- Europa
- Singapur
- En Cloudflare se configuraron 3 registros A con proxy y 3 registros A sin proxy
- Cada servidor entrega la misma estructura de respuesta con nginx
- Todas las solicitudes de ruta se reescriben a
color.png /serverdevuelve/etc/hostnamecomotext/plain
- Todas las solicitudes de ruta se reescriben a
color.pnges un archivo PNG de 1 px y cada servidor tiene un color distinto- US: verde
- EU: azul
- SG: rojo
- Los nombres de host se distinguen como
test-eu,test-us,test-sg - Como la prueba se hace desde Europa, el comportamiento esperado es que se elija el servidor de la UE más cercano
- La página HTML de prueba llena una cuadrícula 10x10 con imágenes aleatorias para visualizar el resultado de la selección de servidor
Comportamiento por cliente cuando todos los servidores están en línea
- Chrome tiende a elegir una ubicación algo aleatoria entre varias y luego quedarse en ese servidor una vez elegido
- Reevalúa la elección después de algunas horas
- En la prueba, incluso llegó a quedarse fijado por horas en el servidor más lento de Singapur
- Cuando no usa HTTP/2, a veces elige aleatoriamente entre dos servidores y forma patrones
- Firefox se comporta de forma similar a Chrome
- Elige una ubicación aleatoria al inicio
- Si se reinicia el navegador, puede seleccionar otra ubicación aleatoria
- Safari siempre elige correctamente el servidor más cercano
- Aunque el servidor quede fuera de línea por un momento y luego vuelva, tras unos cuantos refrescos vuelve a encontrar el servidor de la UE
- curl también se corrige hacia el servidor cercano
- Puede que no ocurra en la primera ejecución, pero si se ejecuta el comando dos veces, siempre termina moviéndose al servidor más cercano
- En el ejemplo, la primera solicitud fue a
test-usy la siguiente cambió atest-eu
Comportamiento al pasar por el proxy de Cloudflare
- Cloudflare elige una ubicación aparentemente aleatoria según la IP del cliente y luego sigue usando siempre esa misma ubicación
- El comportamiento observado se parece a
client_ip_hash modulo server_num - En la IP de casa, sin importar qué se hiciera, Cloudflare conectaba al servidor de EE. UU.
- El resultado de
curl https://rr-cf.hyperknot.com/serverdevuelvetest-us
- El resultado de
- En un hotspot móvil, siempre conectaba al servidor de la UE
- Al ejecutar el mismo comando curl desde varios VPS, cada VPS se conecta a una ubicación aleatoria en el mundo, pero siempre usa el mismo servidor
- El resultado de ejemplo es
test-sg
- El resultado de ejemplo es
Diferencias cuando algunos servidores están fuera de línea
- Se verificó el comportamiento después de detener nginx en el servidor de EE. UU. con
service nginx stop - Chrome, Firefox, Safari y curl detectan el servidor fuera de línea y eligen otro
- Incluso si el servidor se apaga durante la carga, la conexión alternativa actúa lo bastante rápido como para corregirse en menos de 1 segundo
- Cloudflare no detecta el servidor fuera de línea
- Sigue intentando acceder al servidor asignado una vez para esa IP de cliente, sin importar si está en línea o no
- Si ese servidor está fuera de línea, el usuario recibe un error
- El resultado de curl es
error code: 521
Dudas y límites sobre Cloudflare
- Se considera que el comportamiento de Cloudflare al no detectar un origin fuera de línea probablemente sea un bug de la red
- Basándose en la documentación de Cloudflare sobre zero downtime failover, se concluye que debería comportarse como los navegadores y curl
- Como mínimo, debería detectar los servidores fuera de línea
- Sería aún mejor si pudiera elegir el servidor con menor latencia, como Safari
- Con el comportamiento actual, si hay 1 servidor en EE. UU. y 1 en Nueva Zelanda, el 50% de los usuarios de EE. UU. podrían recibir respuesta desde el servidor de Nueva Zelanda
- Los usuarios de Safari podrían obtener peor rendimiento usando Cloudflare que sin usarlo
- En la discusión relacionada en HN, respondieron el CEO y el CTO de Cloudflare
- También se pregunta si existe una plataforma serverless que permita seguir operando el experimento con HTTPS y DNS round robin sin costo por 3 VPS repartidos por el mundo
1 comentarios
Opiniones en Hacker News
Mmm, ya le pedí al equipo de DNS autoritativo que explique qué está pasando aquí
Cuando tenga una respuesta clara, la compartiré en HN. Hace años que no veo el código, y mucha gente lo ha seguido cambiando desde entonces :-)
Mi suposición es que tiene que ver con mantener una preferencia entre la IP del cliente y el servidor backend, como mencionó el autor en el blog. La pregunta clave es: “si el servidor backend se cae, ¿hay que romper esa preferencia?”. Si averiguo más, lo dejaré como respuesta a mi comentario
Una de las primeras soluciones a este problema fueron los registros DNS SRV. Son parecidos a los registros MX, pero pensados para aplicar no solo al correo sino a cualquier servicio
En los registros MX y SRV, el cliente puede especificar la lista de servidores que debe probar y su prioridad, y SRV también tenía un parámetro
weightpara balanceo de carga. Pero SRV, en la práctica, quedó limitado a usarse solo cuando el estándar de ese protocolo indicaba explícitamente el uso de SRV, para evitar una pelea política por redefinir todos los protocolos estándar y obligar a todos los clientes a consultar SRV. Así que, técnicamente, los clientes HTTP no pueden usar SRV. Más tarde, cuando se crearon HTTP/2 y los estándares HTTP posteriores, tampoco se pudo especificar SRV para los nuevos protocolos HTTP, por una lógica deficiente impulsada por Google y otros. SRV está prácticamente muerto para desarrollos nuevos y parece usarse solo en algunos estándares viejosLa nueva solución de balanceo de carga parece ser los registros DNS HTTPS y SVCB. Según entiendo, fueron estandarizados por gente que quería meter parámetros adicionales en DNS para poder iniciar antes el handshake de TLS 1.3 y reducir la cantidad de viajes de ida y vuelta. El tipo de registro SVCB es igual que HTTPS, pero en una forma generalizada como SRV. HTTPS y SVCB tienen los parámetros de prioridad de SRV y MX, pero no el parámetro
weightde SRV. El estándar ya fue publicado y parece que algunos navegadores ya lo soportan, aunque no todos lo han activado. Habrá que ver qué hacen realmente los navegadores en un futuro cercanoNo requieren el truco de CNAME flattening, que puede causar problemas de enrutamiento en CDNs que usan GeoDNS junto con anycast o en lugar de este. Si alguna vez viste que una plataforma recomienda usar el subdominio
wwwen vez del dominio apex, es por esto, y también es una de las razones por las que Akamai impulsó la estandarización de los registros HTTPS, ya que usa GeoDNSDuele especialmente que no existan, porque mucha gente quiere alojar sitios web en el apex del dominio. Aun así, puede ser complicado usar registros estilo MX de forma segura si no se puede depender de DNSSEC
El balanceo de carga por DNS tiene casos límite realmente sucios. Me tocó lidiar con una situación donde un cliente Go HTTP/2 usaba DNS round-robin y hubo problemas
Los clientes Go HTTP/2 siguen reutilizando el primer servidor al que logran conectarse y no resuelven DNS otra vez. Por eso, aunque agregues servidores nuevos al pool, puede que los clientes nunca los descubran
Un caso especialmente patológico es cuando todos los backends se caen y luego vuelve uno solo, el primero: todos los clientes se quedan pegados a ese servidor y no se mueven. Aunque suban otros servidores, casi no habrá clientes nuevos que se conecten a ellos, porque ya están conectados al primero
Algo parecido pasa en
grpc-go. El resolvedor DNS de gRPC solo vuelve a resolver cuando se corta la conexión con el backend. Entonces los clientes gRPC pueden concentrarse todos en un solo host y quedarse ahí. También se ha propuesto configurarMAX_CONNECTION_AGEdel lado del servidor para desconectar clientes periódicamente después de cierto tiempo y forzar que vuelvan a resolver DNSOjalá hubiera una mejor solución estándar para descubrimiento de servicios. Al final, parece que lo mejor que se puede hacer es implementar un balanceador por solicitud basado en IP virtual y dejar que ese balanceador haga health checks. Pero incluso eso solo empuja el problema hacia el sistema que implementa la IP virtual. Parece que se asume que el sistema de enrutamiento es relativamente más estático que los backends, y que ahí se obtiene alguna ventaja
Me pregunto cómo se hace esto en bare metal. Sé que AWS/GCP y demás tienen balanceadores internos, pero me da curiosidad cuál es el truco para implementarlo. También sirven recomendaciones de posts de blog o whitepapers relacionados
Dice “¿qué pasa si un servidor queda offline? Supongamos que detenemos el servidor de EE. UU.:
service nginx stop”, pero esa no es una buena forma de probarloEl cliente verá una conexión rechazada y pasará a la siguiente IP. Pero en la vida real, el servidor puede simplemente no responder en absoluto, o aceptar la conexión y quedarse en silencio
En ese punto dependes del timeout del cliente, y el DNS round-robin, que se supone que mejora la confiabilidad, de repente se vuelve mucho menos atractivo
Detener el servicio es una tarea planificada, y en ese caso primero puedes actualizar DNS para manejarlo
SIG_STOPo unDROPdeip/nftablesson pruebas mucho más realistasComo pueden ver, “todos los clientes detectan esto correctamente y eligen un servidor alternativo” es la parte problemática del asunto. La confiabilidad se decide del lado del cliente
Por ejemplo,
systemd-resolveden algún momento, argumentando que se comportaba de la forma más técnicamente correcta posible, siempre devolvía la dirección IP más baja. Como el round robin de DNS no está bien definido, la lógica era que devolver siempre la IP más baja tampoco era incorrecto. Después del revuelo eso cambió, pero hasta donde sé Debian 11 seguía atado a ese comportamiento, o lo estuvo durante mucho tiempoTambién lidiamos con muchas aplicaciones cuyo comportamiento de reintento es pésimo o directamente inexistente. Funcionan como: “hubo un rechazo de conexión, cancelemos todo, salgamos y no volvamos a intentarlo nunca”. Entonces el 20~30% de todas las solicitudes se va en humo
Si no hay otra opción, puede ser una solución aceptable. Como dice el artículo, si tienes un cliente HTTP de buena calidad con varios reintentos configurados, como un navegador, el round robin de DNS puede servir para encontrar un balanceador de carga real con comprobaciones de estado, etc., y puede ofrecer una tasa de éxito del 100%
Pero el round robin de DNS no es un balanceador de carga, y un balanceador de carga es mejor
En un lugar donde trabajé antes había un servidor DNS interno con cientos de millones de registros y un TTL de 60 segundos, y se usaba en un sistema interno de enrutamiento personalizado que conectaba las conexiones entrantes de los clientes con el recurso correcto dentro de la red. En la práctica era excelente. Cambiar rutas era tan simple como una actualización DDNS, y con
NOTIFYse empujaban los cambios a todos los servidores secundarios, así que la latencia promedio hasta la propagación completa era menor a 60 segundos. Gracias a eso fue fácil crear herramientas más complejas, e incluso hicimos un panel de control para sacar de servicio desde un solo servidor hasta un centro de datos completo con solo presionar un botónEse sistema también tenía asperezas, por supuesto, pero para un sistema de ese tipo era rápido, fácil de inspeccionar y relativamente cercano a ser a prueba de balas
Lo mismo pasa con la conmutación por error. Si una región se cae, ¿quieres que el tráfico se distribuya uniformemente entre las demás regiones, o que se concentre en la región vecina más cercana? Si ese comportamiento importa, debes conservar el control de la gestión del tráfico y no entregárselo a otros
Hoy en día existen otras soluciones para elegir mucho antes de llegar a esa situación de “no hay otra opción”
Quisiera objetar con cuidado la parte de detección automática de servidores fuera de línea por DNS en la frase “puede distribuir la carga entre varios servidores y detectar automáticamente qué servidor está fuera de línea para elegir uno que esté en línea”: el round robin de DNS, en su estado básico, solo sirve más o menos para balancear carga
A menos que pongas lógica inteligente en el cliente, no ocurre nada automáticamente en términos de detección del estado de disponibilidad. La introducción del artículo sí menciona esto hasta cierto punto, pero tuve que leerla varias veces para captar el sentido. Siendo justo, quizá fue un problema de mi propia comprensión. Después leí el resto del artículo y todo trataba precisamente de esa lógica inteligente
Si el registro 1/N del servidor elegido por el navegador no está disponible, no ocurre recuperación automática ni reintento a nivel de protocolo
Como “dato curioso” adicional, no olviden el TTL de DNS de Java [1] y el comportamiento de
.equals()[2][1] https://stackoverflow.com/questions/1256556/how-to-make-java...
[2] https://news.ycombinator.com/item?id=21765788 (hace 5 años, 168 comentarios)
Hay clientes que ignoran el TTL, pero son bastante raros
Si un servidor se cae, quedan direcciones IP distribuidas y en caché por todo el mundo, y no se puede impedir que la gente acceda a esa dirección
https://www.cloudflare.com/learning/dns/glossary/round-robin...
El balanceo de carga también tiene un costo, y además existe el problema de que el balanceador rompa las conexiones de forma sutil o evidente. En algunos proveedores, la disponibilidad del balanceador llegó a ser peor que la de nuestros hosts
Si controlas al cliente, también es razonable llamar a la API de DNS de la plataforma para obtener una lista de IP, mezclarla adecuadamente y rotarla. Mejor aún si puedes incluir en el binario del cliente algunas IP asignadas de forma estable como respaldo para cuando DNS falle. Pero DNS normalmente no está fallando, y resulta útil para hacer cambios operativos sin desplegar nueva configuración o un nuevo binario cada vez que se actualiza el clúster
Si el cliente es un navegador, el comportamiento predeterminado es bastante aceptable. Normalmente usa las IP en orden, así que eso puede ser un problema [1], pero fuera de eso, el comportamiento de reintento es bueno. Si recibe una conexión rechazada, prueba inmediatamente otra IP, y si hay un timeout, al menos intenta con algunas otras IP. No es ideal, así que para navegadores usaría un balanceador y, si es posible, al menos para la carga inicial de la página. Para WebSocket y similares, también se puede usar DNS round robin con una lógica de cliente en JS relativamente inteligente. Aun así, también es posible usar DNS round robin para todo el sitio
Si el cliente no es un navegador y tampoco lo controlas, no queda más que desear suerte
Reconozco al 100% que a veces hay que asumir que alguien implementó un resolvedor con caché de DNS interpretando el campo TTL en días en vez de segundos. Los clientes detrás de ese resolvedor tendrán problemas cuando se actualice el DNS. Pero si el balanceador está detrás de un nombre DNS y llega el día en que haya que cambiar esa dirección, entonces el problema aparecerá igual, y en ese momento no tendrán experiencia con ello
[1] Uno de los RFC propone que la API del sistema operativo ordene las respuestas según coincidencia de prefijo. Eso tendría sentido si los prefijos de IP fueran jerárquicos y sirvieran como una métrica indirecta para elegir el servidor con menor distancia de red. Pero en la práctica, muchas veces dos
/24numéricamente cercanos no están cerca en la red. Si las direcciones de los servidores están muy dispersas, puedes ver que el tráfico de algunas IP de clientes se concentra hacia las IP de servidores numéricamente parecidasClaro, siempre puede haber alguien con el DNS local mal configurado o que esté obligado a usar un cliente deficiente. O aceptas la caída para quienes tienen configuraciones rotas, o tienes que reasignar la IP a otro servidor del mismo centro de datos
Hola. Soy el CTO de Cloudflare. Desplegamos un cambio para todas las cuentas gratuitas de Cloudflare para que se comporten igual que las cuentas de pago
El problema mencionado aquí ya se corrigió, y Zero Downtime Failover debería funcionar en todos los tipos de cuenta. ¿Podrías volver a probarlo?
Gracias por documentarlo por escrito. Me alegra que podamos cambiar este comportamiento para todos
También actualizaré el artículo en consecuencia. Gracias por habilitarlo también para las cuentas gratuitas; es un gran resultado
La versión oscura remezclada de esto es fast flux hosting, y es un método que usan muchos proveedores de bulletproof hosting
https://unit42.paloaltonetworks.com/fast-flux-101/
Puede valer la pena mencionar que Zero Downtime Failover es una función de Pro en adelante
Recuerdo que antes, cuando la documentación de protección del origen estaba separada por nivel de plan, así estaba documentado. Por eso el funcionamiento o los reintentos pueden verse distintos según el caso