LEMP es una variante del conocido sistema LAMP (Linux, Apache, MariaDB y PHP) que usa Nginx como servidor web en lugar de Apache.

Lo he usado muchas veces para probar alguna aplicación web. Por lo general, lo hago en un entorno limpio, para que no interfiera con configuraciones previas.

Para esto, normalmente usaríamos algún tipo de máquina virtual que hemos instalado y configurado de cero. Si es un entorno común, puede que ya tengamos un snapshot que podemos restaurar tras realizar las pruebas. O quizás podríamos usar alguna de las múltiples imágenes remotas que existen en Internet.

Sin embargo, una opción mucho más simple es usar Vagrant para administrar estas imágenes remotas y una herramienta de gestión de configuración para administrar su configuración.

Vagrant es una herramienta para la creación y configuración de entornos de desarrollo virtualizados.

Wikipedia

Vagrant puede usar diferentes proveedores para arrancar estas imágenes remotas, y también diferentes herramientas para la orquestación del software. Nosotros usaremos VirtualBox y Ansible respectivamente para estas tareas.

Ansible es una plataforma de software libre para configurar y administrar computadoras.

Wikipedia

En nuestra máquina anfitrión, solamente necesitamos tener instalados Vagrant y VirtualBox, puesto que Ansible correrá en la máquina huésped. Por lo tanto, tenemos que descargar e instalar el software apropiado para nuestro sistema operativo:

Configuración de Vagrant

La configuración de Vagrant se guarda en un único archivo llamado Vagrantfile.

Primero, le indicamos a Vagrant que deberá usar VirtualBox como proveedor por defecto:

ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox'

Luego, empezamos la configuración en sí seleccionando la imagen remota que usaremos como base. En este ejemplo, usamos la imagen oficial de Ubuntu Xenial 32-bit:

config.vm.box = 'ubuntu/xenial32'

Para configurar el hardware de la máquina virtual (512 MB de RAM y un único procesador limitado al 50%), añadimos lo siguiente:

config.vm.provider :virtualbox do |vbox|
  vbox.memory = 512
  vbox.cpus = 1
  vbox.customize ['modifyvm', :id, '--cpuexecutioncap', '50']
end

Ahora configuramos el nombre de máquina y la dirección IP del sistema operativo huésped:

config.vm.define 'lemp' do |node|
  node.vm.hostname = 'lemp'
  node.vm.network :private_network, ip: '172.28.128.10'
  node.vm.post_up_message = 'Web: http://172.28.128.10'
end

También compartiremos el subdirectorio local vagrant con la máquina huésped de forma que se monte en /vagrant:

config.vm.synced_folder 'vagrant', '/vagrant'

Por último, configuramos Ansible para que se ejecute localmente en la máquina huésped usando la configuración de /vagrant/cfg. En este directorio, buscará el archivo de inventario hosts.ini y el archivo playbook site.yml. Le diremos también que ejecute las tareas usando sudo.

config.vm.provision :ansible_local do |ansible|
  ansible.provisioning_path = '/vagrant/cfg'
  ansible.inventory_path = 'hosts.ini'
  ansible.playbook = 'site.yml'
  ansible.sudo = true
end

Al final, el archivo debería quedar así:

ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox'

Vagrant.configure('2') do |config|
  config.vm.box = 'ubuntu/xenial32'
  config.vm.provider :virtualbox do |vbox|
    vbox.memory = 512
    vbox.cpus = 1
    vbox.customize ['modifyvm', :id, '--cpuexecutioncap', '50']
  end
  config.vm.define 'lemp' do |node|
    node.vm.hostname = 'lemp'
    node.vm.network :private_network, ip: '172.28.128.10'
    node.vm.post_up_message = 'Web: http://172.28.128.10'
  end
  config.vm.synced_folder 'vagrant', '/vagrant'
  config.vm.provision :ansible_local do |ansible|
    ansible.provisioning_path = '/vagrant/cfg'
    ansible.inventory_path = 'hosts.ini'
    ansible.playbook = 'site.yml'
    ansible.sudo = true
  end
end

Configuración de Ansible

Puesto que estamos compartiendo el subdirectorio vagrant con la máquina huésped, necesitamos poner los archivos de configuración de Ansible en vagrant/cfg tal y como hemos especificado en el archivo Vagrantfile.

El archivo de inventario de Ansible contiene las máquinas que configurará. En este caso, correrá localmente en una única máquina por lo que la añadimos:

lemp        ansible_connection=local

Así mismo, los archivos playbook de Ansible guardan los pasos que se tomarán en las máquinas. Podríamos poner todo en este archivo, pero las Buenas Prácticas de Ansible recomiendan usar roles:

---
- name: Configura servidor LEMP
  hosts: lemp
  roles:
    - mariadb
    - php
    - nginx

Aquí especificamos que esta tarea se aplicará a la máquina de nombre lemp y que ejecutará los roles mariadb, php y nginx.

Rol MariaDB

Este rol instalará y configurará MariaDB. Su configuración se encuentra en el subdirectorio vagrant/cfg/roles/mariadb:

vagrant/cfg/roles/mariadb
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
└── vars
    └── main.yml

Las tareas a ejecutar se guardan en tasks/main.yml:

---
- name: Instala servidor
  package: name={{ item }} state=present
  with_items:
    - mariadb-server
    - python-mysqldb
  notify:
    - arranca mysql

- name: Cambia contraseña de root
  mysql_user:
    name: root
    host: localhost
    password: '{{ mysql_root_password }}'
    state: present

- name: Cambia bind-address
  replace:
    dest: /etc/mysql/mariadb.conf.d/50-server.cnf
    regexp: '^bind-address'
    replace: 'bind-address = {{ mysql_bind_address }}'
  notify:
    - reinicia mysql

- name: Crea base de datos de prueba
  mysql_db: name={{ mysql_db_name }} state=present

- name: Crea usuario de prueba
  mysql_user:
    name: '{{ mysql_db_user }}'
    host: '%'
    password: '{{ mysql_db_password }}'
    priv: '{{ mysql_db_name }}.*:ALL'
    state: present

Estos son los pasos que se toman:

  1. Primero, usando el módulo package, instalamos el software necesario.
  2. Luego, usando el módulo mysql_user, cambiamos la contraseña de root de MariaDB por aquella en la variable mysql_root_password.
  3. Para poder acceder al servidor desde nuestra máquina anfitrión, usamos el módulo replace para modificar el archivo de configuración de MariaDB y cambiar el parámetro bind-address por el de la variable mysql_bind_address.
  4. A continuación, creamos la base de datos de prueba, usando el 49
  5. Y, finalmente, creamos un usuario de prueba, de nuevo, usando el módulo mysql_user.

Todas las variables que usemos en este rol se pueden configurar en vars/main.yml:

---
mysql_root_password:    'root'
mysql_bind_address:     '0.0.0.0'
mysql_db_name:          'test'
mysql_db_user:          'test'
mysql_db_password:      'test'

También, definimos unos handlers que son unas tareas básicas que se ejecutan cuando otra tarea cambia algo y notifica al handler. Las usamos para asegurarnos de que el servidor está activado y para reiniciarlo cuando hacemos algún cambio en la configuración:

---
- name: arranca mysql
  service: name=mysql enabled=yes state=started

- name: reinicia mysql
  service: name=mysql state=restarted

Rol PHP

Este rol instalará PHP. Su configuración se encuentra en el subdirectorio vagrant/cfg/roles/php:

vagrant/cfg/roles/php
├── handlers
│   └── main.yml
└── tasks
    └── main.yml

Usando el módulo package, instala PHP-FPM (FastCGI Process Manager) y el módulo para comunicarse con MySQL:

---
- name: Instala PHP
  package: name={{ item }} state=present
  with_items:
    - php-fpm
    - php-mysql
  notify:
    - arranca php-fpm

También definimos el handler que se asegurará de que el servicio esté activado:

---
- name: arranca php-fpm
  service: name=php7.0-fpm enabled=yes state=started

Rol Nginx

Este rol instalará y configurará Nginx. Su configuración se encuentra en el subdirectorio vagrant/cfg/roles/nginx:

vagrant/cfg/roles/nginx
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   └── default
└── vars
    └── main.yml

Las tareas en tasks/main.yml son:

---
- name: Instala servidor
  package: name={{ item }} state=present
  with_items:
    - nginx
  notify:
    - arranca nginx

- name: Cambia configuración por defecto
  template:
    src: default
    dest: /etc/nginx/sites-available/default
  notify:
    - recarga nginx

De nuevo, usando el módulo package, instalará el software necesario. Luego, usando el módulo template, cambia la configuración del sitio por defecto copiando nuestra plantilla desde templates/default:

# Default server configuration

server {
  listen {{ http_port }} default_server;
  listen [::]:{{ http_port }} default_server;

  root {{ base_dir }};
  index index.html index.htm index.php;
  server_name _;

  location / {
    try_files $uri $uri/ =404;
  }

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
  }

  location ~ /\.ht {
    deny all;
  }
}

Las variables que se usan en la plantilla se configuran en vars/main.yml:

