Software Crafters® 2025 | Creado con 🖤 para elevar el nivel de la conversación sobre programación en español | Legal
En este artículo vamos a ver cómo crear un entorno de desarrollo virtualizado utilizando Vagrant y Ansible. Esto nos permitirá tener una máquina virtual con todas las dependencias necesarias para nuestro proyecto, lo que facilita enormemente el trabajo en equipo y la portabilidad del entorno de desarrollo.
Cuando trabajamos en proyectos en equipo, es habitual encontrarnos con problemas relacionados con el entorno de desarrollo: "En mi máquina funciona", "No puedo instalar la versión X del programa Y porque tengo otros proyectos", etc. Además, si cambiamos de ordenador, tenemos que volver a configurar todo el entorno desde cero.
Para solucionar estos problemas, podemos utilizar Vagrant y Ansible:
Vagrant y Ansible son herramientas complementarias que, utilizadas conjuntamente, nos permiten:
Además, estas herramientas son de código abierto, multiplataforma y tienen una comunidad muy activa.
Para seguir este tutorial, necesitarás:
Si estás en Windows, no te preocupes por Ansible, ya que se ejecutará dentro de la máquina virtual.
VirtualBox es un software de virtualización de código abierto que nos permitirá ejecutar máquinas virtuales en nuestro ordenador. Para instalarlo, simplemente descárgalo desde la página oficial y sigue las instrucciones de instalación para tu sistema operativo.
Vagrant es una herramienta que nos permite crear y configurar entornos de desarrollo virtualizados de forma sencilla. Para instalarlo, descárgalo desde la página oficial y sigue las instrucciones de instalación para tu sistema operativo.
Una vez instalado, puedes comprobar que funciona correctamente ejecutando el siguiente comando en una terminal:
vagrant --version
Deberías ver algo como esto:
Vagrant 2.2.16
Ansible es una herramienta de automatización IT que nos permite configurar sistemas de forma declarativa y controlada. Para instalarlo en Linux/macOS, puedes usar el gestor de paquetes de tu distribución o seguir las instrucciones oficiales.
En macOS, puedes usar Homebrew:
brew install ansible
En Ubuntu/Debian:
sudo apt update sudo apt install ansible
Una vez instalado, puedes comprobar que funciona correctamente ejecutando el siguiente comando en una terminal:
ansible --version
Deberías ver información sobre la versión de Ansible instalada.
Ahora que tenemos todas las herramientas necesarias, vamos a crear nuestro entorno de desarrollo virtualizado. En este ejemplo, vamos a crear un entorno para una aplicación web basada en PHP y MySQL, pero puedes adaptarlo a tus necesidades.
Vamos a crear la siguiente estructura de directorios para nuestro proyecto:
miproyecto/ ├── ansible/ │ ├── playbook.yml │ └── roles/ │ ├── common/ │ ├── mysql/ │ ├── nginx/ │ └── php/ ├── Vagrantfile └── www/ └── index.php
Puedes crear esta estructura ejecutando los siguientes comandos:
mkdir -p miproyecto/ansible/roles/{common,mysql,nginx,php} mkdir -p miproyecto/www touch miproyecto/Vagrantfile touch miproyecto/ansible/playbook.yml touch miproyecto/www/index.php
El archivo
Vagrantfile
es el encargado de configurar la máquina virtual que utilizaremos como entorno de desarrollo. Vamos a utilizar Ubuntu 20.04 como sistema operativo base y configuraremos la red y los recursos de la máquina virtual.
Abre el archivo
Vagrantfile
y añade el siguiente contenido:
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| # Utilizamos Ubuntu 20.04 como base config.vm.box = "ubuntu/focal64" # Configuramos la red (IP privada) config.vm.network "private_network", ip: "192.168.33.10" # Configuramos los recursos de la máquina virtual config.vm.provider "virtualbox" do |vb| vb.memory = "2048" vb.cpus = 2 end # Montamos la carpeta del proyecto config.vm.synced_folder "./www", "/var/www/html", owner: "www-data", group: "www-data" # Provisionamos la máquina con Ansible config.vm.provision "ansible_local" do |ansible| ansible.playbook = "ansible/playbook.yml" ansible.install_mode = "pip" ansible.become = true end end
Esta configuración:
www
del proyecto en /var/www/html
de la máquina virtualAhora vamos a configurar Ansible para que instale y configure todo lo necesario en la máquina virtual. Primero, vamos a crear el playbook principal.
Abre el archivo
ansible/playbook.yml
y añade el siguiente contenido:
--- - hosts: all become: true roles: - common - nginx - php - mysql
Este playbook indica que se aplicarán los roles
common
, nginx
, php
y mysql
a la máquina virtual.
El rol
common
se encargará de instalar paquetes básicos y configurar el sistema operativo. Vamos a crear los archivos necesarios:
mkdir -p miproyecto/ansible/roles/common/tasks touch miproyecto/ansible/roles/common/tasks/main.yml
Abre el archivo
miproyecto/ansible/roles/common/tasks/main.yml
y añade el siguiente contenido:
--- - name: Actualizar lista de paquetes apt: update_cache: yes cache_valid_time: 3600 - name: Instalar paquetes básicos apt: name: "{{ item }}" state: present loop: - vim - git - curl - htop - zip - unzip - python3-pip - name: Configurar zona horaria timezone: name: Europe/Madrid
El rol
nginx
se encargará de instalar y configurar el servidor web Nginx. Vamos a crear los archivos necesarios:
mkdir -p miproyecto/ansible/roles/nginx/{tasks,templates} touch miproyecto/ansible/roles/nginx/tasks/main.yml touch miproyecto/ansible/roles/nginx/templates/default.conf.j2
Abre el archivo
miproyecto/ansible/roles/nginx/tasks/main.yml
y añade el siguiente contenido:
--- - name: Instalar Nginx apt: name: nginx state: present - name: Configurar virtual host template: src: default.conf.j2 dest: /etc/nginx/sites-available/default notify: restart nginx - name: Asegurar que Nginx está en ejecución service: name: nginx state: started enabled: yes - name: Crear manejador para reiniciar Nginx meta: flush_handlers
Crea el archivo
miproyecto/ansible/roles/nginx/handlers/main.yml
con el siguiente contenido:
--- - name: restart nginx service: name: nginx state: restarted
Abre el archivo
miproyecto/ansible/roles/nginx/templates/default.conf.j2
y añade el siguiente contenido:
server { listen 80 default_server; listen [::]:80 default_server; root /var/www/html; index index.php index.html index.htm; server_name _; location / { try_files $uri $uri/ =404; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; } location ~ /.ht { deny all; } }
El rol
php
se encargará de instalar y configurar PHP y las extensiones necesarias. Vamos a crear los archivos necesarios:
mkdir -p miproyecto/ansible/roles/php/{tasks,templates} touch miproyecto/ansible/roles/php/tasks/main.yml touch miproyecto/ansible/roles/php/templates/php.ini.j2
Abre el archivo
miproyecto/ansible/roles/php/tasks/main.yml
y añade el siguiente contenido:
--- - name: Instalar PHP y extensiones apt: name: "{{ item }}" state: present loop: - php7.4-fpm - php7.4-cli - php7.4-common - php7.4-curl - php7.4-json - php7.4-mbstring - php7.4-mysql - php7.4-xml - php7.4-zip - name: Configurar PHP template: src: php.ini.j2 dest: /etc/php/7.4/fpm/php.ini notify: restart php-fpm - name: Asegurar que PHP-FPM está en ejecución service: name: php7.4-fpm state: started enabled: yes - name: Crear manejador para reiniciar PHP-FPM meta: flush_handlers
Crea el archivo
miproyecto/ansible/roles/php/handlers/main.yml
con el siguiente contenido:
--- - name: restart php-fpm service: name: php7.4-fpm state: restarted
Abre el archivo
miproyecto/ansible/roles/php/templates/php.ini.j2
y añade el siguiente contenido:
[PHP] engine = On short_open_tag = Off precision = 14 output_buffering = 4096 zlib.output_compression = Off implicit_flush = Off unserialize_callback_func = serialize_precision = -1 disable_functions = disable_classes = zend.enable_gc = On expose_php = Off max_execution_time = 30 max_input_time = 60 memory_limit = 128M error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors = Off display_startup_errors = Off log_errors = On log_errors_max_len = 1024 ignore_repeated_errors = Off ignore_repeated_source = Off report_memleaks = On html_errors = On variables_order = "GPCS" request_order = "GP" register_argc_argv = Off auto_globals_jit = On post_max_size = 8M auto_prepend_file = auto_append_file = default_mimetype = "text/html" default_charset = "UTF-8" doc_root = user_dir = enable_dl = Off file_uploads = On upload_max_filesize = 2M max_file_uploads = 20 allow_url_fopen = On allow_url_include = Off default_socket_timeout = 60 [CLI Server] cli_server.color = On [Date] date.timezone = Europe/Madrid [Pdo_mysql] pdo_mysql.cache_size = 2000 pdo_mysql.default_socket= [mail function] SMTP = localhost smtp_port = 25 mail.add_x_header = Off [SQL] sql.safe_mode = Off [MySQL] mysql.allow_local_infile = On mysql.allow_persistent = On mysql.cache_size = 2000 mysql.max_persistent = -1 mysql.max_links = -1 mysql.default_port = mysql.default_socket = mysql.default_host = mysql.default_user = mysql.default_password = mysql.connect_timeout = 60 mysql.trace_mode = Off [MySQLi] mysqli.max_persistent = -1 mysqli.allow_persistent = On mysqli.max_links = -1 mysqli.cache_size = 2000 mysqli.default_port = 3306 mysqli.default_socket = mysqli.default_host = mysqli.default_user = mysqli.default_pw = mysqli.reconnect = Off [mysqlnd] mysqlnd.collect_statistics = On mysqlnd.collect_memory_statistics = Off [OPcache] opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1
El rol
mysql
se encargará de instalar y configurar MySQL. Vamos a crear los archivos necesarios:
mkdir -p miproyecto/ansible/roles/mysql/{tasks,templates} touch miproyecto/ansible/roles/mysql/tasks/main.yml touch miproyecto/ansible/roles/mysql/templates/my.cnf.j2
Abre el archivo
miproyecto/ansible/roles/mysql/tasks/main.yml
y añade el siguiente contenido:
--- - name: Configurar variables para la instalación de MySQL debconf: name: mysql-server question: "{{ item.question }}" value: "{{ item.value }}" vtype: "{{ item.vtype }}" loop: - { question: 'mysql-server/root_password', value: 'root', vtype: 'password' } - { question: 'mysql-server/root_password_again', value: 'root', vtype: 'password' } - name: Instalar MySQL apt: name: "{{ item }}" state: present loop: - mysql-server - mysql-client - python3-mysqldb - name: Configurar MySQL template: src: my.cnf.j2 dest: /etc/mysql/my.cnf notify: restart mysql - name: Crear base de datos mysql_db: name: miproyecto state: present login_user: root login_password: root - name: Crear usuario de la base de datos mysql_user: name: miproyecto password: miproyecto priv: 'miproyecto.*:ALL' state: present login_user: root login_password: root - name: Asegurar que MySQL está en ejecución service: name: mysql state: started enabled: yes - name: Crear manejador para reiniciar MySQL meta: flush_handlers
Crea el archivo
miproyecto/ansible/roles/mysql/handlers/main.yml
con el siguiente contenido:
--- - name: restart mysql service: name: mysql state: restarted
Abre el archivo
miproyecto/ansible/roles/mysql/templates/my.cnf.j2
y añade el siguiente contenido:
[mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql log-error = /var/log/mysql/error.log bind-address = 0.0.0.0 symbolic-links = 0 sql_mode = NO_ENGINE_SUBSTITUTION max_allowed_packet = 16M innodb_buffer_pool_size = 512M innodb_log_file_size = 128M [mysql] default-character-set = utf8mb4 [mysqldump] max_allowed_packet = 16M
Vamos a crear un archivo PHP de prueba para comprobar que todo funciona correctamente. Abre el archivo
www/index.php
y añade el siguiente contenido:
<?php phpinfo();
Ahora que tenemos todo configurado, vamos a poner en marcha nuestro entorno de desarrollo virtualizado. Para ello, abre una terminal en la carpeta del proyecto y ejecuta el siguiente comando:
vagrant up
Este comando creará la máquina virtual, la configurará según lo especificado en el
Vagrantfile
y ejecutará el playbook de Ansible para instalar y configurar todo lo necesario.
El proceso puede tardar varios minutos la primera vez, ya que tiene que descargar la imagen base y todos los paquetes necesarios.
Una vez finalizado, podrás acceder a tu entorno de desarrollo a través de la IP configurada en el
Vagrantfile
:
http://192.168.33.10
Deberías ver la página de información de PHP (phpinfo()), lo que indica que tu entorno de desarrollo está funcionando correctamente.
Aquí tienes algunos comandos útiles de Vagrant para gestionar tu entorno de desarrollo:
vagrant up
: Inicia la máquina virtualvagrant halt
: Detiene la máquina virtualvagrant reload
: Reinicia la máquina virtualvagrant provision
: Vuelve a ejecutar el playbook de Ansiblevagrant ssh
: Inicia una sesión SSH en la máquina virtualvagrant destroy
: Destruye la máquina virtualEl entorno de desarrollo que hemos creado es bastante básico, pero puedes personalizarlo según tus necesidades añadiendo más roles a Ansible, modificando la configuración de los servicios, etc.
Por ejemplo, podrías añadir roles para instalar:
También puedes modificar la configuración de los servicios existentes o añadir más bases de datos, usuarios, etc.
Una de las ventajas de utilizar Vagrant y Ansible es que puedes compartir tu entorno de desarrollo con tu equipo de forma sencilla. Simplemente, añade los archivos
Vagrantfile
y la carpeta ansible
al control de versiones (Git, por ejemplo), y cualquier persona podrá replicar exactamente el mismo entorno de desarrollo con un simple vagrant up
.
En este artículo hemos visto cómo crear un entorno de desarrollo virtualizado utilizando Vagrant y Ansible. Esto nos permite tener una máquina virtual con todas las dependencias necesarias para nuestro proyecto, lo que facilita enormemente el trabajo en equipo y la portabilidad del entorno de desarrollo.
Utilizando estas herramientas, podemos asegurarnos de que todos los miembros del equipo utilizan el mismo entorno de desarrollo, evitando así los típicos problemas de "en mi máquina funciona".
Además, si cambiamos de ordenador, solo tenemos que clonar el repositorio y ejecutar
vagrant up
para tener exactamente el mismo entorno de desarrollo que antes.
En resumen, utilizar Vagrant y Ansible para crear entornos de desarrollo virtualizados es una práctica muy recomendable para cualquier equipo de desarrollo, ya que aporta numerosas ventajas y facilita enormemente el trabajo en equipo.