Node.js sobre IaaS en Ubuntu

Cuando hablamos de servicios PaaS todo es mucho más fácil que cuando manejamos máquinas virtuales. Despliegas tu aplicación en la plataforma y Azure se encarga de todo lo demás: balanceo de carga, alta disponibilidad, auto-escalado, backups, actualizaciones, seguridad, monitorización y muchas cosas más. Pero sabemos que no siempre es posible. A veces nuestra aplicación necesita funcionalidades que la plataforma todavía no nos ofrece, o puede que nuestra aplicación todavía no esté totalmente preparada para escalar o utilizar algunos servicios como el almacenamiento distribuido. En esos casos tendremos que desplegar nuestra aplicación en un entorno donde podamos configurar e instalar todo lo necesario para nuestra aplicación. Para ello tenemos múltiples opciones:

  • Usar una máquina virtual básica acompañada de un script de instalación durante el inicio
  • Crear una imagen Docker con todo lo necesario, que luego podremos desplegar en el Azure Container Service
  • Crear una imagen personalizada con todo lo necesario para ejecutar nuestra aplicación

En este artículo veremos cómo preparar nuestra VM básica para más adelante crear un conjunto de escalado automático que nos permitirá usar los beneficios que nos proporciona Azure en cuanto a escalado y alta disponibilidad de nuestro servicio.

IaaS y el escalado

Cuando usamos máquinas virtuales, tanto en Cloud como On-Prem, y nuestra aplicación necesita escalar, necesitamos:

  • Una imagen del sistema operativo con todo lo básico para nuestra aplicación ya instalado. Dicha imagen tiene que estar preparada para arrancar como una nueva máquina, es decir, no debe que contener valores que tienen que ser únicos, como el nombre de la máquina, opciones de usuario, etc. Esto en Windows se consigue con el comando sysprep.
  • Scripts que desplieguen de forma automatizada los paquetes y aplicaciones que no vayan en la imagen base
  • Un conjunto de máquinas físicas y/o virtuales donde desplegar esas imágenes
  • Un balanceador de carga, que establezca qué máquinas están disponibles y dirija el tráfico en función de la carga de cada una de las VM
  • Un orquestador, que gestione el despliegue de las imágenes y la actualización de las mismas
  • Un conjunto de scripts que arranquen y paren las máquinas en función de las necesidades y de las métricas de uso establecidas (uso de CPU, memoria, cola de llamadas, etc.), si queremos automatizar el escalado

Todo esto puede llegar a ser bastante complicado, por suerte, hay muchos productos que nos permiten gestionar un despliegue en alta disponibilidad.

En Azure, disponemos de los conjuntos de escalado (Azure Scale Sets) que ya cuentan con muchas de las funcionalidades que necesitamos para tener nuestra aplicación en alta disponibilidad, con un escalado flexible con el que podemos añadir y quitar máquinas para ajustar el coste de nuestra solución a las necesidades reales de nuestros usuarios.

En este artículo vamos a crear una máquina virtual básica que nos servirá en el próximo artículo como base para el conjunto de escalado.

Nota: si quieres hacer el mismo ejercicio que presento en este artículo necesitarás:

Creación de la máquina virtual

Vamos a crear una imagen de un Ubuntu 16, en la que instalaremos Node.js, una pequeña aplicación de ejemplo y configuraremos Node como un servicio para que arranque con la máquina.

Empezamos creando una VM en Azure con la imagen de Ubuntu 16, aunque podéis elegir vuestra distribución favorita:

image_thumb1

Al crear la VM, recordad alojarla en la misma región donde luego crearéis el Scale Set, o si no tendremos que mover luego la imagen:

image_thumb6

Una vez creada, podremos acceder por ssh a la máquina con las credenciales que hemos creado, una aplicación como Putty nos servirá:

image_thumb5

Dentro de la máquina vamos a instalar node, en Ubuntu lo podemos hacer con el gestor de paquetes con los siguientes comandos:

 {
  sudo apt-get update -y;
  sudo apt-get upgrade -y;
  curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -;
  sudo apt-get install -y nodejs;
}

Ahora, vamos a crear una carpeta con una aplicación node en el directorio raiz. No nos sirve el del usuario porque luego lo borraremos durante la preparación.

 {
 sudo mkdir /nodeserver;
 cd /nodeserver;
 sudo sh -c 'echo '"'"'var http=require("http"),id=Math.floor(1e3*Math.random())+1,server=http.createServer(function(t,e){e.writeHead(200,{"Content-Type":"text/html"}),e.write("<html><head>"),e.write("<h1>Hello World</h1>"),e.write("id:"+id+"<br>"),e.write("time:"+Date.now()+"<br>"),e.end("")}),port=process.env.PORT||80;console.log("Listening on port "+port),server.listen(port);'"'"' > hello.js';
 sudo npm init –y;
}

Una vez tenemos el código en node lo configuramos para que arranque automáticamente. Para ello usaremos systemd que ejecutará y vigilará nuestro servidor node desde el arranque de la máquina:

 {
 cd /nodeserver;
 sudo sh -c 'echo "[Unit]\nDescription=node http server\n[Service]\nWorkingDirectory=/nodeserver\nExecStart=/usr/bin/node .\nRestart=always\n[Install]\nWantedBy=multi-user.target\n" > mainsite.service';
 sudo systemctl daemon-reload;
 sudo systemctl enable /nodeserver/mainsite.service;
 sudo systemctl start mainsite.service;
}

Probamos nuestro servidor

Si ahora queremos probar nuestro servidor, necesitaremos abrir el puerto en las reglas de NAT de nuestro Network Security Group:

image_thumb8

Comprobamos que ya abre nuestro Hello World a través de la url o IP pública y podemos empezar con el siguiente paso, la exportación del disco para generar una imagen base.

Exportación de la máquina virtual

Una vez tenemos nuestra VM en marcha y funcionando, necesitaremos generar una imagen que nos sirva para poder desplegar tantas máquinas idénticas como necesiten nuestros usuarios. El primer paso es llamar al agente de azure desde dentro de la máquina virtual para avisar de que vamos a generalizar la máquina, de forma que borre la información del usuario, claves ssh y el nombre del equipo:

  sudo waagent deprovision+user

Limpio el OS, nos vamos a nuestro equipo cliente con el Azure Xplat-CLI instalado y ejecutamos los siguientes comandos para parar la máquina, generalizar y exportar el .vhd del sistema:

 azure vm deallocate -g <your-resource-group-name> -n <your-virtual-machine-name>
azure vm generalize –g <your-resource-group-name> -n <your-virtual-machine-name>
azure vm capture <your-resource-group-name> <your-virtual-machine-name> <your-vhd-name-prefix> -t <your-template-file-name.json>

Una vez exportado el disco duro, lo encontraremos en la misma cuenta de almacenamiento donde tenemos el vhd de la máquina virtual, en un contenedor llamado system:

 https://[Account name].blob.core.windows.net/system/Microsoft.Compute/Images/vhds/[your-vhd-name-prefix]-osDisk.[Unique ID].vhd

Fin de la primera parte

Esta imagen, junto con la plantilla .json que nos ha creado nos servirá para poder crear tantas veces como queramos una máquina idéntica, sin necesidad de volver a ejecutar todos los scripts de actualización e instalación. Esto nos proporcionará mucha más velocidad de reacción cuando necesitemos desplegar múltiples imágenes idénticas, como ocurre en el caso de los Virtual Machine Scale Sets. En el próximo artículo veremos cómo usar esta imagen para crear un VMSS personalizado.

Que la IaaS os acompañe!

@jmservera
Senior Technical Evangelist
Developer eXperience