Una de las herramientas que los devs muchas veces no aprovechamos es el balanceo de cargas a nivel DNS. Con esta técnica, el tráfico de red se distribuye hacia servidores diferentes con base en la resolución del dominio; en lugar de retornar una única IP, la herramienta puede proporcionar direcciones diferentes dependiendo de la configuración establecida. Este tipo de herramientas pueden ayudar en estrategias de multi-nube enfocadas a la mejora de desempeño o recuperación de desastres.
Este es un ejemplo bastante simple de como, menos de 15 segundos después de presentarse una falla en el sitio principal en Brasil (GRU), Akamai Global Traffic Manager (GTM) logra transicionar las peticiones HTTP del cliente hacia el sitio secundario en EEUA (MIA). Además, unos minutos después de ser reparada la falla, GTM manda las peticiones de manera automática y transparente hacia Brasil nuevamente. Todo esto a través de seleccionar la IP correspondiente al destino, sin necesidad de redirigir el tráfico como sucede en otras estrategias de balanceo.
Este es un video mostrando el resultado final del ejercicio, y el código fuente está en Github. Kudos a Allan Madrigal por su ayuda con la configuración de GTM.
Revisemos el funcionamiento, dividiendo el proceso en tres secciones. Primero, la parte del balanceo con GTM, después, la configuración del cliente, y finalmente la visualización.
Global Traffic Manager
Como mencionamos anteriormente, para este ejemplo tenemos un sitio replicado en dos regiones, São Paulo, Brasil, como principal, y Miami, USA, como secundaria. Pensemos en que la audiencia se encuentra en Sudamérica, y Brasil nos ha resultado mejor en cuestión de latencia, desempeño, servicios disponibles, costos, etc.
Independientemente de las tecnologías y herramientas que utilicemos, la URL a la que apuntemos para hacer llamadas va a regresar una IP para que realizar la comunicación. Digamos que para São Paulo la IP es 192.0.20.0
, y para Miami 192.0.20.1
. En este caso estamos usando Linode Kubernetes Engine (LKE) para correr nuestra aplicación, expuesta como servicio tipo LoadBalancer
en nuestro cluster de Kubernetes, que genera un NodeBalancer con IP Pública.
En GTM configuramos que nuestra conexión es de tipo “failover”, y nos da un FQDN para usarlo como nuestra dirección de servidor, por ejemplo misitio.jgaona.akadns.net
. GTM realiza pruebas continuas para verificar la salud de ambos sitios; en este caso se configuró para hacerlo cada 10 segundos. Cuando se detecta que el sitio principal esta funcionando correctamente, los queries de DNS que se realizan al momento del cliente intentar conectarse hacia misitio.jgaona.akadns.net
, regresan un registo de tipo A
con la IP correspondiente a São Paulo, que es 192.0.20.0
.
En cuanto las pruebas de salud de GTM detectan que hay una falla, la IP que se regresa es la correspondiente al sitio secundario en Miami, 192.0.20.1
. Las fallas pueden errores tipo 5XX, hasta 3XX o 4XX si así lo deseamos.
Otro punto importante que debemos considerar es el TTL, que indica a los “resolvers” cuanto tiempo deben mantener en caché las direcciones IP que corresponden a un FQDN. Un valor pequeño ayuda a realizar cambios de manera ágil, casi en tiempo real, útiles para escenarios de recuperación de desastres. Sin embargo, puede incrementar el tiempo de respuesta al tener que realizar las consultas al servidor de DNS continuamente. Para este caso, la configuración está definida en 30 segundos para poder reaccionar de manera ágil, que es la idea del ejemplo. Cada carga de trabajo tiene su propios pros y contras en este sentido.
Cliente HTTP
Para emular el tráfico hacia nuestro sitio, se utilizó una aplicación de consola en .NET. Realmente el punto importante en este ejercicio es entender que el cliente sólo realiza los queries al DNS al momento de crear la conexión. Esto significa que, si usamos un objeto que viva por largo tiempo, como un singleton, los cambios realizados por GTM no van a ser detectados, a menos que se cree establezca nuevamente la conexión.
La clase HttpClient
utiliza SocketsHttpHandler
para administrar las configuraciones que corresponden a cada instancia, entre las que se incluye el pool de conexiones. Este tiene un tiempo de vida “Indefinido” por default; sin embargo puede configurarse de acuerdo a las necesidades del cliente usando la propiedad PooledConnectionLifetime
. Para este escenario se definió un tiempo de vida de 15 segundos:
var handler = new SocketsHttpHandler
{
// Ensure the DNS config is refreshed continuously
// https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines#dns-behavior
PooledConnectionLifetime = TimeSpan.FromSeconds(15)
};
HttpClient httpClient = new HttpClient(handler);
Con esta configuración logramos que el cliente obtenga la IP del destino que GTM define como adecuado para procesar las peticiones.
Visualización
Para mostrar como se realiza el cambio de destino con GTM, usamos SignalR y D3js. Al momento de recibir una petición en el sitio en cada ubicación, se envia un mensaje indicando “donde” ocurrió hacia un Hub de SignalR configurado en un tercer sitio, también corriendo en Akamai LKE.
public async void OnGet()
{
// Send the site name to the page for easy visualization.
ViewData["site"] = site;
// Notify the SignalR Hub we received a request
await _hubConnection.InvokeAsync("BroadcastRequestCount", site);
}
Al recibirse el mensaje, se determina el origen con el contenido del mensaje y se actualizan las gráficas, como puede verse en el video mostrando el resultado final del ejercicio.
connection.on("UpdateRequestCount", function (count) {
gru = 0; mia = 0;
if (count === "gru") {
gru = 1;
} else {
mia = 1;
}
updateCharts(gru, mia);
});

En las gráficas se ilusta la proporción de peticiones recibidas en cada sitio, así como la cantidad.
Puntos finales
Aunque ambos endpoints están corriendo en Akamai LKE, la ventaja de GTM es que puede balancear el tráfico entre ubicaciones sin importar donde estén, incluso fuera de Akamai Cloud, ya sea on-prem u otras nubes.
Claramente los resultados pueden variar dependiendo de las configuraciones y tipos de cargas de trabajo, pero resulta interesante evaluar el balanceo a nivel DNS como herramienta de estrategias de multi-nube. Otros escenarios pueden ser enviar peticiones del cliente al front-end más cercano, menos ocupado, aleatorio, etc., independientemente de su ubicación en el mundo.