Alta disponibilidad: Load Balancer y Failover con Haproxy

1. Nociones básicas de Escalabilidad, Load Balancing y Failover


En este artículo veremos cómo conformar un Load Balancer con Failover usando software con el conclusión de Balancear un Web Server. Un Load Balancer es, en escencia, un ordenanza que recibe peticiones de un cliente, las procesa a partir de algún criterio (algoritmo) y finalmente las envía a alguno de los servidores que las resolverán y responderán.

Si ya estás familiarizado con los conceptos de escalabilidad, Load Balancing y Failover creíble quieras salterarte esta parte e moverse directo al punto 2, en que comenzaremos a detallar la configuración de nuestro Cluster HA.

En esta oportunidad, balancearemos la carga de un Web Server (HTTP), pero los Load Balancers suelen tolerar balanceo de diversos servicios tales como smtp, ftp, https, dns, pop3 y otros.

Pongamos por ejemplo que nuestro sitio web se encuentra alojado en un dedicado y que debido al alto tráfico el servidor comienza con picos de Server Load que sobrecargan el procesador. Esto, generalmente, tiene el poco feliz final de downtime o una conformidad lenta y nos pone frente al problema y el mundo de la escalabilidad: debemos acordar el alto tráfico de algún modo.
Ahora bien, existen dos modos de encarroñar este problema y conseguir que nuestro Servidor Web soporte el tráfico. En los 2 casos estaríamos «escalando» pero de otro modo.

Escalar verticalmente
Tan incauto como comprar un dedicado con mayor capacidad (CPU, RAM). Si el tráfico sigue creciendo y el día de mañana el nuevo Server nos queda chico nuevamente compramos solo más grande. Y así …

Escalar horizontalmente
Este método apela a una resolución más económica y consiste en agregar un nuevo servidor al servidor saturado de modo de tener 2 servidores web con copias idénticas de nuestro sitio y «repartir» el trabajo que antes realizaba el dedicado original. Un inseguro crecimiento del tráfico permitiría agregar solo o más servidores.

En cuerpo de escalabilidad, la resolución correcta a la hora de responder a tráfico alto y posibilidades de que éste siga creciendo es escalar horizontalmente y es aquí donde los Load Balancers se tornan necesarios dado que una estructura de servidores como la descripta en el modelo de escalabilidad horizontal requiere que delante de los 2 o más servidores webs de que disponemos, exista un ordenanza que capture las peticiones de los visitantes y las envíe a solo u otro servidor, repartiendo de este modo el trabajo y la carga.

Decimos que la escalabilidad horinzontal es el método adecuado porque escalar verticalmente tiene un límite (en algún punto llegaremos a la instancia en que compramos el Servidor más pujante que nuestro Datacenter pueda ofrecernos, habremos invertido muchísimo dinero en Servidores y las migraciones  de la aplicación a un server más grande sin downtime y -finalmente- acabaremos pensando en escalar horizontalmente).

Por otro lado, escalar horizontalmente es económico, dado que se utilizan muchos boxes  de bajo costo.  Agregar un nuevo Box detrás del Load Balancer es tarea de minutos y no supone downtime de ninguna clase (al menos si las cosas salen bien). Se elimina además un problema fundamental en el mundo de la alta disponibilidad: el SPF o Single Point of Failure debido a que si un Box «se muere» el Balaceador detectará su colocación (Failover) y seguirá enviando las peticiones de los clientes a los otros: tendremos tiempo de arreglarlo o reemplazarlo de un modo transparente para nuestros visitantes.

Las ventajas de escalar de este modo no terminan aquí (otra muy fuerte es el Global Load Balancing, que permite descubrir la ubicación geográfica de nuestro visitante y acordar sus peticiones con el Servidor más cercano a él). En resumen, aclarar las virtudes de escalar horizontalmente requeriría un amplio artículo dedicado al tema. Nos bastará con dominar que éste es el esquema elegido por Google, Facebook y, en línea generales, todos los sitios que deben acordar millones de Requests por segundo día a día.

