Configurar balanceador de carga y alta disponibilidad en Debian

Configurar alta disponibilidad es prácticamente un requisito indispensable en un ambiente de producción, mientras más críticas son nuestras operaciones, más nos veremos forzados a balancear la carga de los servidores y mantener un respaldo activo para cualquier eventualidad o un simple mantenimiento de alguno de los equipos. Es por esto que a raíz de un manual que pude aplicar correctamente en mi ambiente laboral, he decidido preparar uno totalmente en español y subrayando ciertos detalles del manual original.

Escenario inicial

Contamos con 4 servidores (todos ellos con Debian Lenny), dos de los cuales nos sirven de resolución de dominios y al mismo tiempo de balanceadores de carga, a estos le llamaremos LB1 y LB2.

Al mismo tiempo contamos con dos servidores web con LAMP (Linux, Apache, MySQL, PHP). Aunque poseen MySQL, no se les dará uso, ya que se manejan los servidores de bases de datos en otros equipos con sus respectiva redundancia y alta disponibilidad por separado. A estos los llamaremos WEB1 y WEB2.

Escenario deseado

Luego de la configuración exitosa, las peticiones web se realizarán a un interfaz virtual y compartida entre los dos balanceadores de carga, para hacer transparente al usuario el cambio entre equipos. Así quedaríamos de la siguiente forma:

A los dos balanceadores de carga LB1 y LB2 se les instalará HAProxy y keepalived mediante consola, detallado más adelante (en caso de que en este punto no sepan como hacerlo).

Preparando los servidores web y el Apache

Parto desde el punto en que el Apache está configurado para gestionar los documentos web desde /var/www/, si no conoce la configuración exacta de su directorio de trabajo web, puede ir al archivo /etc/apache2/sites-available/default y confirmar.

Para ambos servidores web WEB1 y WEB2, vamos a modificar el archivo de configuración de apache de la siguiente manera, comentando la línea correspondiente al LogFormat y la reemplazaremos como está descrito más abajo.

nano /etc/apache2/apache2.conf
#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

Además vamos a configurar el HAProxy para que constantemente se mantenga realizando peticiones del archivo check.txt dentro de la carpeta /var/www (nuevamente hago énfasis en que este sería el directorio por defecto del apache en caso de que no se haya especificado lo contrario). Este tipo de peticiones estaría generando cierto descontrol en cualquier monitor de estadísticas que se pueda instalar a los servidores web, así que es algo que se debe tomar en consideración.

El siguiente paso es añadir dos líneas al apache para que se mantenga verificando que pueda alcanzar dicho archivo:

nano etc/apache2/sites-available/default
SetEnvIf Request_URI "^/check\.txt$" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

A continuación creamos el archivo check.txt, reiniciarmos el apache y terminamos la configuración de los servidores web:

touch /var/www/check.txt
/etc/init.d/apache2 restart

Configurando los balanceadores de carga

En este punto es necesario descargar en ambos balanceadores LB1 y LB2 el HAProxy. El archivo de configuración general del HAProxy se encuentra en /etc/haproxy/haproxy.cfg. Luego de descargarlo con apt, haremos una copia de seguridad del archivo de configuración y posteriormente crearemos uno nuevo para modificarlo.

apt-get install haproxy
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg_copia
cat /dev/null > /etc/haproxy/haproxy.cfg
vi /etc/haproxy/haproxy.cfg
global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        #debug
        #quiet
        user haproxy
        group haproxy

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

listen webfarm 192.168.0.99:80
       mode http
       stats enable
       stats auth someuser:somepassword
       balance roundrobin
       cookie SERVERID insert indirect
       option httpclose
       option forwardfor
       option httpchk HEAD /check.txt HTTP/1.0
       server webA 192.168.0.102:80 cookie A check
       server webB 192.168.0.103:80 cookie B check

