Tutorial configurando vagrant para una aplicación
Contenido
- 1 Despliegue de una Aplicación Python en Vagrant
- 2 Parte 1: Aprovisionamiento mediante un Script de Shell
- 3 Parte 2: Aprovisionamiento mediante Ansible
- 4 Conclusión
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:
- 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 por 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
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`.
# 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.
# 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`.
# 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.
# 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`.
Parte 2: Aprovisionamiento mediante Ansible
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=flask_db MYSQL_ROOT_PASSWORD=root_password
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/bionic64"
config.vm.network "forwarded_port", guest: 5000, host: 8080
config.vm.provision "ansible" do |ansible|
ansible.playbook = "playbook.yml"
ansible.extra_vars = { ansible_python_interpreter: "/usr/bin/python3" }
end
config.vm.synced_folder ".", "/home/vagrant/app"
end
Paso 3: Escribir el Playbook de Ansible
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:
# Configurar el repositorio y actualizar paquetes
- name: Habilitar repositorio "universe" y actualizar paquetes
ansible.builtin.apt_repository:
repo: "ppa:deadsnakes/ppa"
state: present
update_cache: yes
when: ansible_distribution == "Ubuntu"
- name: Actualizar caché de paquetes
ansible.builtin.apt:
update_cache: yes
# Instalar dependencias de sistema
- name: Instalar dependencias de sistema
package:
name:
- python3
- python3-venv
- python3-pip
- mariadb-server
- python3-pymysql
- libmariadb-dev
- screen
state: present
# Configurar MariaDB
- name: Crear base de datos y usuario de MariaDB
mysql_user:
login_unix_socket: /var/run/mysqld/mysqld.sock
name: 'root'
password: "{{ MYSQL_ROOT_PASSWORD }}"
plugin: mysql_native_password
- name: Crear base de datos
mysql_db:
name: "{{ DATABASE_DB }}"
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
# Crear entorno virtual y activar
- name: Crear y activar entorno virtual
command: python3 -m venv {{ app_directory }}/venv
args:
creates: "{{ app_directory }}/venv/bin/activate"
# Instalar dependencias de Python
- name: Instalar dependencias de Python desde requirements.txt
pip:
requirements: "{{ app_directory }}/requirements.txt"
virtualenv: "{{ app_directory }}/venv"
virtualenv_python: python3
# Copiar archivo .env
- name: Copiar archivo .env al directorio de la aplicación
copy:
src: "{{ playbook_dir }}/.env"
dest: "{{ app_directory }}/.env"
owner: vagrant
group: vagrant
mode: '0640'
# Lanzar aplicación Flask en segundo plano
- 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
# Mensaje de finalización
- name: Mensaje final
debug:
msg: "¡Aprovisionamiento completado y aplicación lanzada en http://localhost:8080!"
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!