2. Comenzando con nuestro Load Balancer

Antes de comenzar a conformar nuestro LB, debemos entrar -aunque sea, brevemente- una distinción acerca de estos sistemas ubicados al frente de nuestros servidores. Existen 2 tipos de Load Balancers. Estos son:

1. Hardware Load Balancer
2. Software Load Balancer

Load Balance con Hardware supone utilizar equipos de red diseñados específicamente para obrar Balanceo de Carga. Se trata en todos los casos de dispositivos con un costo bastante alto. Un ejemplo significativo es el conocido F5 Big Ip de F5 Networks, aunque existen muchas empresas que fabrican éste tipo de componentes de Networking.

Por otro lado, hacer Load Balancing con Software supone contar con Boxes dedicados exclusivamente a esta tarea. Estos boxes corren Linux y sobre ellos instalamos algun software que se encargará de obrar el balanceo.Existen diversas soluciones para hacer Load Balancing con Software. La más conocida es, quizás, Linux Virtual Server (LVS), pero hemos optado en este acontecimiento por Haproxy, dado que es una resolución tan robusta como sencilla de poner a andar, representando la elección ideal para introducirnos en el mundo del Load Balancing.

2.1 Nuestro escenario

Visto y considerando que balancearemos un Servidor Web que corre una aplicación PHP/MYSQL necesitamos como mínimo adecuar de 3 equipos con Linux (Debian Squeeze, en nuestro caso). Uno de los equipos será destinado al Balanceo y los otros dos serán los servidores lampp detrás de éste. Necesitaremos además crear una IP implícito en el balanceador, a la que direccionaremos todo el tráfico.

Aquí están los valores con que trabajaremos
– Nodo1:  Load Balancer – Debian Squeeze + Haproxy (192.168.1.31)
– Nodo2:  Server HTTP – Debian Squeeze + LAMPP (192.168.1.32)
– Nodo3:  Server HTTP – Debian Squeeze + LAMPP (192.168.1.33)
– IP Virtual: 192.168.1.20
– Dominio virtual: www.sitio.dev

3 notas sobre este entorno
1. Es muy importante que nuestras interfaces de Networking en cada box estén configuradas estáticamente y no senda DHCP. Si éstas obtienen su IP senda DHCP es hora de setearlas a static. Hacer esto es muy incauto y pueden seguir éste artículo para manejar esta tarea a cabo.

2. Debido a que estamos trabajando en una Red local no usaremos Bind9 para acordar nombres  de dominios. Nos limitaremos a publicar el archivo hosts del Box Balanceador para que un nombre de dominio ficticio www.sitio.dev apunte a la IP implícito (192.168.1.20).

3. Al lector atento no se le habrá escapado que este entorno tiene un SPF a nivel del Load Balancer: si la máquina corriendo Linux y Haproxy se cae por alguna raciocinio nuestro sitio alojado en los 2 Servidores no responderá cuando ingresemos www.sitio.dev en nuestro navegador. Nos hemos tomado la licencia de trabajar con este SPF para la coordinación de este artículo, pero si quisieramos conformar un Cluster de alta disponibilidad con Failover en producción lo ideal sería adecuar de un segundo Box pasivo balanceando que tomara la tarea del primero ante una inseguro falla (arquitectura active/passive). Estos 2 Load Balancers podrían utilizar heartbeat para comprender su colocación constantemente de modo que ante una inseguro falla en el Load Balancer 1 la IP Virtual resolviera al Load Balancer 2.

3. Configurando la IP Virtual (192.168.1.20) y preparando los servidores web

Vamos a publicar nuestro fichero /etc/network/interfaces en nodo1 (el Load Balancer) para crear una IP Virtual.