Nótese la línea listen webfarm 192.168.0.99:80 es dónde configuramos la IP compartida, sin necesidad de agregar una interfaz de red virtual a ninguno de los servidores.

Ahora nos vamos al archivo /etc/default/haproxy e igualamos a 1 la variable ENABLED.

/etc/default/haproxy
# Set ENABLED to 1 if you want the init script to start haproxy.
ENABLED=1
# Add extra flags here.
#EXTRAOPTS="-de -m 16"

Ya con nuestra dirección IP compartida configurada, debemos tener un mecanismo que les diga a los servidores que deben estar escuchando por dicha IP, para esto instalaremos el heartbeat tanto en LB1 como en LB2:

apt-get install heartbeat

Luego de instalar el hearbeat, debemos permitir que el HAProxy se enlace con la IP compartida, para eso agregaremos una línea al archivo /etc/sysctl.conf:

nano  /etc/sysctl.conf
[...]
net.ipv4.ip_nonlocal_bind=1

Ahora ejecutamos:

sysctl -p

El siguiente paso es crear tres archivos de configuración para el heartbeat: /etc/ha.d/authkeys, /etc/ha.d/ha.cf, y /etc/ha.d/haresources. Los archivos /etc/ha.d/authkeys y /etc/ha.d/haresources deben ser idénticos tanto en LB1 como en LB2, sin embargo /etc/ha.d/ha.cf será diferente en una línea.

Ejecutamos lo siguiente en LB1 y LB2

nano /etc/ha.d/authkeys
auth 3
3 md5 clave_comun

He utilizado clave_comun como una cadena de texto a su preferencia que servirá para que los procesos del hearbeat en LB1 y LB2 pueden autenticar entre ellos. Este archivo debe tener permiso de lectura solo para el usuario root, para eso ejecutamos lo siguiente en LB1 y LB2:

chmod 600 /etc/ha.d/authkeys

Continuamos entonces con el archivo /etc/ha.d/ha.cf en el LB1, pero antes es necesario conocer el nombre del host en que estamos trabajando, ya que lo usaremos en el archivo, para esto basta con ejecutar:

uname -n
nano /etc/ha.d/ha.cf
#
#       keepalive: cantidad de segundos entre verificaciones
#
keepalive 2
#
#       deadtime: segundos para declarar el host innacesible
#
deadtime 10
#
#       Puerto UDP para las comunicaciones ppp-udp
#
udpport 694
bcast eth0
mcast eth0 225.0.0.1 694 1 0
ucast eth0 192.168.0.101
#       Interfaces para el uso del heartbeat
udp eth0
#
#       Facilidad syslog()/logger
#
logfacility local0
#
#       Identificar el servidor clúster, aqui es donde va el nombre del host del uname -n
#
node nombre_del_host_LB1
node nombre_del_host_LB2

Procedemos con el mismo archivo pero en LB2, solo una línea del ucast es la que cambia:

nano /etc/ha.d/ha.cf
#
#       keepalive: cantidad de segundos entre verificaciones
#
keepalive 2
#
#       deadtime: segundos para declarar el host innacesible
#
deadtime 10
#
#       Puerto UDP para las comunicaciones ppp-udp
#
udpport 694
bcast eth0
mcast eth0 225.0.0.1 694 1 0
ucast eth0 192.168.0.100
#       Interfaces para el uso del heartbeat
udp eth0
#
#       Facilidad syslog()/logger
#
logfacility local0
#
#       Identificar el servidor clúster, aqui es donde va el nombre del host del uname -n
#
node nombre_del_host_LB1
node nombre_del_host_LB2

Tanto en LB1 como en LB2 ejecutamos lo siguiente:

nano /etc/ha.d/haresources
nombre_del_host_LB1 192.168.0.99

Ya con todo esto configurado, procedemos a iniciar el servicio heartbeat en LB1 y LB2:

/etc/init.d/heartbeat start

Vamos a verificar que LB1 esté escuchando realmente por la IP compartida, para esto ejecutamos:

ip addr sh eth0

El resultado sería algo muy parecido a esto:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
     link/ether 00:0c:29:63:f7:5c brd ff:ff:ff:ff:ff:ff
     inet 192.168.0.100/24 brd 192.168.0.255 scope global eth0
     inet 192.168.0.99/24 brd 192.168.0.255 scope global secondary eth0:0
     inet6 fe80::20c:29ff:fe63:f75c/64 scope link
        valid_lft forever preferred_lft forever

Lo podemos validar con un ifconfig:

ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0c:29:63:f7:5c
           inet addr:192.168.0.100  Bcast:192.168.0.255  Mask:255.255.255.0
           inet6 addr: fe80::20c:29ff:fe63:f75c/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           RX packets:15838 errors:0 dropped:0 overruns:0 frame:0
           TX packets:8761 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:1000
           RX bytes:21111826 (20.1 MiB)  TX bytes:844920 (825.1 KiB)
           Interrupt:18 Base address:0x1400

 eth0:0    Link encap:Ethernet  HWaddr 00:0c:29:63:f7:5c
           inet addr:192.168.0.99  Bcast:192.168.0.255  Mask:255.255.255.0
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           Interrupt:18 Base address:0x1400

 lo        Link encap:Local Loopback
           inet addr:127.0.0.1  Mask:255.0.0.0
           inet6 addr: ::1/128 Scope:Host
           UP LOOPBACK RUNNING  MTU:16436  Metric:1
           RX packets:8 errors:0 dropped:0 overruns:0 frame:0
           TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:0
           RX bytes:560 (560.0 B)  TX bytes:560 (560.0 B)

En el LB2 podemos ejecutar lo mismo, y darnos cuenta que no está escuchando por la IP compartida siempre y cuando LB1 esté accesible por el heartbeat:

ip addr sh eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
     link/ether 00:0c:29:be:7b:3b brd ff:ff:ff:ff:ff:ff
     inet 192.168.0.101/24 brd 192.168.0.255 scope global eth0
     inet6 fe80::20c:29ff:febe:7b3b/64 scope link
        valid_lft forever preferred_lft forever
ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0c:29:be:7b:3b
           inet addr:192.168.0.101  Bcast:192.168.0.255  Mask:255.255.255.0
           inet6 addr: fe80::20c:29ff:febe:7b3b/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           RX packets:15689 errors:0 dropped:0 overruns:0 frame:0
           TX packets:9010 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:1000
           RX bytes:21151306 (20.1 MiB)  TX bytes:879697 (859.0 KiB)
           Interrupt:18 Base address:0x1400

 lo        Link encap:Local Loopback
           inet addr:127.0.0.1  Mask:255.0.0.0
           inet6 addr: ::1/128 Scope:Host
           UP LOOPBACK RUNNING  MTU:16436  Metric:1
           RX packets:8 errors:0 dropped:0 overruns:0 frame:0
           TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:0
           RX bytes:560 (560.0 B)  TX bytes:560 (560.0 B)

Ya solo nos resta iniciar el HAProxy en LB1 y LB2:

/etc/init.d/haproxy start

Habrán notado que utlicé las siguientes opciones mientras configuraba el HAProxy:

options stats enable
stats auth someuser:somepassword

Esto es para tener acceso a las estadísticas de nuestro balanceo de carga mediante una interfaz web en la IP compartida, lo cual sería http://192.168.0.99/haproxy?stats. Estas estadísticas lucen de la siguiente manera:

Estadísticas del HAProxy

No hay respuestas

  1. 18 junio, 2013

    […] Configurar balanceador de carga y alta disponibilidad en Debian […]

Deja un comentario

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


*

Markup Controls
Emoticons Smile Grin Sad Surprised Shocked Confused Cool Mad Razz Neutral Wink Lol Red Face Cry Evil Twisted Roll Exclaim Question Idea Arrow Mr Green