Tutorial configurando vagrant para una aplicación

De Wiki de EGC
Revisión del 20:27 12 nov 2024 de Brgutierrez (discusión | contribuciones) (Paso 3: Escribir el Playbook de Ansible)
Saltar a: navegación, buscar

Contenido

Despliegue de una Aplicación Python en Vagrant

Introducción

Este tutorial te guiará en el despliegue de una aplicación Flask utilizando Vagrant, Ansible y Shell scripts. La finalidad es entender cómo automatizar la configuración y despliegue de aplicaciones en entornos virtuales reproducibles, como los que proporciona Vagrant.

¿Por qué utilizamos Vagrant?

Vagrant es una herramienta de administración de entornos virtuales que permite definir en un archivo (llamado `Vagrantfile`) las especificaciones necesarias para crear, configurar y aprovisionar una máquina virtual de manera rápida y consistente. Vagrant es especialmente útil en desarrollo y pruebas, ya que nos permite configurar un entorno de desarrollo idéntico al entorno de producción en cuestión de minutos y garantiza que cada miembro del equipo de desarrollo trabaje en un entorno coherente.

Con Vagrant, evitamos el clásico problema de "en mi máquina funciona" al tener un entorno idéntico en cada ejecución, independientemente de la máquina en la que se esté corriendo el proyecto. En este tutorial, usaremos Vagrant para crear una máquina virtual de Linux donde desplegaremos nuestra aplicación de Flask.

¿Qué es el Aprovisionamiento?

Aprovisionar, en el contexto de máquinas virtuales y Vagrant, se refiere al proceso de instalar y configurar el software necesario en una máquina virtual para que pueda ejecutar una aplicación. Cuando aprovisionamos una máquina, estamos automatizando la instalación de dependencias, configuraciones de sistema, servicios necesarios (como bases de datos), y otras tareas de configuración que preparan el entorno para ejecutar la aplicación.

Métodos de Aprovisionamiento

En este tutorial veremos dos métodos de aprovisionamiento:

1. Mediante un script de shell: Usando un archivo `.sh` (como `provision.sh`), que contiene comandos de terminal para instalar y configurar los componentes necesarios.

2. Mediante Ansible: Usando un archivo de configuración llamado `playbook.yml`, que describe de manera declarativa lo que queremos instalar y configurar en la máquina virtual.

¿Por qué utilizar Ansible para el Aprovisionamiento?

Ansible es una herramienta de automatización de TI que permite gestionar la configuración, el despliegue y la orquestación de sistemas. Comparado con los scripts de shell, Ansible ofrece varias ventajas:

  • Declarativo y legible: Un playbook de Ansible describe el estado deseado del sistema (qué queremos conseguir), en lugar de los pasos para llegar a ese estado (cómo hacerlo), lo cual hace que sea más legible y fácil de mantener.
  • Idempotencia: Ansible ejecuta las tareas de manera idempotente, lo que significa que si ejecutamos el playbook varias veces, el sistema solo aplicará los cambios necesarios, evitando que se realicen operaciones innecesarias.
  • Escalabilidad y organización: Ansible permite crear playbooks para gestionar múltiples servidores de manera centralizada, lo cual es esencial en ambientes de producción donde hay decenas o cientos de servidores.
  • Ecosistema y comunidad: Ansible es una herramienta popular con una gran comunidad de usuarios y módulos preexistentes para una amplia variedad de tareas, lo que facilita extender la funcionalidad y resolver problemas comunes rápidamente.

¿Cuándo es mejor utilizar un script de shell y cuándo Ansible?

Para proyectos pequeños o configuraciones rápidas, un script de shell puede ser suficiente. Sin embargo, para entornos más complejos, donde la repetición, escalabilidad y legibilidad son factores importantes, Ansible es preferible debido a su idempotencia, claridad y modularidad.

En este tutorial, vamos a provisionar un entorno para nuestra aplicación Flask de ambas maneras, para que puedas comparar los métodos y comprender en qué casos uno puede ser más ventajoso que el otro.

Nuestra Aplicación: Gestión de Tareas

La aplicación que desplegaremos es una sencilla aplicación de gestión de tareas, creada previamente en otras prácticas. Esta aplicación está basada en Flask, un framework de desarrollo web en Python, y utiliza una base de datos MariaDB para almacenar información. El propósito de este tutorial es aprender a desplegar la aplicación en un entorno controlado y reproducible usando Vagrant y los métodos de aprovisionamiento mencionados anteriormente.

Estructura del Proyecto

Tras la última práctica, el proyecto debería presentar la siguiente estructura de archivos:

 flask_testing_project/
 ├── app.py                # Archivo principal de la aplicación Flask
 ├── templates/            # Directorio con la plantilla HTML
 │   └── tasks.html        # Plantilla para mostrar y agregar tareas
 ├── tests/
 │   ├── test_app.py       # Pruebas para la lógica de la aplicación
 │   └── test_interfaz.py   # Pruebas para la interfaz de usuario
 ├── locustfile.py         # Archivo para pruebas de carga con Locust
 ├── Dockerfile            # Archivo para construir la imagen Docker de la aplicación
 ├── requirements.txt      # Dependencias de Python necesarias para la aplicación
 └── docker-compose.yml    # Archivo de configuración de Docker Compose
 

Parte 1: Aprovisionamiento mediante un Script de Shell

En la primera parte del tutorial, aprovisionaremos el entorno de desarrollo con un script de shell (`provision.sh`).

Paso 1: Generar el archivo .env

Comienza creando un archivo .env en el directorio flask_testing_project. Este archivo contendrá las variables de entorno que tu aplicación Flask necesita para conectarse a la base de datos y otras configuraciones.

En el archivo .env, añade lo siguiente:

DATABASE_USER=flask_user
DATABASE_PASSWORD=flask_password
DATABASE_HOST=localhost
DATABASE_DB=tasks_db
 

Este archivo será leído por la aplicación Flask para cargar estas configuraciones automáticamente al inicio.

Paso 2: Modificar app.py para usar dotenv

Dotenv es una biblioteca que permite gestionar variables de entorno en una aplicación de manera sencilla y segura. Las variables de entorno son valores de configuración (como credenciales de bases de datos, claves API, configuraciones específicas del entorno, etc.) que suelen variar entre diferentes entornos (desarrollo, pruebas, producción) y que, por seguridad, no queremos incluir directamente en el código fuente. Dotenv permite almacenar estas variables de entorno en un archivo llamado .env, donde cada línea contiene un par clave=valor. Luego, al utilizar la biblioteca python-dotenv en nuestro código, podemos cargar automáticamente estas variables al entorno de la aplicación al inicio. Esto nos permite acceder a ellas a través de os.environ, como si estuvieran configuradas en el sistema, pero sin exponerlas en el código.

¿Sabrías enumerar al menos tres motivos por lo que el uso de dotenv es una buena práctica?

Para que la aplicación Flask cargue automáticamente las variables desde el archivo `.env`, edita `app.py` para que incluya dotenv:

  1. Abre `app.py` y añade al principio las siguientes líneas:
 from dotenv import load_dotenv
 import os

 # Cargar variables de entorno desde el archivo .env
 load_dotenv()

Paso 3: Añadir python-dotenv a requirements.txt

Abre el archivo `requirements.txt` y añade la biblioteca `python-dotenv` al final:

attrs==24.2.0
blinker==1.8.2
Brotli==1.1.0
.....
wsproto==1.2.0
zope.event==5.0
zope.interface==7.1.1
python-dotenv #Añadir esta línea al requirements.txt que ya teníamos
 

Paso 4: Crear el Vagrantfile

Recuerda que el Vagrantfile es el archivo de configuración que Vagrant utiliza para definir y gestionar una máquina virtual. Este archivo es el núcleo de cualquier proyecto que utilice Vagrant, ya que contiene instrucciones específicas para la configuración de la máquina virtual, incluyendo el sistema operativo, red, sincronización de carpetas, y scripts de aprovisionamiento.

Al colocar el Vagrantfile en el directorio raíz del proyecto (flask_testing_project/), mantenemos todos los recursos y configuraciones necesarias para el despliegue de la aplicación en un solo lugar. Esto hace que el proyecto sea fácil de portar y reproducir, ya que cualquier persona con este directorio y Vagrant puede ejecutar el proyecto en una máquina virtual idéntica.

A continuación, crea un archivo llamado Vagrantfile en el directorio flask_testing_project/ y añade el siguiente contenido:

 Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/jammy64"
    
    config.vm.network "forwarded_port", guest: 5000, host: 8080
    
    # Sincronización de la carpeta de tu proyecto para que sea accesible desde la VM
    config.vm.synced_folder ".", "/home/vagrant/app", type: "virtualbox"
    
    # Copiar el archivo .env desde el directorio del proyecto en tu máquina local
    config.vm.provision "file", source: ".env", destination: "/home/vagrant/app/.env"
    
    config.vm.provision "shell", path: "provision.sh"
end

Explicación línea a línea del Vagrantfile

Vagrant.configure("2") do |config|

Este bloque define la configuración para la máquina virtual. El "2" especifica la versión de la sintaxis de configuración de Vagrant, y config es el objeto que contiene todas las configuraciones de esta máquina virtual.

config.vm.box = "ubuntu/jammy64"

Esta línea indica a Vagrant qué sistema operativo (o "box") debe utilizar. Aquí, especificamos "ubuntu/jammy64", una versión de Ubuntu 22.04 (Jammy Jellyfish). Vagrant descargará esta box la primera vez que se ejecute, creando una base común para cualquier máquina que utilice este archivo de configuración.

config.vm.network "forwarded_port", guest: 5000, host: 8080

Esta línea configura el reenvío de puertos para que puedas acceder a la aplicación Flask que se ejecuta en el puerto 5000 de la VM desde el puerto 8080 de tu máquina local. Esto significa que, aunque Flask escuche en localhost:5000 dentro de la VM, podrás acceder a la aplicación en http://localhost:8080 en tu navegador fuera de la VM.

config.vm.synced_folder ".", "/home/vagrant/app", type: "virtualbox"

Aquí estamos sincronizando la carpeta raíz del proyecto (representada por ".") con el directorio /home/vagrant/app dentro de la máquina virtual. Esto permite que los archivos en tu máquina local se mantengan sincronizados con los de la VM, de modo que cualquier cambio que hagas en tu proyecto se refleje inmediatamente dentro de la máquina virtual. La opción type: "virtualbox" es una opción específica de VirtualBox que puede optimizar el rendimiento de esta carpeta sincronizada.

config.vm.provision "file", source: ".env", destination: "/home/vagrant/app/.env"

Este paso copia el archivo .env de configuración desde tu máquina local al directorio correspondiente en la máquina virtual (/home/vagrant/app/.env). Este archivo .env contiene variables de entorno necesarias para que la aplicación Flask funcione correctamente. Asegurar que se copie garantiza que la aplicación tenga las configuraciones necesarias para ejecutarse.

config.vm.provision "shell", path: "provision.sh"