carp@nodo1:/$ su root
Contraseña: Ingresamos clave Root
root@nodo1:/# pico /etc/network/interfaces
Debajo de la declaración estática de la Ip de este Box, agregamos:
# Load Balancer VIP
auto eth0:1
iface eth0:1 inet static
  address 192.168.1.20
  gateway 192.168.1.1
  netmask 255.255.255.0
  network 192.168.1.0
  broadcast 192.168.1.255
salvamos (ctrl+o), salimos (ctrl+x) y reiniciamos la red

root@nodo1:/# /etc/init.d/networking restart
Reconfiguring network interfaces…
done.
root@nodo1:/# 
Podemos comprobar la creación de nuestra VIP con ifconfig
root@nodo1:/#  ifconfig

[…]
eth0:1    Link encap:Ethernet  HWaddr 00:1d:60:d9:03:9a
          inet addr:192.168.1.20  Bcast:192.168.1.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
[…]

Creada nuestra VIP, editaremos hosts para apuntar el dominio www.sitio.dev a élla.

root@nodo1:/# pico /etc/hosts
Agregamos debajo de los valores del fichero:
192.168.1.20 sitio.dev www.sitio.dev
salvamos (ctrl+o) y salimos (ctrl+x) 

Recordamos que por tratarse de una configuración local no utilizamos (aunque podríamos) Bind9 para acordar dominios.  Si así lo hicieramos deberíamos registrar la entrada www como CNAME en la zona correspondiente. Aquí nos limitamos a controlar el dominio ficticio desder hosts y lo repetimos con y sin las www para que éste sea accesible de los 2 modos.

Ya tenemos nuestra VIP configurada en nodo1 y el dominio www.sitio.dev apuntando a ella: estamos listos para comenzar con Haproxy, pero antes -siempre resta algo- tendremos que obrar una modificación en el modo en que nuestros 2 servidores Lampp (192.168.1.32/33) almacenarán los logs.

Haproxy funcionará como un proxy transparente delante de nuestros servidores web y es de fundamental importancia que los configuremos para que al hacer logging lo hagan con las IPS de nuestros visitantes y no con la del Balanceador (de otro modo todos los request en access y error.log tendrán como IP 192.168.1.20).

Para prevenir esto ingresamos a cada Box -por lo general a través de ssh- y buscamos en el fichero apache2.conf (en mi caso, las últimas líneas del archivo). Al llegar a la parte en que se definen los Logs encontraremos una explicación comentada de lo que necesitamos cambiar.

Vamos con el primer Servidor Web (nodo2 : 192.168.1.32)

root@nodo1:/# ssh 192.168.1.32
Ingresamos clave root del Box nodo2:
root@nodo2:/# pico /etc/apache2/apache2.conf

Bajamos hasta el final del fichero y encontraremos:

LogFormat «%v:%p %h %l %u %t «%r» %>s %O «%{Referer}i» «%{User-Agent}i»» vhost_combined
LogFormat «%h %l %u %t «%r» %>s %O «%{Referer}i» «%{User-Agent}i»» combined
LogFormat «%h %l %u %t «%r» %>s %O» common
LogFormat «%{Referer}i -> %U» referer
LogFormat «%{User-agent}i» agent

Tal cual se explica arriba de esta sección como comentario necesitamos intrudoducir este cambio en la segunda línea:

LogFormat «%v:%p %h %l %u %t «%r» %>s %O «%{Referer}i» «%{User-Agent}i»» vhost_combined
LogFormat «%{X-Forwarded-For}i %l %u %t «%r» %>s %O «%{Referer}i» «%{User-Agent}i»» combined
LogFormat «%h %l %u %t «%r» %>s %O» common
LogFormat «%{Referer}i -> %U» referer
LogFormat «%{User-agent}i» agent

salvamos (ctrl+o) y salimos (ctrl+x)

