Balanceo de carga y escalabilidad con ASP.net, primer contacto

Una de los problemas a los que no he tenido la oportunidad de enfrentarme, de manera profesional, es manejar carga de servidores web, así que esta semana me he dispuesto a montar un sistema para balancear dos sitios ASP.net alojados en Azure. En este artículo veremos cómo ponerlo en marcha. Ha sido una aventura muy interesante, así que te invito a que sigas leyendo.

La arquitectura

Últimamente tenemos muchas capas de servicios que nos abstraen de la lógica necesaria para todo esto: los Azure Websites pueden escalar automáticamente, Azure proporciona su propio balanceador de carga a través del componente Traffic Manager, y Amazon tiene su Elastic Load Balancer.

Para este escenario, sin embargo, quería montarlo de manera un poco más artesanal, y esta es la arquitectura:

  • Dos Azure Websites diferentes, uno alojado en North Europe y otro en West US.
  • Una máquina virtual, con Linux (aquí meteremos el balanceador de carga) alojada en East Asia.

¿Por qué Linux? Linux tiene una gran importancia en la actualidad en el área de servidores. Llevaba tiempo sin trabajar con él y además parecía divertido combinar las diferentes plataformas.

Identificando las instancias

Un detalle que puede pasar desapercibido si vamos a nuget.org, es que en el pie de página identifican el servidor en el que estamos (You are on XXXX), con lo cual, si refrescamos la página, veremos otro servidor.

nuget

Para esta prueba sería muy útil saber qué servidor está sirviendo los contenidos, así que lo primero que haremos será crear un proyecto por defecto de ASP.net, y sustituir la acción Index de HomeController por:

public ActionResult Index()
{
    ViewBag.Title = "Home Page";
    ViewBag.ServerName = System.Environment.MachineName;
    return View();
}

Por otro lado sustituimos el código de Index.cshtml (la vista) por:

<div class="jumbotron">
    <h1>Load balancing testing</h1>
    <p class="lead">You are on @ViewBag.ServerName</p>
</div>

El resultado (en local) es este:

Lb_local

Una vez que tenemos nuestro sitio funcionando, el siguiente paso es subirlo a dos servidores diferentes, que son en este caso:

Cada máquina tiene un identificador diferente, de esta manera, cuando hagamos el balanceo de carga, podremos saber en qué máquina estamos.

Agregando el balanceador

Para el balanceador he optado por un Ubuntu Server 12 hospedado en una máquina virtual también en Azure. Una de las cosas que no debemos olvidar es abrir el puerto 80 para poder recibir tráfico normal de Internet (para este ejemplo dejamos de lado temas relacionados con HTTPS, certificados SSL y similares). Con Linux tenemos muchas opciones. Estuve probando las siguientes:

Fair

Es una opción muy sencilla para empezar, pero tiene un problema, ya que necesita un componente en cada máquina que queremos balancear, lo cual nos limita el radio de acción. Puede que en otra ocasión…

Balance

A diferencia de Fair, no necesita un componente en cada máquina, lo que era un plus. Sin embargo, el hecho de estar basado en comandos y no en configuración no me acababan de convencer. Finalmente, no pude hacerlo funcionar por problemas de puertos, permisos y otros.

HAProxy

HAproxy es, según he podido leer, el balanceador más completo y configurable. Funciona mediante un fichero de configuración y necesita direcciones IP fijas, ya que, según los ejemplos, está pensado para que los servidores a balancear estén en la misma red. Después de varios intentos, no conseguí que el balanceador funcionara, así que desistí.

Apache

Por último, descubrí que Apache tiene un módulo específico para proxy y balanceo, que se parecía bastante a lo que necesitaba. Aunque parezca matar moscas a cañonazos, al final resultó ser bastante fácil de configurar.

Instalando y configurando Apache

El primer paso es instalar Apache en nuestro sistema:

sudo apt-get install apache2

El siguiente paso es habilitar los componentes necesarios para el balanceador:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer

Una vez tenemos activados todos los componentes, debemos escribir la configuración al final (justo antes de la directiva </IfModule>) de /etc/apache2/mods-available/proxy.conf:

<VirtualHost *:80>
ProxyRequests off
<Proxy balancer://mycluster>
    # WebHead1
    BalancerMember http://scaledemo7844.azurewebsites.net:80

    # WebHead2
    BalancerMember http://scaledemoamerica.azurewebsites.net:80

                Order Deny,Allow
                Deny from none
                Allow from all

    ProxySet lbmethod=byrequests

</Proxy>
        ProxyPass / balancer://mycluster/
</VirtualHost>

Finalmente reiniciamos el servicio:

sudo apache2 restart

Si todo ha ido bien (y espero que sí), podemos acudir a http://ubuntubalancer.cloudapp.net/ (actualmente offline), donde accederemos indistintamente a cualquiera de las dos instancias y podremos ver su identificador.

Conclusiones

