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
Paso 4: Crear el Vagrantfile
Crea un archivo `Vagrantfile` en la raíz del directorio `flask_testing_project/`. Este archivo configurará la máquina virtual de Vagrant, incluyendo la asignación de puertos y el archivo de aprovisionamiento que usaremos. Añade el siguiente contenido al `Vagrantfile`:
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
Paso 5: Escribir el Script de Aprovisionamiento provision.sh
Dentro del directorio `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 `provision.sh` debe ser el siguiente:
#!/bin/bash
# Actualizar el sistema
sudo apt-get update
# Instalar Python, pip y MariaDB
sudo apt-get install -y python3 python3-venv python3-pip mariadb-server
# Configurar MariaDB
sudo mysql -e "CREATE DATABASE IF NOT EXISTS flask_db;"
sudo mysql -e "CREATE USER 'flask_user'@'localhost' IDENTIFIED BY 'flask_password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON flask_db.* TO 'flask_user'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
# Crear y activar entorno virtual
cd /home/vagrant/app
python3 -m venv venv
source venv/bin/activate
# Instalar dependencias de Python
pip install -r requirements.txt
# Lanzar aplicación en segundo plano
nohup python3 app.py &
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!