Las modificaciones no terminan aquí. El mecanismo de Failover de nuestro software de Balanceo (Haproxy) consiste en enviar requests cada x cantidad de segundos (este coraje es configurable, como veremos más adelante) a cada Box para dominar si éste se encuentra o no disponible. Para esto, se suele crear un fichero .txt vacío en cada box que Haproxy solicitará para comprender su estado. Si Haproxy obtiene una conformidad positiva (OK 200) entonces el servidor está funcional. Si por el contrario el Request al fichero .txt no es respondido Haproxy no enviará más solicitudes a ese servidor y dirigirá todo el tráfico al otro/s.

Ahora bien, necesitamos decirle a nuestros servidores que no computen estas solicitudes ni la logueen, debido a que esto aumentaría drásticamente el tamaño de nuestros logs. Para lograrlo, editaremos el fichero de cofiguración de VirtualHosts en cada solo de los Boxes indicando explicitamente que toda solicitud del archivo test.txt (así se llamará el archivo que aún no creamos) no debe esencia incluída en los logs.

Seguimos con la sesión ssh en 192.168.1.32
root@nodo2:/# pico /etc/apache2/sites-enabled/www.sitio.dev

Este fichero es copia de 000-default (el archivo que Apache2 trae por defecto) y en él debemos buscar la sección Log Files y modificarla de modo que esto:

 # Log files
CustomLog /var/log/apache2/access.log combin 

quede así:

 # Log files
SetEnvIf Request_URI «^/test.txt$» dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog 

salvamos (ctrl+o), salimos de nano (ctrl+x) y finalmente cerramos la sesión ssh con exit

Ingresamos al segundo servidor web senda ssh  (nodo3 : 192.168.1.33) y repetimos lo hecho en los ficheros /etc/apache2/apache2.conf y /etc/apache2/sites-enabled/www.sitio.dev.
Una vez que hemos realizado estas modificaciones en ambos servidores, nos reconectamos senda ssh y creamos el archivo test.txt vacío en cada solo en el nivel en que nuestro sitio se encuentra (en cada box corremos touch /var/www/test.txt).

4. Instalando y configurando Haproxy

Al conclusión llegamos al punto en que instalamos el software con que realizaremos Load Balancing. Si bien el exigencia en que estos pasos se han llevado a cabo (preparar los servidores web antes de poner Haproxy) no tienen por qué transitarse de este modo, es recomendable siempre «preparar» nuestros boxes para el software antes de instalarlo. De esta forma, luego podemos dedicarnos pura y exclusivamente a la configuración del mismo olvidando el resto.

root@nodo1:/$ su root
Contraseña: Ingresamos clave Root
root@nodo1:/# apt-get install haproxy

Una vez instalado Haproxy (link al paquete por las dudas) procedemos a configurarlo. Debido a que este fichero trae consigo mucha información que no utilizaremos, creamos una copia del singular y luego borramos su contenido para dominio ingresar nuestra configuración desde 0.

oot@nodo1:/$ cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy_orig.cfg
root@nodo1:/# cat /dev/null > /etc/haproxy/haproxy.cfg
root@nodo1:/# pico /etc/haproxy/haproxy.cfg

 Ingresamos al fichero en blanco y en él introducimos: 

global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        maxconn 4096
        user haproxy
        group haproxy
        daemon

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option redispatch
        maxconn 2000
        contimeout      5000
        clitimeout      50000
        srvtimeout      50000

listen lbservers 192.168.1.20:80
        mode http
        stats enable
        stats auth carp:miclave
        balance roundrobin
        cookie uid prefix
        option httpclose
        option forwardfor
        option httpchk HEAD /test.txt HTTP/1.0
        server nodo2 192.168.1.32:80 cookie A check
        server nodo3 192.168.1.33:80 cookie B check

Guardamos (ctrl+o) y salimos (ctrl+x)