En este artículo hemos visto cómo podemos escalar nuestra página y usar un balanceador intermedio para gestionar las peticiones. Es un primer paso y todavía quedaría mucho por hacer. En el próximo artículo de esta serie, veremos cómo compartir la sesión entre las diferentes páginas que componen nuestro pequeño cluster.

Enlaces Interesantes:

BitNami Trac con Windows 8

BitNami es una plataforma que facilita el despliegue de aplicaciones como Trac, Redmine, WordPress, u otros en múltiples plataformas sin que sea necesario instalar o configurar servicios. Se integra perfectamente, aunque al actualizar Windows 8 pueden surgir incidencias, sobre todo relativas a los puertos.

En este caso, tras actualizar, el servicio dejó de funcionar. Tras comprobar que en efecto, el servicio estaba detenido (usando la consola de administración, accesible mediante services.msc) y otras cosas, descubrí que el error estaba relacionado con el puerto, ya que el servidor estaba escuchando en el puerto 80. Para comprobarlo, se puede usar el monitor de recursos (Ctrl+Shift+Esc, pestaña Network, apartado Listening Ports).

Por lo que he podido averiguar, tenía instalado Internet Information Services, o, al menos parte de él. La parte suficiente como para que me devolviera un bonito error 404. En este caso se plantean 2 soluciones.

Cambiar el puerto en IIS

Para ello se accede a la consola de administración de Internet Information Services (IIS desde el menú buscar), y en el apartado Default Web Site, seleccionar Bindings.

Si no se encuentra IIS, es posible que no esté instalado el administrador, para lo que habrá que acudir a Agregar o quitar programas, a la sección Agregar o quitar características de Windows y seleccionar Internet Information Services/Web Management Tools/IIS Management Console

En este menú, tan solo es necesario cambiar el puerto 80 a otro puerto, como 8080. Para que tenga efecto se tendrá que reiniciar el servidor usando la opción Restart situada debajo de Manage Website

Finalmente se podrá reiniciar apache abriendo una consola de comandos y escribiendo

net start tracApache

Cambiar el puerto en Apache

La otra alternativa pasa por editar la configuración de Apache y cambiar el puerto, editando el el fichero httpd.conf, situado en C:\Program Files\BitNami Track Stack\apache2\conf, y editar la siguiente línea, asignando otro puerto (por ejemplo el 8080):

Listen 80

Finalmente se podrá reiniciar apache abriendo una consola de comandos y escribiendo

net start tracApache

Conclusiones

Es importante cuando se trabaja con este tipo de soluciones, tener en cuenta los puertos, ya que nos pueden ahorrar algunos dolores de cabeza.

Más información:

Ant y NAnt, gestión inteligente de código fuente

Apache Ant, es originalmente a java lo que la herramienta Makefile a gcc, permite automatizar la compilación, pruebas, documentación, distribución y puesta en funcionamiento de nuestras aplicaciones.

NAnt es una implementación del sistema para la plataforma .net, así que gran parte de lo contado aquí es válido para ambas plataformas.

Introducción

Ant está basado en tareas, y se diferencia de comandos de shell y ficheros make en que la sintaxis está basada en XML en vez de comandos. Esto permite que sea independiente del sistema operativo en el que se implementa.

Respecto a la compatibilidad, los editores actuales de Java (NetBeans y Eclipse) lo soportan soportan nativamente, de hecho concretamente en Netbeans la compilación se hace a través de un fichero Ant que se genera automáticamente.

Ejemplo de uso

El siguiente ejemplo muestra un fichero estándar de Ant, contiene información del proyecto, y las diferentes tareas llamadas target que tienen el atributo correspondiente al nombre, y las diferentes acciones a realizar.

<project name="MyProject" default="dist" basedir=".">
    <description>
        simple example build file
    </description>
  <!-- set global properties for this build -->
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist"  location="dist"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
        description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
        description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
        description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

Las acciones que se muestran en este ejemplo son:

  • Creación / borrado de directorios.
  • Compilación.
  • Distribución y empaquetado.
Estas acciones serán válidas para cualquier proyecto de principiantes, aunque se pueden hacer muchas más:
  • Copia a un servidor remoto vía FTP.
  • Copia de ficheros
  • Compresión en zip.
  • Pruebas.
  • Generación de documentación (Existe un plugin para ejecutar Doxygen).

Además, debido a que está integrado con los entornos de desarrollo, se pueden ejecutar tareas en momentos clave, como antes de la compilación, justo después, justo antes del empaquetado, etc.

Un escenario posible podría ser el de la entrega de una versión de un producto, donde con un único script podríamos compilar, pasar las pruebas unitarias, de integración y de aceptación, generar la documentación, ofuscar el código, empaquetarlo, generar un ejecutable, enviarlo vía FTP y recibiendo un e-mail con los resultados.

Conclusiones:

Una de las cosas que es deseable tener en un proyecto de desarrollo, es este script mágico que nos da un entregable del producto en un paso. Ant permite llevar a cabo esta tarea, ahorrarnos complicaciones y mejorar nuestra productividad.

Más información:

http://ant.apache.org/manual/index.html