Load Balancing / HA - Múltiples backends en Varnish

Varnish permite la definición y utilización de múltiples backends HTTP, ya sea por selección manual (en base a Host o URL), por balanceo de carga o por "disponibilidad" de los mismos ("probe").

El primer caso (selección manual) se basa en la utilización de expresiones regulares sobre la URL o el HOST para decidir el uso de uno u otro backend.

El resto de casos se basan en las capacidades de Balanceo de carga o Alta Disponibilidad de Varnish, es decir, en la utilización del director, un backend virtual que agrupa varios backends con ciertas propiedades de "prueba" y "balanceo".


Selección manual de backends (separación de recursos)

En muchos casos puede darse la ocasión de que tengamos los contenidos distribuídos entre varios servidores HTTP. Por ejemplo, podemos tener el contenido dinámico PHP en un servidor con Apache + mod_php, y el contenido estático (imágenes, js, css en este ejemplo) en un servidor NGINX aparte.

En ese caso, podemos definir múltiples backends y utilizar uno u otro en vlc_recv() en función de la URL.

En el siguiente ejemplo, se envían todos los datos al backend "apache" excepto aquellas URLs que empiecen por /images/, /layout/, /js/ o /css/, que se envían a un backend alternativo ("estaticos"):

backend apache
{
  .host = "127.0.0.1";
  .port = "8080";
}
 
backend estaticos {
  .host = "172.16.1.2";
  .port = "80";
}

 
sub vcl_recv 
{
    # Contenidos estaticos -> dirigir a un backend diferente
    if (req.url ~ "^/(images|layout|js|css)/" ) 
    {
        unset req.http.Cookie;
        unset req.http.Authenticate;
        set req.backend = estaticos;
        return(lookup);
    }
 
    # El resto, van al backend por defecto (apache):
    set req.backend = apache;
    return(pass);
}

Nótese que a la hora de seleccionar el backend de estáticos, hemos también eliminado las Cookies y las peticiones de Autenticación puesto que nuestro backend de estáticos sólo contiene "ficheros".


Load Balancing en Varnish

El balanceo de carga y alta disponibilidad en Varnish se utiliza mediante un "backend HTTP virtual" llamado director, que agrupa varios backends junto a las pruebas necesarias para determinar si están "sanos" (si responden a peticiones HTTP).

Veamos la definición de 2 backends HTTP de contenido idéntico, y cómo se agrupan en un director para su posterior uso:

backend servidor1 
{
    .host = "172.16.1.2";
    .port = "81";
    .probe = 
    {
        .url = "/";
        .interval = 5s;
        .timeout = 3s;
        .window = 5;
        .threshold = 3;
    }
}

backend servidor2 
{
    .host = "172.16.1.3";
    .port = "81";
    .probe = 
    {
        .url = "/";
        .interval = 5s;
        .timeout = 2s;
        .window = 5;
        .threshold = 3;
    }
}

El parámetro .probe indica a Varnish la forma de testear si el backend está disponible o no. En el anterior ejemplo, se verifica la URL "/" de cada backend cada 5 segundos. Si tarda más de 2 segundos en contestar en más de 3 de las últimas 5 pruebas, se considera que el backend está caído y Varnish no le volverá a mandar peticiones hasta que vuelva a estar de nuevo disponible.

A continuación definimos nuestro "backend virtual" (el director), con el método round-robin (alternar peticiones) o con el método random (envío aleatorio de peticiones a uno u otro backend):

director backends_http round-robin 
{
    {
        .backend = servidor1;
    }
    {
        .backend = servidor2;
    }
}

En el caso de directors de tipo random, podemos indicar un parámetro adicional en los backends llamado .weight (valor de 1 a N) para especificar un peso a cada servidor

director backends_http random 
{
    .retries 2
    {
        .backend = servidor1;
        .weight = 1;
    }
    {
        .backend = servidor2;
        .weight = 1;
    }
}

El parámetro .retries indica el número de intentos que el director realizará para encontrar un backend válido (por defecto, para este y otros métodos de balanceo), es igual al número de backends definidos.

Tras esto, podemos usar el director como cualquier otro backend en las funciones vcl_*(), bien asignándolo directamente, o bien en base a algún match en host o url:

sub vcl_recv 
{
    if (req.http.host ~ "^(www.)?dominio.com$") 
    {
        set req.backend = backends_http;
    }
}

Un apunte: dentro de los directors podemos utilizar (definir) backends no definidos anteriormente:

director nombre metodo
{
   { .backend = backend_uno; }
   { .backend = backend_dos; }
   { .backend = { .host="blah"; .port="82"; } }
}


Otros métodos de balanceo

En la documentación de Varnish se especifican otros métodos de balanceo además de round-robin y random:


  • hash director → Este tipo de "director" seleccionará un backend HTTP en base al hash de la URL. Esto permite usar Varnish como balanceador de otros Varnish y permite que los objetos no sean cacheados en múltiples cachés.
  • dns director → Este tipo de "director" permite definir múltiples backends en base a la propiedad ".list y seleccionar uno u otro en realizando una consulta DNS de la cabecera Host: para determinar si algún backend hace match con él.

El ejemplo para director dns de la página oficial de Varnish es:

director directorname dns 
    {
        .list = 
        {
            .port = "81";
            .connect_timeout = 0.4;
            "192.168.15.0"/24;
            "192.168.16.128"/25;
        }
        .ttl = 5m;
        .suffix = "internal.net";
}

Este código especifica 384 backends, todos con los mismos parámetros de conexión, de forma que se le añade "internal.example.com" al host-header enviado por el cliente, y se consulta esto en el DNS para utilizar el backend resultante por IP.

En el anterior ejemplo, si un cliente HTTP pregunta por "www.dominio.com", Varnish acudirá al DNS y preguntará por la IP de "www.dominio.com.internal.net". Si se obtiene una IP válida y está dentro de los backends definidos en la lista, utilizará ese backend para atender la petición.


Client Director

Finalmente, a partir de Varnish 2.1.4 se puede balancear en función de alguno de los parámetros del cliente HTTP, como su IP, la URL solicitada o alguna de las cabeceras HTTP enviadas:

director balanceo_cliente client 
{
    {
        .backend = servidor1;
        .weight = 1;
    }
    {
        .backend = servidor2;
        .weight = 1;
    }
}

sub vcl_recv {
   set req.backend = balanceo_cliente;

   // Balancear por IP del cliente (es la accion por defecto):
   set client.identity = client.ip;
   
   // Balancear por URL:
   // set client.identity = req.url;

   // Balancear por User-Agent
   // set client.identity = req.http.user-agent;
}



<Volver a Página de VARNISH>