Hemos definido en la sección listen la ip implícito a que el Balanceador responderá y el puerto (que por esencia Balanceo HTTP es 80). Definimos además un usuario y clave para las stats (que nos permitirán mirar el colocación de nuestro farm de servers) y optamos por el algoritmo Round Robin para balancear la carga. Como todo buen balanceador, Haproxy permite otros métodos para balancear que no veremos aquí pero es recomendable conocer. Además, definimos la cookie con nombre uid (que de hecho se crea en nuestra aplicación cuando un usuario se conecta con su cuenta) como cuantificación para fijar sticky sessions (esto significa que una vez el usuario inicia consulta el Balanceador lo mantendrá en el Box en que se logueó de modo de conservar los valores de la consulta que no se encuentran en otros boxes). Finalmente indicamos el nombre del fichero (test.txt) que le dirá al balanceador si los servidores se encuentran o no usables y, por último, listamos los 2 boxes con lampp hacia los cuales deberán dirigirse los Requests.

Ahora vamos a agregar Haproxy a init de modo que se cargue al bootear linux. Esto se realiza seteando ENABLED=1 en /etc/default/haproxy

root@nodo1:/# pico /etc/default/haproxy
Cambiamos ENABLED=0 por ENABLED=1
Guardamos (ctrl+o) y salimos (ctrl+x)

5. Finalizando la configuración y probando nuestro Balanceador

Sólo un paso nos separa de ingresar www.sitio.dev en nuestro navegador y obtener el sitio web de alguno de los 2 servidores que lo alojan. Debemos agregar al archivo /etc/sysctl.conf la variable net.ipv4.ip_nonlocal_bind con coraje 1 para que el kernel permita a nuestras aplicaciones (Haproxy) utilizar una IP que no está asociada a ningún dispositivo (nuestra vip, que no posee un placa). Ingresamos al fichero de configuración y agregamos al final la siguiente línea:

root@nodo1:/# pico /etc/sysctl.conf
En el final agregamos:
net.ipv4.ip_nonlocal_bind=1
Guardamos (ctrl+o) y salimos (ctrl+x)

Una vez reiniciemos el Load Balancer la configuración que acabamos de agregar será cargada. Para evitarnos un reboot innecesario la cargaremos con el comando:

root@nodo1:/# sysctl -p
Y al fin arrancamos Haproxy
root@nodo1:/# /etc/init.d/haproxy start
Starting haproxy: haproxy.
root@nodo1:/

Si Haproxy ha comenzado sin problemas y los servidores web nodo2 y nodo3 están corriendo Lampp sin inconvenientes tendríamos que obtener el sitio que alojamos en ellos al ingresar en un navegador (en mi acontecimiento el Box con Haproxy tiene GUI y navego desde ahí) www.sitio.dev

Es una buena creencia agregar al  body del index.php de ambos sitios algo como «nodo2» y «nodo3» en los respectivos servidores de modo de dominio cargar la página y dominar a qué box envió la súplica Haproxy.
Podemos mirar el colocación de nuestro cluster de servidores ingresando a: http://192.168.1.20/haproxy?stats (les pedirá el user y pass que han definido en /etc/haproxy/haproxy.cfg, sección listen directiva stats.
Pueden exponer el failover deteniendo solo de los 2 servers para revisar que el sitio seguirá mostrandosé desde el otro y mirar en el panel de stats de Haproxy como el server detenido aparece en rojo.

6. Consideraciones finales

Haproxy es software pudiente a la hora de obrar Load Balancing con Failover y, como hemos visto, configurarlo no nos ha representado mayores dificultades. Está claro que hemos realizado una configuración básica y seguramente podría mejorarse en varios aspectos. Hemos decidido, simplemente, «ponerlo a andar» como primer paso (esto no es poco). Una vez funcionando pueden estudiar en profundidad el software y tweakearlo como les guste.

Aquí hemos asumido que los 2 sitios webs php/msyql alojados en los servers webs detrás del LB, ya han verificado la sincronización de sus contenidos. Este no es un materia menor y hemos, con anterioridad, cubierto posibles soluciones tanto para sincronizar ficheros como para tener las bases de datos mysql en idéntico estado.

Fuentes de Información

El contenido del post es de mi autoría, y/o, es un recopilación de distintas fuentes.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *