Thursday, January 2, 2014

Configuración automática de un Web Role

Es común que nuestras aplicaciones web requieran de ciertas configuraciones especiales para poder funcionar. Ejemplos de estas configuraciones pueden ser ejecutar el application pool con una cuenta diferente a la cuenta por defecto, cambiar los parámetros de timeout y reciclado por defecto, apuntar un directorio virtual a una carpeta compartida remota, y cualquier otra cosa que se imaginen que pueda ser configurable en IIS.

Intuitivamente, todas estas configuraciones pueden ser realizadas conectándose por escritorio remoto a la instancia de nuestro Web Role (que en definitiva es una VM con IIS) y aplicadas manualmente tal como lo haríamos en un servidor en nuestro propio datacenter.

Sin embargo, esta opción no es muy recomendable para un Cloud Service debido a que todos los cambios "manuales" que hagamos sobre la configuración podrían desaparecer en el caso de que Windows Azure requiera re-aprovisionar el servicio (ej. debido a una falla de hardware). Esto último puede ocurrir en cualquier momento y sin aviso, por lo que es importante tenerlo en cuenta al planificar la migración de una aplicación a la nube.

Así mismo, una de las ventajas de utilizar un Cloud Service (ej. en lugar de una VM) es la capacidad de poder escalar rápidamente a una nueva configuración mas potente (ej. con mas instancias) y de esa forma atender una mayor demanda transaccional. Si cada vez que tuviéramos que agregar mas instancias, estuviéramos obligados a realizar varios cambios de configuración manuales, este esquema de escalabilidad elástica dejaría de ser tan transparente e inmediato.

Anatomía de un  Cloud Service

Un servicio en la nube o "Cloud Service" consiste en un contenedor lógico de uno o mas roles de tipo Web Role (ej. para aplicaciones web que requieren de IIS) o Worker Role (para aplicaciones que solo realizan procesamiento, ej. un proceso batch o un servicio windows). A su vez cada rol definido en el servicio puede estar compuesto por una o mas instancias (ej, para contar con alta disponibilidad).


Cada instancia corresponde efectivamente a una máquina virtual que alojará a nuestra aplicación, y a la cual podemos conectarnos mediante escritorio remoto una vez que nuestro cloud service está en línea. La topología definida inicialmente puede variar en el tiempo (ej. agregar o quitar instancias) sin que esto suponga detener el servicio.

Todo esto se define en un par de archivos XML de configuración (.cscfg y .csdef) y que pueden generarse fácilmente con Visual Studio y el Windows Azure SDK (Add New Project -> Cloud -> Windows Azure Cloud Service). Más información acerca de esto en: http://msdn.microsoft.com/en-us/library/windowsazure/ee405487.aspx

Para "publicar" un Cloud Service, se debe generar además un paquete de instalación, el cual consiste en un archivo de extensión ".cspkg" conteniendo todos los componentes de la aplicación (ej. assemblies, páginas web, imágenes, scripts, configuraciones, etc.). Este paquete también se genera fácilmente desde Visual Studio con el Windows Azure SDK.

Ciclo de Vida de un Web Role

Cuando subimos un nuevo paquete con la definición del Cloud Service, Windows Azure realiza varias cosas en un orden predeterminado:

  1. Interpreta el archivo con la definición del Cloud Service, a efectos de determinar los roles a crear y sus instancias.
  2. Aprovisiona el hardware necesario para satisfacer las necesidades del servicio y despliega las imágenes de las VMs que ejecutarán cada instancia
  3. Ejecuta tareas de inicialización definidas en archivo de definición del Cloud Service. Estas tareas pueden consistir en la ejecución de script (ej. .bat, .cmd) o ejecutable (.exe) durante la inicialización del servicio. En este punto puede ocurrir que el sitio web aún no haya sido creado por Azure, algo que debe ser tenido en cuenta por las tareas que ejecutemos aquí. Las tareas pueden ejecutarse sincrónicamente o asíncrónicamente de acuerdo a como hayan sido configuradas.
  4. Una vez finalizadas las tareas sincrónicas, ejecuta en cada instancia un programa llamado IISConfigurator.exe el cual es responsable por crear y configurar los sitios web definidos en el Web Role. La ejecución de este programa se realiza de manera asincrónica, por lo que el próximo paso no quedará a la espera de finalización para iniciar.
  5. Se dispara el evento "OnStart" de la clase RoleEntryPoint en el sitio web. Este evento puede ser interceptado agregando en nuestro sitio web una clase que herede de RoleEntryPoint y realice un override del método OnStart. Este es un buen lugar para agregar código de inicialización o de configuración de nuestro sitio web, previo a que este comience a recibir peticiones.
  6. Finalizado el evento OnStart, el role quedará en estado "ready" aceptando las peticiones que lleguen. En ese momento se disparará además el evento "Run" de RoleEntryPoint. Típicamente este evento se usa en los Worker Role para monitorear que la instancia siga "viva" mediante la aplicación de un bucle infinito y un Thread.Sleep.
  7. Si luego de un tiempo el role es reciclado o se solicita un shutdown del servicio, el evento OnStop de RoleEntryPoint es disparado a efectos de realizar cualquier tarea de limpieza que se necesaria. Algo importante a tener en cuenta es que este evento ejecuta bajo un límite de tiempo, y si nuestro código de limpieza demora mas de lo normal es posible que no pueda concluir.



Definir tareas de inicialización de un Role

Para definir una tarea de inicialización basta con editar el archivo .csdef de nuestro Cloud Service y agregar en la definición del role un nuevo elemento "Startup". Dentro de este elemento podemos definir uno o mas elementos de tipo "Task" con los siguientes atributos:

  1. commandLine: nombre de un archivo de comandos o ejecutable
  2. executionContext: dos posibilidades, "limited" o "elevated"
    1. limited: la tarea ejecutará sin privilegios especiales
    2. elevated: la tarea ejecutará con privilegios de administrador
  3. taskType: tres posibilidades, "simple", "background", "foreground"
    1. simple: la tarea es disparada bloqueando la instancia hasta que la tarea finalice. Si la tarea falla, la instancia no inicializará
    2. background: la tarea es disparada sin bloquear la inicialización de la instancia
    3. foreground: la tarea es disparada sin bloquear la inicialización de la instancia y esta no podrá finalizar en tanto no finalice la tarea.
Ejemplo:



Interceptar eventos del ciclo de vida del Role

Los eventos del ciclo de vida se encuentran definidos en la clase RoleEntryPoint contenida en el namespace/assembly Microsoft.WindowsAzure.ServiceRuntime.dll. Esta clase es abstracta y provee tres métodos para interceptar los diferentes eventos.


Basta crear una nueva clase en el sitio web (típicamente de nombre WebRole.cs), heredar de RoleEntryPoint, y finalmente realizar un override del método/evento que querramos interceptar.
Esto mismo se puede hacer también para cualquier otra aplicación no web que ejecute como Worker Role:


En el ejemplo anteriror podemos ver un Worker Rol que durante su inicialización crea una instancia de un ServiceHost para atender las llamadas a un servicio. Así mismo en el método Run realiza un bucle infinito en el cual verifica cada diez segundos que el servicio se encuentre disponible, y en caso de no estarlo alertar mediante un mensaje de error.

Conclusiones

Windows Azure Clound Service nos provee una solución de plataforma como servicio en la nube (PaaS) altamente escalable. Nuestras aplicaciones web pueden ejecutar en este tipo de entornos como una alternativa de menor costo de configuración que las soluciones de infraestructura pura en la nube (IaaS).
Diferentes mecanismos provistos por la plataforma nos peermiten definir y configurar los entornos de ejecución de manera automática, evitando el esfuerzo de re-configuraciones manuales al momento de incrementar la capacidad de procesamiento