Esta línea define un paso de aprovisionamiento que ejecutará un script de shell llamado provision.sh en la máquina virtual. Este script contendrá instrucciones para instalar dependencias, configurar la aplicación y preparar la máquina virtual para ejecutar el proyecto Flask. Añadir este paso en el Vagrantfile asegura que todo el entorno de la aplicación se instale automáticamente al iniciar la VM con vagrant up.

Paso 5: Escribir el Script de aprovisionamiento provision.sh

¿Qué es un archivo con extensión .sh?

Un archivo con extensión .sh es un script de shell, un archivo de texto que contiene una serie de comandos escritos en un lenguaje de scripting específico para el shell, el intérprete de comandos que interactúa con el sistema operativo. Los scripts .sh se usan ampliamente en sistemas Unix y Linux, aunque también funcionan en otros entornos compatibles con shells como bash, sh o zsh.

¿Para qué sirven los archivos .sh?

Los archivos .sh permiten automatizar tareas, ya que el shell ejecuta cada comando en el archivo de forma secuencial, sin necesidad de intervención manual. Esto es especialmente útil para configurar sistemas, instalar paquetes, mover o copiar archivos, configurar redes, iniciar aplicaciones y realizar prácticamente cualquier acción que se pueda ejecutar desde la línea de comandos.

Por ejemplo, en el contexto de nuestro proyecto con Vagrant y Flask, usaremos un archivo .sh llamado provision.sh para:

Instalar dependencias como Python y Flask. Configurar la base de datos. Preparar el entorno de la aplicación. Esto permite que, al ejecutar vagrant up, el script se ejecute automáticamente, configurando la máquina virtual sin necesidad de realizar los pasos de configuración uno a uno.

Cómo se ejecuta un archivo .sh

Para ejecutar un archivo .sh en un sistema Linux o Mac, simplemente hay que abrir una terminal y escribir:
 sh nombre_del_archivo.sh
O bien, puedes ejecutar el script con permisos adicionales o en otro shell específico, como bash:
 bash nombre_del_archivo.sh

Cuando Vagrant encuentra un archivo .sh especificado en el Vagrantfile (como en config.vm.provision "shell", path: "provision.sh"), ejecuta ese archivo automáticamente en la máquina virtual como parte del proceso de aprovisionamiento. Esto garantiza que la máquina esté configurada correctamente cada vez que se inicia.

En resumen, los archivos .sh son esenciales para automatizar y simplificar la configuración de entornos en sistemas Linux o Unix, ayudándonos a ahorrar tiempo y evitar errores de configuración manual.

Creando nuestro script de aprovisionamiento

Dentro del directorio raíz, flask_testing_project,, crea un archivo llamado provision.sh que contendrá los comandos necesarios para instalar Python, MariaDB, y todas las dependencias de la aplicación.

El contenido de nuestro provision.sh debe ser el siguiente:

 #!/bin/bash

# Establecer el modo no interactivo para evitar prompts durante las instalaciones
export DEBIAN_FRONTEND=noninteractive

# Actualizar el sistema
sudo apt-get update -y
sudo apt-get upgrade -y

# Instalar dependencias necesarias: Python, MariaDB y herramientas adicionales
sudo apt-get install -y python3-pip python3-dev mariadb-server libmariadb-dev screen

# Iniciar y asegurar la instalación de MariaDB
sudo systemctl start mariadb
sudo systemctl enable mariadb > /dev/null 2>&1
sudo systemctl restart mariadb > /dev/null 2>&1


# Cargar las variables del archivo .env
set -a
source /home/vagrant/app/.env
set +a

# Crear base de datos y usuario solo si no existen
sudo mysql -e "CREATE DATABASE IF NOT EXISTS $DATABASE_DB;"
sudo mysql -e "CREATE USER IF NOT EXISTS '$DATABASE_USER'@'localhost' IDENTIFIED BY '$DATABASE_PASSWORD';"
sudo mysql -e "GRANT ALL PRIVILEGES ON $DATABASE_DB.* TO '$DATABASE_USER'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"

# Cambiar al directorio de la aplicación
cd /home/vagrant/app

# Instalar las dependencias de la aplicación
pip3 install --upgrade pip --root-user-action=ignore
pip3 install --ignore-installed -r requirements.txt --root-user-action=ignore

# Configurar permisos para que el usuario vagrant tenga acceso completo
sudo chown -R vagrant:vagrant /home/vagrant/app

# Lanzar la aplicación Flask en segundo plano con screen
screen -m -d -S flask_app bash -c "source /home/vagrant/app/.env && python3 /home/vagrant/app/app.py"

# Mensaje de finalización
echo "¡Aprovisionamiento completado y aplicación lanzada en http://localhost:8080!"

Explicación línea a línea del script provision.sh

El script `provision.sh` configura el entorno en una máquina virtual Vagrant para ejecutar una aplicación Flask. Este archivo asegura que todas las dependencias y configuraciones necesarias se instalen automáticamente. A continuación, explicaremos cada línea:

#!/bin/bash
  • #!/bin/bash: Especifica el intérprete que debe usarse para ejecutar el script, en este caso, `bash`, que es un intérprete de comandos y shell utilizado comúnmente en sistemas Unix y Linux. `bash` permite ejecutar comandos y secuencias de comandos de forma interactiva o automatizada. Al especificar `#!/bin/bash`, el sistema sabe que debe usar el programa `bash` para ejecutar las instrucciones del script, asegurando que se interpreten correctamente en un entorno compatible con esta shell.
# Establecer el modo no interactivo para evitar prompts durante las instalaciones
export DEBIAN_FRONTEND=noninteractive
  • export DEBIAN_FRONTEND=noninteractive: Configura el entorno en modo no interactivo, para que no aparezcan preguntas durante la instalación de paquetes.
# Actualizar el sistema
sudo apt-get update -y
sudo apt-get upgrade -y
  • sudo apt-get update -y: Actualiza la lista de paquetes disponibles.
  • sudo apt-get upgrade -y: Actualiza todos los paquetes instalados a sus últimas versiones disponibles.
# Instalar dependencias necesarias: Python, MariaDB y herramientas adicionales
sudo apt-get install -y python3-pip python3-dev mariadb-server libmariadb-dev screen
  • sudo apt-get install -y ...: Instala Python, MariaDB y otras herramientas esenciales (`python3-pip`, `python3-dev`, `libmariadb-dev` y `screen`) para ejecutar la aplicación Flask.
    • python3-pip: Es el administrador de paquetes para Python 3. Pip permite instalar y gestionar bibliotecas y dependencias de Python de manera sencilla, lo que es fundamental para el desarrollo y despliegue de aplicaciones Python. En este caso, se utiliza para instalar las dependencias listadas en el archivo `requirements.txt`.
    • python3-dev: Es un paquete que contiene archivos de cabecera y bibliotecas necesarias para compilar módulos de Python. Este paquete es esencial cuando se requieren módulos de Python que necesitan ser compilados desde el código fuente, especialmente aquellos que interactúan con librerías externas o requieren acceso a funcionalidades del sistema operativo.
    • libmariadb-dev: Este paquete incluye las bibliotecas de desarrollo necesarias para interactuar con MariaDB (una base de datos compatible con MySQL). Contiene los archivos de cabecera necesarios para compilar aplicaciones que usan la API de MariaDB, lo cual es esencial para que la aplicación Flask pueda interactuar con la base de datos MariaDB.
    • screen: Es una herramienta que permite gestionar sesiones de terminal en segundo plano. Con `screen`, puedes iniciar una sesión de terminal en segundo plano y luego desconectarte, sin que los procesos sigan siendo interrumpidos. Es útil para ejecutar aplicaciones como un servidor Flask de manera persistente, incluso si se cierra la terminal o se d*esconecta la sesión SSH.


# Iniciar y asegurar la instalación de MariaDB
sudo systemctl start mariadb
sudo systemctl enable mariadb > /dev/null 2>&1
sudo systemctl restart mariadb > /dev/null 2>&1
  • sudo systemctl start mariadb: Inicia el servicio MariaDB para manejar bases de datos.
  • sudo systemctl enable mariadb: Asegura que MariaDB se inicie automáticamente al iniciar el sistema.
  • sudo systemctl restart mariadb: Reinicia MariaDB para aplicar los cambios de configuración.
# Cargar las variables del archivo .env
set -a
source /home/vagrant/app/.env
set +a
  • set -a / set +a: Activa y desactiva la exportación automática de variables de entorno. Esto garantiza que las variables definidas en el archivo `.env` sean accesibles durante el script.
# Crear base de datos y usuario solo si no existen
sudo mysql -e "CREATE DATABASE IF NOT EXISTS $DATABASE_DB;"
sudo mysql -e "CREATE USER IF NOT EXISTS '$DATABASE_USER'@'localhost' IDENTIFIED BY '$DATABASE_PASSWORD';"
sudo mysql -e "GRANT ALL PRIVILEGES ON $DATABASE_DB.* TO '$DATABASE_USER'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
  • sudo mysql -e ...: Ejecuta comandos SQL para crear la base de datos y el usuario de MariaDB si no existen, y les asigna los privilegios necesarios para acceder a la base de datos.
# Cambiar al directorio de la aplicación
cd /home/vagrant/app
  • cd /home/vagrant/app: Cambia el directorio de trabajo a la ubicación de la aplicación Flask.
# Instalar las dependencias de la aplicación
pip3 install --upgrade pip --root-user-action=ignore
pip3 install --ignore-installed -r requirements.txt --root-user-action=ignore
  • pip3 install --upgrade pip --root-user-action=ignore: Actualiza `pip`, el gestor de paquetes de Python.
  • pip3 install --ignore-installed -r requirements.txt --root-user-action=ignore: Instala las dependencias de la aplicación Flask especificadas en el archivo `requirements.txt`.
    • --ignore-installed: Este flag le indica a `pip` que ignore cualquier versión previamente instalada de los paquetes listados en el archivo `requirements.txt`. En otras palabras, si ya tienes una versión de algún paquete instalada en el sistema, `pip` la ignorará y volverá a instalar la versión especificada en el archivo `requirements.txt`. Esto es útil para asegurarse de que las versiones de las dependencias sean las correctas, independientemente de las versiones que ya existan en el sistema.
    • -r requirements.txt: El flag `-r` le dice a `pip` que lea las dependencias de un archivo de texto (en este caso, `requirements.txt`). El archivo debe contener una lista de los paquetes necesarios para el proyecto Flask. `pip` instalará todas las dependencias que se encuentren en este archivo.
    • --root-user-action=ignore: Este flag se usa para evitar que `pip` muestre advertencias o solicite confirmaciones de permisos cuando se está ejecutando como usuario root. Es útil en entornos automatizados, como cuando ejecutamos scripts de aprovisionamiento, ya que evita que el proceso se detenga por preguntas interactivas.
# Configurar permisos para que el usuario vagrant tenga acceso completo
sudo chown -R vagrant:vagrant /home/vagrant/app
  • sudo chown -R vagrant:vagrant /home/vagrant/app: Cambia la propiedad del directorio de la aplicación para que el usuario `vagrant` tenga acceso completo.
# Lanzar la aplicación Flask en segundo plano con screen
screen -m -d -S flask_app bash -c "source /home/vagrant/app/.env && python3 /home/vagrant/app/app.py"
  • screen -m -d -S flask_app bash -c "...": Utiliza `screen` para ejecutar la aplicación Flask en segundo plano, de manera que pueda seguir funcionando incluso después de cerrar la terminal.
    • -m: Este flag le dice a `screen` que inicie una nueva sesión de pantalla. Si no se incluye este flag, `screen` no abriría una nueva sesión y no funcionaría como esperamos. En este caso, es necesario para crear una nueva instancia de `screen`.
    • -d: Este flag le indica a `screen` que se desconecte de la sesión de manera inmediata. Es decir, inicia la sesión en segundo plano, sin necesidad de interactuar con ella directamente. El proceso sigue ejecutándose, pero el usuario no verá la salida directamente en la terminal.
    • -S flask_app: Este flag establece un nombre para la sesión de `screen`. En este caso, la sesión se llamará "flask_app". El nombre de la sesión es útil para poder identificarla más tarde, si se desea reconectarse o cerrar la sesión de `screen`.
    • bash -c "...": Esta parte del comando le dice a `screen` que ejecute un comando específico dentro de la sesión de `screen`. En este caso, el comando es `bash -c "..."`, que ejecuta la aplicación Flask (`python3 /home/vagrant/app/app.py`). El `bash -c` permite ejecutar un comando en un shell de bash dentro de la sesión de `screen`.
# Mensaje de finalización
echo "¡Aprovisionamiento completado y aplicación lanzada en http://localhost:8080!"
  • echo "¡Aprovisionamiento completado y aplicación lanzada en http://localhost:8080!": Muestra un mensaje indicando que el aprovisionamiento ha finalizado y que la aplicación Flask está corriendo en el puerto 8080.

Paso 6: Iniciar el Entorno Virtual con Vagrant

Desde la terminal, navega al directorio `flask_testing_project/` y ejecuta el siguiente comando:

 vagrant up

Cuando el proceso finalice, podrás acceder a la aplicación en `http://localhost:8080`.

¿Has podido ver qué ocurría en cada paso? ¡Prueba a entrar en la máquina virtual y ver cómo está funcionando todo por dentro!

Parte 2: Aprovisionamiento mediante Ansible

El aprovisionamiento con Ansible ofrece varias ventajas frente al uso de scripts de shell. Mientras que los scripts de shell son imperativos, es decir, detallan los pasos exactos para lograr un objetivo, Ansible adopta un enfoque declarativo, donde especificamos el estado deseado del sistema y Ansible se encarga de llevarlo a cabo. Esto permite una mayor idempotencia, es decir, que la configuración se pueda aplicar varias veces sin causar efectos no deseados. Además, Ansible facilita la gestión de infraestructuras grandes de forma escalable y reproducible, lo que hace que sea más fácil mantener entornos consistentes y automatizados en comparación con los scripts tradicionales. Un aspecto clave de Ansible es que permite aprovisionar diferentes máquinas sin importar el sistema operativo o la infraestructura subyacente, ya que simplemente le indicamos qué necesitamos y Ansible se encarga de implementar la configuración adecuada, independientemente de cómo o dónde se ejecute.

Paso 1: Actualizar el archivo .env

Asegúrate de que el archivo `.env` en `flask_testing_project/` contiene las variables necesarias para la base de datos y la aplicación:

DATABASE_USER=flask_user
DATABASE_PASSWORD=flask_password
DATABASE_HOST=localhost
DATABASE_DB=tasks_db
MYSQL_ROOT_PASSWORD=your_root_password
 

La diferencia en este segundo archivo `.env` respecto al primero radica en la inclusión de la variable `MYSQL_ROOT_PASSWORD`. Esta variable es necesaria porque, al utilizar Ansible en el segundo paso, se crea un usuario de base de datos específico (en este caso, `flask_user`) y se le otorgan privilegios. Para hacer esto, es necesario acceder como el usuario `root` en MariaDB, y `MYSQL_ROOT_PASSWORD` define la contraseña de ese superusuario.

La razón para agregar esta variable es garantizar que el playbook de Ansible pueda interactuar con MariaDB utilizando las credenciales de `root`, lo cual es esencial para realizar tareas administrativas como la creación de bases de datos y la asignación de permisos a usuarios específicos.

En resumen, el cambio es necesario para asegurar que Ansible pueda ejecutar correctamente las tareas que requieren privilegios de administrador en MariaDB (como la creación de bases de datos y usuarios), algo que no se necesitaba en el aprovisionamiento anterior con shell, donde se asume que ya tienes acceso a MariaDB sin pasar por un playbook de automatización.

Paso 2: Modificar el Vagrantfile para usar Ansible

Modifica el `Vagrantfile` para ejecutar un playbook de Ansible en lugar del script `provision.sh`:

Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/jammy64"
    
    config.vm.network "forwarded_port", guest: 5000, host: 8080
    
    # Sincronización de la carpeta de tu proyecto para que sea accesible desde la VM
    config.vm.synced_folder ".", "/home/vagrant/app", type: "virtualbox"
    
    # Aprovisionamiento con Ansible
    config.vm.provision "ansible" do |ansible|
    ansible.playbook = "playbook.yml"  # Nombre del archivo playbook de Ansible
    ansible.extra_vars = {
      ansible_python_interpreter: "/usr/bin/python3"
    }
  end
end

Explicación línea a línea del Vagrantfile

Vagrant.configure("2") do |config|

Esta línea indica la versión de la configuración de Vagrant que se está utilizando. En este caso, la versión "2" es la más común y reciente. El bloque de código que sigue contiene las configuraciones de la máquina virtual.

config.vm.box = "ubuntu/jammy64"

Esta línea especifica la "box" de Vagrant que se utilizará como base para la máquina virtual. En este caso, se está utilizando una imagen de Ubuntu 22.04 LTS (Jammy) para x86_64 (64 bits). Es una imagen de sistema operativo que se descargará y utilizará para crear la máquina virtual.

config.vm.network "forwarded_port", guest: 5000, host: 8080

Aquí se está configurando el reenvío de puertos entre la máquina virtual y el host. El puerto 5000 en la máquina virtual (donde probablemente se ejecuta la aplicación Flask) se asignará al puerto 8080 en el host. Esto permite acceder a la aplicación Flask a través del puerto 8080 en el navegador del host.

config.vm.synced_folder ".", "/home/vagrant/app", type: "virtualbox"

Esta línea establece la sincronización de carpetas entre el host y la máquina virtual. El directorio actual del host (el que contiene el `Vagrantfile`) se sincroniza con `/home/vagrant/app` en la máquina virtual. Esto significa que cualquier cambio realizado en el directorio del proyecto en el host se reflejará automáticamente en la máquina virtual y viceversa. Esto es útil para desarrollar y probar código en la máquina virtual sin tener que mover manualmente archivos.

config.vm.provision "ansible" do |ansible| Aquí se está configurando el aprovisionamiento de la máquina virtual usando Ansible. Vagrant proporciona soporte para varias herramientas de aprovisionamiento, y en este caso se está eligiendo Ansible para automatizar la configuración de la máquina virtual.

ansible.playbook = "playbook.yml" Esta línea indica el nombre del archivo de playbook de Ansible que se utilizará para el aprovisionamiento. En este caso, el archivo se llama `playbook.yml` y debe estar en el mismo directorio que el `Vagrantfile`. El playbook de Ansible contiene las tareas que se realizarán para configurar la máquina virtual.

ansible.extra_vars = { 'ansible_python_interpreter: "/usr/bin/python3" }

En esta sección se está configurando una variable extra para Ansible. En este caso, se está especificando que el intérprete de Python que se debe utilizar es `/usr/bin/python3`. Esto es útil porque algunas veces Ansible puede intentar usar Python 2 por defecto, y al especificar esta variable, nos aseguramos de que Ansible utilice la versión correcta de Python.

Paso 3: Escribir el Playbook de Ansible

Un playbook de Ansible es un archivo que contiene una serie de instrucciones o "plays" que definen la configuración de una máquina o un conjunto de máquinas. Estas instrucciones están escritas en formato YAML,, un formato legible para los humanos que se utiliza comúnmente en la automatización de tareas.

El objetivo de un playbook es describir el estado deseado de un sistema o una serie de sistemas y luego ejecutar las acciones necesarias para llegar a ese estado. A diferencia de los scripts de shell, que son imperativos (especifican los pasos exactos a seguir), los playbooks de Ansible son declarativos: tú defines lo que necesitas, y Ansible se encarga de hacerlo realidad. Esto implica que Ansible no se preocupa por el orden exacto de ejecución ni por la necesidad de "limpiar" el estado después de ejecutar el playbook; Ansible gestiona estos detalles por ti.

Un playbook está compuesto por varias secciones o bloques, cada uno de los cuales puede definir tareas a ejecutar en uno o más nodos. Los bloques se componen de:

  • Hosts: Indica qué máquinas o grupos de máquinas deben ser gestionados.
  • Tasks (Tareas): Son las acciones que se deben ejecutar sobre las máquinas especificadas.
  • Variables: Definen los valores dinámicos que puedes utilizar a lo largo del playbook.
  • Handlers: Tareas especiales que solo se ejecutan si otras tareas notifican un cambio.

Ventajas de los Playbooks de Ansible:

  • Idempotencia: Puedes ejecutar un playbook varias veces sin efectos secundarios, garantizando que el sistema quede siempre en el mismo estado deseado.
  • Reusabilidad: Los playbooks son fáciles de reutilizar en diferentes entornos y con diferentes configuraciones.
  • Escalabilidad: Puedes gestionar miles de máquinas de manera sencilla sin tener que escribir un script diferente para cada máquina.
  • Simplicidad: Los playbooks de Ansible son fáciles de leer y escribir, incluso para aquellos que no están familiarizados con la programación.

En la raíz de flask_testing_project, crea un archivo llamado playbook.yml con el siguiente contenido:

---
- name: Aprovisionamiento de aplicación Flask con Ansible
  hosts: all
  become: true
  vars:
    app_directory: "/home/vagrant/app"
    env_file_path: "/home/vagrant/app/.env"

  tasks:
    ###########################################################################################
    # Sección 1: Lectura de variables de entorno desde el archivo .env
    ###########################################################################################
    - name: Leer archivo .env y establecer variables
      set_fact:
        DATABASE_USER: "{{ lookup('env', 'DATABASE_USER') }}"
        DATABASE_PASSWORD: "{{ lookup('env', 'DATABASE_PASSWORD') }}"
        DATABASE_HOST: "{{ lookup('env', 'DATABASE_HOST') }}"
        DATABASE_DB: "{{ lookup('env', 'DATABASE_DB') }}"
        MYSQL_ROOT_PASSWORD: "{{ lookup('env', 'MYSQL_ROOT_PASSWORD') }}"
      become: false  # No necesitamos permisos elevados para leer el archivo .env

    ###########################################################################################
    # Sección 2: Configuración del sistema operativo (repositorios, paquetes)
    ###########################################################################################
    - name: Habilitar repositorio "universe" en Ubuntu (si es necesario)
      ansible.builtin.apt_repository:
        repo: "ppa:deadsnakes/ppa"
        state: present
        update_cache: yes
      when: ansible_distribution == "Ubuntu"

    - name: Actualizar la caché de paquetes
      ansible.builtin.apt:
        update_cache: yes

    - name: Instalar dependencias de sistema necesarias
      package:
        name:
          - python3
          - python3-venv
          - python3-pip
          - mariadb-server
          - python3-pymysql
          - libmariadb-dev
          - screen
        state: present

    ###########################################################################################
    # Sección 3: Instalación de herramientas y servicios necesarios (MariaDB, Python)
    ###########################################################################################
    - name: Instalar pip y setuptools para Python si no están instalados
      shell: |
        wget https://bootstrap.pypa.io/get-pip.py
        python3 get-pip.py
      args:
        executable: /bin/bash
      when: ansible_facts.packages['python3-pip'] is not defined

    - name: Iniciar y habilitar el servicio de MariaDB
      service:
        name: mariadb
        state: started
        enabled: yes

    ###########################################################################################
    # Sección 4: Configuración de la base de datos y usuarios en MariaDB
    ###########################################################################################
    - name: Cambiar el plugin de autenticación de root a mysql_native_password
      mysql_user:
        login_unix_socket: /var/run/mysqld/mysqld.sock
        login_user: 'root'
        login_password: ''
        name: 'root'
        password: "{{ MYSQL_ROOT_PASSWORD }}"
        state: present
        plugin: mysql_native_password

    - name: Crear base de datos y usuario de MariaDB con privilegios adecuados
      mysql_db:
        name: "{{ DATABASE_DB }}"
        state: present
        login_user: root
        login_password: "{{ MYSQL_ROOT_PASSWORD }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock
        collation: utf8_general_ci

    - name: Crear el usuario flask_user y otorgar privilegios
      mysql_user:
        name: "{{ DATABASE_USER }}"
        password: "{{ DATABASE_PASSWORD }}"
        priv: "{{ DATABASE_DB }}.*:ALL"
        host: "localhost"
        login_user: root
        login_password: "{{ MYSQL_ROOT_PASSWORD }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock
        state: present
        plugin: mysql_native_password

    - name: Crear archivo SQL para base de datos y usuario
      copy:
        content: |
          CREATE DATABASE IF NOT EXISTS {{ DATABASE_DB }};
          CREATE USER IF NOT EXISTS '{{ DATABASE_USER }}'@'localhost' IDENTIFIED BY '{{ DATABASE_PASSWORD }}';
          GRANT ALL PRIVILEGES ON {{ DATABASE_DB }}.* TO '{{ DATABASE_USER }}'@'localhost';
          FLUSH PRIVILEGES;
        dest: /tmp/setup.sql
        owner: root
        group: root
        mode: '0600'

    - name: Ejecutar el script SQL en MariaDB utilizando mysql_db
      mysql_db:
        name: "{{ DATABASE_DB }}"
        state: present
        login_user: root
        login_password: "{{ MYSQL_ROOT_PASSWORD }}"
        login_unix_socket: /var/run/mysqld/mysqld.sock
        target: /tmp/setup.sql
      become: yes

    - name: Eliminar el archivo temporal del script SQL
      file:
        path: /tmp/setup.sql
        state: absent

    ###########################################################################################
    # Sección 5: Configuración del entorno virtual y dependencias de Python
    ###########################################################################################
    - name: Eliminar entorno virtual existente si existe
      file:
        path: "{{ app_directory }}/venv"
        state: absent
        force: yes

    - name: Crear el entorno virtual para la aplicación Flask
      command: python3 -m venv {{ app_directory }}/venv
      args:
        creates: "{{ app_directory }}/venv/bin/activate"

    - name: Activar el entorno virtual y actualizar pip
      shell: |
        source {{ app_directory }}/venv/bin/activate
        pip install --upgrade pip
      args:
        executable: /bin/bash

    - name: Instalar dependencias de Python desde requirements.txt
      pip:
        requirements: "{{ app_directory }}/requirements.txt"
        virtualenv: "{{ app_directory }}/venv"
        virtualenv_python: python3

    ###########################################################################################
    # Sección 6: Configuración final de la aplicación Flask
    ###########################################################################################
    - name: Copiar archivo .env al directorio de la aplicación
      copy:
        src: "{{ playbook_dir }}/.env"  # Ruta local del archivo .env
        dest: "{{ app_directory }}/.env"
        owner: vagrant
        group: vagrant
        mode: '0640'

    - name: Cambiar permisos del directorio de la aplicación
      file:
        path: "{{ app_directory }}"
        owner: vagrant
        group: vagrant
        recurse: yes

    - name: Lanzar aplicación Flask en segundo plano con screen
      shell: |
        screen -m -d -S flask_app bash -c "source {{ app_directory }}/venv/bin/activate && {{ app_directory }}/venv/bin/python3 {{ app_directory }}/app.py"
      args:
        executable: /bin/bash

    ###########################################################################################
    # Sección 7: Mensaje final
    ###########################################################################################
    - name: Mensaje final
      debug:
        msg: "¡Aprovisionamiento completado y aplicación lanzada en http://localhost:8080!"

Explicación por bloques del playbook

Bloque inicial

Este bloque establece el nombre del playbook, define los hosts donde se ejecutará, y configura permisos de superusuario. También declara algunas variables generales que se usarán en el playbook.

  • name: Define el nombre del playbook como "Aprovisionamiento de aplicación Flask con Ansible".
  • hosts: Indica que el playbook se ejecutará en todos los hosts especificados en la configuración de Vagrant.
  • become: Activa los permisos de superusuario para la mayoría de las tareas.
  • vars: Declara variables:
    • app_directory: Define la ruta al directorio donde se desplegará la aplicación Flask.
    • env_file_path: Especifica la ruta al archivo `.env`, que contiene las variables de entorno necesarias.

Sección 1: Lectura de variables de entorno desde el archivo .env

Este bloque lee las variables de entorno desde el archivo `.env` y las almacena para su uso posterior.

  • set_fact: Usa `set_fact` para establecer valores de variables en tiempo de ejecución. Cada variable se obtiene usando `lookup`, que recupera los valores de `DATABASE_USER`, `DATABASE_PASSWORD`, `DATABASE_HOST`, `DATABASE_DB` y `MYSQL_ROOT_PASSWORD`.
  • become: Desactivado aquí, ya que leer el archivo `.env` no requiere permisos elevados.

Sección 2: Configuración del sistema operativo (repositorios, paquetes)

Esta sección instala los repositorios y paquetes necesarios para la aplicación y su entorno.

  • Habilitar repositorio "universe" en Ubuntu (si es necesario): Agrega el repositorio "deadsnakes" en Ubuntu para tener acceso a versiones adicionales de Python.
  • Actualizar la caché de paquetes: Refresca la caché del sistema para que los paquetes se encuentren en sus versiones más recientes.
  • Instalar dependencias de sistema necesarias: Instala los paquetes esenciales (`python3`, `python3-venv`, `python3-pip`, `mariadb-server`, `python3-pymysql`, `libmariadb-dev`, `screen`) requeridos para el entorno de Flask y MariaDB.

Sección 3: Instalación de herramientas y servicios necesarios (MariaDB, Python)

Esta sección configura e inicia los servicios y herramientas clave como MariaDB y Python.

  • Instalar pip y setuptools para Python si no están instalados: Descarga e instala `pip` y `setuptools`, necesarios para administrar paquetes de Python, si `python3-pip` no está instalado.
  • Iniciar y habilitar el servicio de MariaDB: Activa MariaDB para que inicie automáticamente con la máquina.

Sección 4: Configuración de la base de datos y usuarios en MariaDB

Aquí se crea la base de datos, el usuario, y se establecen permisos en MariaDB.

  • Cambiar el plugin de autenticación de root a mysql_native_password: Cambia la configuración de autenticación para permitir el uso de `mysql_native_password`, asegurando compatibilidad con el playbook.
  • Crear base de datos y usuario de MariaDB con privilegios adecuados: Crea la base de datos definida en `DATABASE_DB`.
  • Crear el usuario flask_user y otorgar privilegios: Define el usuario `flask_user`, con permisos específicos en la base de datos de la aplicación.
  • Crear archivo SQL para base de datos y usuario: Genera un archivo SQL temporal para ejecutar las configuraciones de base de datos y usuario.
  • Ejecutar el script SQL en MariaDB utilizando mysql_db: Aplica el archivo SQL para confirmar la creación de la base de datos y el usuario.
  • Eliminar el archivo temporal del script SQL: Elimina el archivo temporal para mayor seguridad.

Sección 5: Configuración del entorno virtual y dependencias de Python

Crea y configura un entorno virtual para la aplicación, y luego instala sus dependencias.

  • Eliminar entorno virtual existente si existe: Elimina el entorno virtual si está presente para evitar conflictos de versiones.
  • Crear el entorno virtual para la aplicación Flask: Crea un entorno virtual específico para la aplicación.
  • Activar el entorno virtual y actualizar pip: Activa el entorno y actualiza `pip` a la última versión.
  • Instalar dependencias de Python desde requirements.txt: Instala las librerías requeridas por la aplicación desde el archivo `requirements.txt`.

Sección 6: Configuración final de la aplicación Flask

Configura el archivo `.env`, ajusta permisos, y lanza la aplicación en segundo plano.

  • Copiar archivo .env al directorio de la aplicación: Copia el archivo `.env` al directorio de la aplicación para que Flask pueda leer las variables de entorno.
  • Cambiar permisos del directorio de la aplicación: Cambia permisos de directorio para que el usuario `vagrant` tenga acceso completo.
  • Lanzar aplicación Flask en segundo plano con screen: Inicia la aplicación Flask usando `screen`, permitiendo que se ejecute en segundo plano.

Sección 7: Mensaje final

Este bloque muestra un mensaje de éxito.

  • Mensaje final: Muestra una notificación indicando que el aprovisionamiento ha finalizado y que la aplicación está disponible en la dirección especificada.

Paso 4: Iniciar el Entorno Virtual con Vagrant y Ansible

Para iniciar el entorno, ejecuta nuevamente:

 vagrant up

La aplicación Flask estará disponible en `http://localhost:8080` cuando finalice el aprovisionamiento.

Conclusión

Este tutorial te ha guiado a través de dos métodos de aprovisionamiento de una aplicación Flask en Vagrant, utilizando un script de shell y un playbook de Ansible. ¡Ahora deberías tener una mejor comprensión del aprovisionamiento automático y de cómo desplegar aplicaciones en entornos virtuales reproducibles!