La manera de construir y desplegar aplicaciones está sufriendo una revolución.

Las empresas están apostando cada vez más por un modelo de trabajo que pueda escalar, que soporte caídas minimizando sus consecuencias, y a la vez quieren asegurarse de que no haya pérdida de datos. El clásico deployment sobre bare metal tiene una serie de problemas que no siempre son asumibles en un mercado complejo como el actual.

En esta página explicaré cómo se construye y despliega una simple aplicación en PHP usando Nginx, Ansible, Docker, y el proveedor de máquinas virtuales Digital Ocean.

Digital Ocean es un portal que pone a disposición una arquitectura de máquinas virtuales llamadas droplets, muy flexibles y al mismo tiempo, económicas. Es perfecto para trabajar con cloud computing.

Pre-requisitos: para realizar este tutorial tienes que tener un ordenador con Ansible 2.3, Docker 17, y la librería de Python dopy 0.3.5.

Digital Ocean, al igual que otros proveedores como AWS, Google Cloud o Azure, tiene un API muy cómodo, y un específico módulo de Ansible, que usaremos para nuestro despliegue. El código fuente de este ejercicio se encuentra en este enlace.

En primer lugar, entraremos en Digital Ocean y generaremos un token de autenticación con posibilidad de escritura. Este token es secreto, ya que tiene pleno acceso a operar sobre la cuenta. Además, crearemos una clave ssh RSA añadida a la cuenta de DO.

Para generarla en una máquina Mac o Linux, usaremos el comando ssh-keygen, que nos generará una pareja de claves. SSH usa un sistema de autenticación con claves asimétricas.

La privada, llamada por defecto id_rsa, es necesaria para poder acceder a la consola de las máquinas virtuales. La pública, llamada por defecto id_rsa.pub, tendrá que ser añadida a la cuenta de DO, en el apartado correspondiente. Anotaremos el nombre de la clave, ya que es uno de los parámetros del playbook.

Cuando hayamos creado un droplet, añadiremos esta clave entre las habilitadas para el acceso a través de SSH. 

En el código fuente encontraremos dos carpetas: setup, que contiene los playbook, inventory y más ficheros usados por Ansible; la otra, Docker, contiene el código fuente de la propia aplicación y los ficheros necesarios para Docker.

La peculiaridad de Docker es que ejecuta la aplicación en un contenedor, que es un proceso en nuestra propia máquina, como cualquier otro proceso gestionado por el kernel, y que usa los mismos recursos hardware del resto de procesos. Es decir, a diferencia de un gestor de máquinas virtuales, no necesita un hypervisor para poder ejecutar aplicaciones.

Docker utiliza un file-system layered. Esta abstracción permite que el proceso dentro del contenedor, en lugar de acceder directamente al file system de la máquina, “vea” una capa con un sistema de ficheros propios de la aplicación que se está ejecutando.

Cuando exportamos una aplicación usando Docker, exportamos también el layer asociado, garantizando en este modo que la aplicación se ejecute exactamente de la misma manera en todos los entornos.

Otra diferencia con una máquina virtual tradicional es que Docker no necesita reservar recursos para cada contenedor, al igual que ocurre con virtual machines como VmWare, Xen o VirtualBox. La principal limitación de este sistema es que los contenedores solo pueden ejecutarse en ambiente Linux.

El playbook principal es generate_machines.yml. Éste aplica el role “sdw”, que genera un droplet en DO y recoge su IP. Un role en Ansible es un módulo aislado, que contiene una serie de reglas (playbook), unas variables y unos handlers que pueden ser exportados e importados en otra máquina con facilidad.

El fichero hosts es el inventory. Es un componente fundamental de Ansible, ya que recoge IPs y variables de las máquinas target. Aquí hay un inventory vacío, que contiene una serie de parámetros genéricos que aplican a todos los hosts que serán añadidos dinámicamente.

El role sdw añade la IP de la nueva virtual machine a este inventory, pero solo en memoria, no se modificará el fichero hosts en ningún momento.

Esto es importante para poder tener IP dinámicas.

Ejecutemos el comando:

ansible-playbook generate_machines.yml -i hosts -e ‘vmname=sdw do_token=TOKEN environment=dev do_keyname=“CLAVE”’

con “-i hosts” identificamos el nombre del inventory, la opción -e introduce parámetros extra: vmname=sdw  el nombre del droplet, do_token=TOKEN es el token para operar en DO, environment=dev es el entorno, dev o pro, y do_keyname=“CLAVE” es el nombre de la clave pública que permitirá acceso SSH al droplet.

El comando nos producirá un output muy largo que demuestra la ejecución de cada paso realizado. Si queremos más detalles, la opción -v nos los dará.

Si ejecutamos el comando una segunda vez, Ansible solo se limitará a controlar que la máquina esté creada, y verificará que todas las acciones siguen válidas. Por esta razón se dice que Ansible está usado de forma declarativa, y esta característica lo diferencia de scripts .sh que son típicamente usados en su forma imperativa.

En el próximo artículo entraremos en el detalle de cada script, comentando las líneas de código más importantes.