---
http_port:              80
base_dir:               '/vagrant/www'

De esta forma, cualquier archivo que guardemos en el subdirectorio vagrant/www de nuestra máquina anfitrión estará disponible en el servidor web de la máquina huésped. Podemos trabajar localmente con nuestra herramienta de desarrollo favorita y ver los cambios inmediatamente en el servidor web.

Por último, definimos los handlers que se asegurarán de que el servidor esté activado y de que recargamos la configuración cuando hagamos cambios:

---
- name: arranca nginx
  service: name=nginx enabled=yes state=started

- name: recarga nginx
  service: name=nginx state=reloaded

Usando Vagrant

Una vez que todo esté configurado, simplemente tendremos que arrancar la máquina huésped con el comando vagrant up lemp.

La primera vez que lancemos el comando, se descargará la imagen remota, por lo que podría tardar un rato. En subsecuentes arranques solamente se comprobará si tenemos la versión más reciente de la imagen.

Una vez finalice el arranque, podremos conectarnos al servidor web en http://172.28.128.10. Como ejemplo, supongamos que hemos puesto lo siguiente en el archivo vagrant/www/index.php:

<?php phpinfo(); ?>

Cuando nos conectemos al servidor con nuestro navegador web, veremos algo como esto:

PHP information

Podemos también conectarnos al servidor de bases de datos con el comando:

mysql --host=172.28.128.10 --user=test --password test

Y, tras introducir nuestra contraseña, podremos ejecutar órdenes de SQL:

Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 32
Server version: 10.0.29-MariaDB-0ubuntu0.16.04.1 Ubuntu 16.04

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [test]> 

Para controlar la máquina huésped, aquí están los comandos de Vagrant más importantes:

Acción Comando
arranca la máquina huésped vagrant up lemp
reinicia la máquina huésped vagrant reload lemp
apaga la máquina huésped vagrant halt lemp
arranca y reconfigura la máquina huésped vagrant up lemp --provision
conecta con la máquina huésped usando SSH vagrant ssh lemp
destruye la máquina huésped vagrant destroy lemp

Conclusión

Usar Vagrant con Ansible (o cualquier otro [gestor de configuración][vagrant provision]) nos proporciona un sistema portable y reproducible, contenido en unos pocos archivos de texto:

.
├── [ 66K]  vagrant
│   ├── [ 58K]  cfg
│   │   ├── [  37]  hosts.ini
│   │   ├── [ 54K]  roles
│   │   │   ├── [ 17K]  mariadb
│   │   │   │   ├── [4.1K]  handlers
│   │   │   │   │   └── [ 133]  main.yml
│   │   │   │   ├── [4.7K]  tasks
│   │   │   │   │   └── [ 759]  main.yml
│   │   │   │   └── [4.2K]  vars
│   │   │   │       └── [ 162]  main.yml
│   │   │   ├── [ 21K]  nginx
│   │   │   │   ├── [4.1K]  handlers
│   │   │   │   │   └── [ 131]  main.yml
│   │   │   │   ├── [4.3K]  tasks
│   │   │   │   │   └── [ 263]  main.yml
│   │   │   │   ├── [4.4K]  templates
│   │   │   │   │   └── [ 397]  default
│   │   │   │   └── [4.1K]  vars
│   │   │   │       └── [  70]  main.yml
│   │   │   └── [ 12K]  php
│   │   │       ├── [4.1K]  handlers
│   │   │       │   └── [  79]  main.yml
│   │   │       └── [4.1K]  tasks
│   │   │           └── [ 139]  main.yml
│   │   └── [  93]  site.yml
│   └── [4.1K]  www
│       └── [ 144]  index.php
└── [ 748]  Vagrantfile

Se acabó el pelearse con instaladores, restaurar snapshots o reconfigurar cosas. Puedes arrancar un sistema limpio, meter la pata, destruirlo y arrancarlo como nuevo otra vez en unos pocos minutos. Puedes incluso usar un sistema de control de versiones para guardar los archivos y compartirlos con otra gente.

Además, existen imágenes oficiales de varios sistemas operativos en la página de Vagrant. No sólo de Ubuntu, si no también de Debian, Fedora, CentOS y FreeBSD. Puedes incluso especificar tu propia imagen con el parámetro config.vm.box_url.

Así mismo, la gran cantidad de módulos de Ansible nos permite configurar el sistema operativo huésped automáticamente de casi cualquier forma imaginable, aunque puede que necesitemos adaptar muchas de las tareas para otras distros de Linux u otros sistemas operativos.

En conclusión, este método simplifica enormemente el proceso de creación y gestión de entornos de prueba.

Leer más