Pruebas sobre la instalación

Cada backend tiene en su DocumentRoot los siguientes archivos:

  • index.html, con el nombre del backend.
  • sesion.php. Inicia la sesión del usuario. Sirve para probar que HAProxy dirige al usuario siempre al mismo backend donde inició la sesión.
  • cerrar\_sesion.php. Sirve para cerrar la sesión del usuario.
  • check.txt. Este archivo puede estar en blanco (touch /var/www/check.txt) y sirve para el chequeo de disponibilidad que hace HAProxy.

Además, el backend1 usado para servidor de imágenes tiene una imagen de Tux: /var/www/tux.jpg.

Este es el archivo index.html.

<pre><html><body><h1>< works!</h1>;
  <p>backend1&lt;/p>
 <p><img src="tux.jpg"/<p>
</body></html></pre>

Es el mismo en los dos backends. Notar que hace referencia a la imagen tux.jpg. Esa imagen se encuetra en backend1, pero no en backend2. Sin embargo, aunque la página se cargue de backend2, la imagen se carga igual, porque el balanceador redirige la petición a backend1 (donde se encuentra la imagen).

El archivo sesion.php, para iniciar la sesión del usuario e imprimir el backend que lo atiende:

<pre><?php
session_start();
if (!isset($_SESSION['server']))
{
  echo "seteando server=backend1";
  $_SESSION['server'] = "backend1";
}
echo "El servidor es: ".$_SESSION['server'];
?<</pre>
El archivo cerrar_sesion.php, para cerrar la sesión del usuario (tomado de php.net):
<pre><?php
// Initialize the session.
// If you are using session_name("something"), don't forget it now!
session_start();</pre>
<pre>// Unset all of the session variables.
$_SESSION = array();</pre>
<pre>// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (ini_get("session.use_cookies")) {
  $params = session_get_cookie_params();
  setcookie(session_name(), '', time() - 42000,
  $params["path"], $params["domain"],
  $params["secure"], $params["httponly"]
);
}</pre>
<pre>// Finally, destroy the session.
session_destroy();
echo "sesion borrada en backend1";
?<</pre>

El archivo check.txt no lo incluyo porque está en blanco.

Explicación de los casos

Entonces, ¿cómo queda todo funcionando? Veamos algunos casos:

  • El primer caso es el más sencillo: el cliente solicita la página http://192.168.0.123, es decir, el index.html. La solicitud primero pasa por HAProxy, quien verifica que el usuario no ha iniciado sesión, por lo que aplica el algoritmo de RoundRobin teniendo en cuenta el peso de cada servidor.
  • El cliente recibe la página de uno de los backends, pero dado que la página requiere una imágen, tux.jpg, el navegador realiza otra petición. Esta vez, cuando la solicitud pasa por HAProxy, el balanceador se da cuenta que el cliente solicita una imagen, entonces manda la solicitud directamente al backend ”imagenes”; dado que ese backend tiene un solo servidor, backend1, es ese servidor quien responde con la imagen.
  • Para verificar esto puede analizar los logs de los apaches corriendo en los backends, y verá cómo la imágen siempre se carga de backend1, mientras que las peticiones por la página web (index.html), siempre se reparten entre los dos backends.
  • El segundo caso es para las sesiones: Podemos realizar una petición a http://192.168.0.132/sesion.php para iniciar sesión en un backend. La petición pasa primero por HAproxy, quien elije un backend dependiendo del algoritmo de RoundRobin y teniendo en cuenta el peso de cada backend (recordemos que en esta primera instancia el usuario no inició sesión).
  • Recién cuando el backend atiende la solicitud, el usuario inicia sesión. Cuando se setea la cookie de la sesión, HAProxy la reconoce y le antepone -opción prefix- el identificador del servidor de backend (cookie A o B).
  • Luego, cuando el cliente accede nuevamente a esa URL, HAProxy lo envía al mismo backend en donde inició sesión.
  • Por último, probemos cerrar_sesion.php. Podemos solicitar http://192.168.0.123/cerrar_sesion.php. Cuando la petición pase por HAProxy, el balanceador reconoce la cookie de sesión y lo envía al backend correspondiente. Allí el usuario recupera su sesión, la limpia y finalmente la destruye. Luego, los próximos accesos que realice el cliente podrán ser a backend1 o backend2, dependiendo del RoundRobin y el peso de cada servidor.

Notas y Tips

Veamos algunos tips para trabajar con Apache y HAProxy. Para ver el tráfico http en tiempo real se puede usar tcpdump, tanto en los backends como en el balanceador (tcpdump -n port 80). Con esto veremos el tráfico iniciado por el cliente y los chequeos hechos por el balanceador (los requerimientos al archivo check.txt).

Una forma fácil de verificar qué servidor de backend atiende la petición es viendo sus logs de acceso. En mi caso, tail -f /var/log/apache2/access.log. Esto puede cambiar de acuerdo al sistema. Para el primer ejemplo veríamos cómo es el apache del backend1 quien siempre sirve la imagen, mientras que son los dos apaches que sirven el archivo html.

Para bajar un backend de forma segura se puede renombrar el archivo check.txt. De esta forma HAProxy lo detecta como caído y deja de enviarle peticiones. Es así que estamos seguros que nadie más está accediendo a este backend. Esto sirve, por ejemplo, cuando se quiere hacer una actualización del sistema o se lo quiere reiniciar.

Estadíticas de carga para HAProxy. Notar el servidor backend2 caído. Todas las peticiones de los clientes se dirigen a backend1. Esta ''caída'' la simulé haciendo "mv /var/www/check.txt /var/www/check.txt-" en backend2.

Una topología que incrementaría la seguridad es disponer de dos IPs para el balanceador, una en una red privada y otra en la pública. Los clientes estarían en la red pública y los backends en la privada. Aquí tiene más sentido el parámetro que vimos más arriba en la configuración de los backends (source ip_del_balanceador), dado que en este escenario el balanceador tendría dos IPs, una privada para comunicarse con los backends, y un pública, para comunicarse con los clientes.

Conclusiones

Veamos algunas conclusiones luego de haber trabajado con HAProxy.

  • Centralizar código: El código de la aplicación estará replicado en todos los servidores de backend. Una opción es instalar un servidor de NFS con el código y montar ese directorio en los servidores de backend.
  • Chequeo de backends: Es necesario alguna tener forma de chequear que los backend usados están online. Además, para migrar o actualizar un servidor de backend se puede modificar el archivo \emph{check.txt} para que el balanceador lo crea caído. A partir de ese momento se puede trabajar tranquilamente con el backend sabiendo que el balanceador no le enviará tráfico.
  • El balance de carga no es backup ni HA: Creo que es importante aclarar que el hecho de tener un sitio balanceado no proporciona HA (High Availability, Alta Disponibilidad) ni sirve como Backup. La principal motivación para balancear es la performance.
  • El ambiente puede ser heterogeneo: Los backends pueden ser heterogeneos, pues no es necesario que todos tengan el mismo sistema operativo o servidor web, y mucho menos tener el mismo hardware.
  • Las herramientas son libres y gratuitas: Quitando los costos de hardware y configuración, se puede montar un servidor web balanceado y con HA (usando keepalived, por ejemplo) con herramientas de licencia libres.
  • No depende del lenguaje de programación: El balanceador elegido es independiente del lenguaje de programación. Por ejemplo, se pueden balancear servidores de backend Windows con IIS y páginas en ASP utilizando HAProxy sobre Linux.

Referencias