Thursday, August 29, 2013

Sesiones web en alta disponibilidad

En este post voy a tratar de explicar con una demostración de concepto, como dotar de alta disponibilidad a las sesiones de una aplicación web.

 Debido a mis limitaciones técnicas, voy a utilizar lo siguiente:
  •  nginx como balanceador web 
  •  php como lenguaje de programación 
  •  apache como servidor web 
  •  memcached,  la estrella en todo esto. 

todo esto sobre Debian Wheezy.

Cuando se tiene una granja de servidores web para una aplicación, se requiere que la información almacenada en la sesión de usuario se mantenga y pueda ser accedida por cualquiera de los servidores, incluso, cuando alguno de ellos falla. Existen varias técnicas, algunas más complicadas que otras. Yo voy a hablar de como hacerlo con memcached.

Escenario

 El escenario descrito como bloques funcionales será el siguiente:
  •  Un balanceador web (nginx) 
  •  Dos servidores web (apache + php) 
  •  Tres instancias de memcached 

El blanceador (nginx) tendrá como servidores de backend a los dos apaches que son los que alojarán el código php del supuesto sitio web. Para efectos de esta demostración de concepto, todos estos bloques funcionales estarán corriendo en la misma máquina :) Espero que esto no lleve a confusión.

Comenzamos con la instalación y configuración de los bloques funcionales.

Balanceador web (nginx) 

Instalamos nginx de la siguiente manera:
# aptitude install nginx


 Nos vamos al directorio /etc/nginx/sites-available y creamos el archivo mem-test que tendrá la configuración de nuestro balanceador. Este es el contenido del archivo:
upstream mem-test {
                   server 127.0.0.1:8081;   
                   server 127.0.0.1:8082;

}

server {
      listen 8080;
      location / {
                  proxy_pass http://mem-test;
      }
}


Como se aprecia, voy a tener dos servidores web escuchando en los puertos 8081 y 8082. Será mi "granja" que después configuraré. La sección siguiente le dice a nginx que escuche en el puerto 8080 (mi blanceador) y que todo lo que llegue lo pase a los servidores descritos arriba.

Nos vamos al directorio /etc/nginx/sites-enabled, creamos un link simbólico que apunte al archivo creado
# cd /etc/nginx/sites-enabled
# ln -s /etc/nginx/sites-aviable/mem-test

y borramos el link "default".

Listo, ya tenemos nginx configurado como balanceador, nos queda arrancarlo:
# service start nginx

No deberían salir errores si se siguieron los pasos descritos.

Servidores web (apache + php) 

Instalamos apache y php de la siguiente manera:
# aptitude install apache2 libapache2-mod-php5

Configuramos dos virtualhosts en apache añadiendo
NameVirtualHost *:8081
NameVirtualHost *:8082
Listen 8081
Listen 8082

al archivo /etc/apache2/ports.conf y creamos dos archivos de configuración llamados server01 y server02 en /etc/apache2/sites-available. Estos son los que yo creé:
server01
<VirtualHost *:8081>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/server01
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/server01>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>


server02
<VirtualHost *:8082>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/server02
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/server02>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Creamos los directorios /var/server01 y  /var/server02 que alojarán las páginas de nuestros sitios web
# mkdir /var/server01 /var/server02

Activamos los sitios:
# a2ensite server01 server2

Memcached 

Instalamos memcached y el soporte de php para memcache
# aptitude install memcached php5-memcache

Creamos los tres archivos de configuración de las instancias de memcached
cp /etc/memcached /etc/memcached_inst01.conf
cp /etc/memcached /etc/memcached_inst02.conf
cp /etc/memcached /etc/memcached_inst03.conf

En mi escenario, estos son los puertos en los que escuchan las diferentes instancias de memcached:
  • inst01: 11211 
  • inst02: 11212 
  • inst03: 11213 


Eso se hace modificando la línea donde dice "-p" en cada archivo de configuración y colocando el valor correspondiente.

Arrancamos memcached
/etc/init.d/memcached start
Starting memcached: memcached_inst01.
Starting memcached: memcached_inst02.
Starting memcached: memcached_inst03.

Podemos comprobar que están corriendo con un ps.

php5-memcache 

Para que php use memcache para almacenar las sesiones, hay que realizar lo siguiente:
En el archivo /etc/php5/apache2/php.ini colocar:
session.save_handler = memcache
session.save_path = "tcp://127.0.0.1:11211,tcp://127.0.0.1:11212,tcp://127.0.0.1:11213"

y en el archivo /etc/php5/apache2/conf.d/20-memcache.ini añadir:
memcache.session_redundancy=4

Nota muy importante, el valor de memcache.session_redundancy debe ser igual al número de instancias de memcached +1.

Reiniciamos el apache:
# service apache2 stop
# service apache2 start

Solo falta escribir un código de prueba. En cada uno de los DocumentRoot de los virtualhosts, creamos un archivo llamado index.php como el siguiente para comenzar:
<?session_start() ?>
<html>
<head>
        <title>Server01</title>
</head>
<body>
Server01
<? if(empty($_SESSION['contador'])) {?>

<p>El contador est&aacute; vac&iacute;o</p>
<? }else {?>
<p>Valor actual del contador: <? echo $_SESSION['contador'];?></p>
<p>Sumamos 1 al contador</p>
<? $_SESSION['contador'] = $_SESSION['contador'] + 1;}?>
</body>
</html>

Hay que tener en cuenta que cada index.php debe reflejar el nombre de su respectivo servidor virtual.

Ahora, con cualquier navegador (yo usé lynx) podemos probar con la URL
http://localhost:8080

y recargamos la página varias veces.

Esta prueba debe mostrar _siempre_ que la variable "contador" está vacía lo cual es lógico. Lo único que debería variar es el nombre del servidor, que debe alternarse entre server01 y server02 con lo que sabemos que el balanceador está funcionando.

Una vez confirmado esto, modificamos el index.php de server02 para que quede de esta manera:
<? session_start() ?>
<html>
<head>
        <title>Server02</title>
</head>
<body>
Server02
<? if(empty($_SESSION['contador'])) {?>
<p>El contador est&aacute vac&iacute;o</p>
<? $_SESSION['contador'] = 1;
 }else {?>
<p>Valor actual del contador: <? echo $_SESSION['contador'];?></p>
<p>Sumamos 1 al contador</p>
<? $_SESSION['contador'] = $_SESSION['contador'] + 1;}?>
</body>
</html>

Al recargar la URL debemos observar como el contador comienza a incrementarse después de haber contactado a server02.

Para probar que se mantienen las sesiones al ocurrir un fallo, procedemos a parar las instancias de memcached teniendo en cuenta que hay que dejar el menos una corriendo.
# /etc/init.d/memcached stop inst01

Al recargar la URL deberíamos observar que el contador se sigue incrementando demostrando así que tenemos alta disponibilidad con las sesiones web.

Esto es solo una demostración de concepto pero se evidencia la flexibilidad y redundancia de memcached para guardar y mantener la información de las sesiones web.

No comments:

Post a Comment