En este artículo pondremos en práctica los conceptos de playbooks, roles, templates y handlers para desplegar un stack completo de servidor web: Nginx + PHP-FPM + MariaDB. Utilizaremos roles separados para cada componente, variables por entorno y templates dinámicos para los archivos de configuración. Al final verificaremos el despliegue con curl.

Playbook completo de despliegue

El playbook principal orquesta tres roles: nginx, php-fpm y mariadb. Cada rol es responsable de instalar, configurar y asegurar su servicio correspondiente. Las variables específicas de cada entorno se definen en group_vars/.

«`
---
# playbooks/deploy-webstack.yml
- name: Desplegar stack web completo
  hosts: webservers
  become: yes
  vars_files:
    - "vars/{{ env }}.yml"

  roles:
    - role: common
    - role: nginx
    - role: php-fpm
    - role: mariadb
    - role: site-config```


Las variables por entorno permiten desplegar el mismo playbook en desarrollo, staging y producción sin modificar el código:

```
# group_vars/production.yml
---
env: production
server_name: "itfrontech.com"
document_root: "/var/www/itfrontech"
php_version: "8.4"
php_memory_limit: "256M"
nginx_worker_connections: 4096
mariadb_root_password: "{{ vault_mariadb_root }}"
mariadb_databases:
  - name: itfrontech
    user: wp_user
    password: "{{ vault_db_password }}" ```


Template de configuración del sitio virtual

El template del sitio virtual de Nginx incluye la integración con PHP-FPM mediante FastCGI, configuración de SSL (cuando aplica), cabeceras de seguridad y reglas de reescritura para WordPress u otras aplicaciones.

```
{# templates/site.conf.j2 #}
server {
    listen {{ nginx_port }};
    server_name {{ server_name }};
    root {{ document_root }};

    index index.php index.html index.htm;

    access_log /var/log/nginx/{{ server_name }}_access.log;
    error_log  /var/log/nginx/{{ server_name }}_error.log {{ nginx_error_level }};

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    {% if enable_ssl | default(false) %}
    listen 443 ssl http2;
    ssl_certificate /etc/ssl/certs/{{ server_name }}.pem;
    ssl_certificate_key /etc/ssl/private/{{ server_name }}.key;
    {% endif %}
}```


Handlers para reinicio y verificación

Los handlers aseguran que los servicios se reinicien o recarguen solo cuando sus configuraciones cambian, evitando downtime innecesario. Además, incluimos un handler que verifica la sintaxis de configuración antes de aplicar cambios.

```
# handlers/main.yml del rol nginx
---
- name: Recargar Nginx
  service:
    name: nginx
    state: reloaded

- name: Verificar sintaxis Nginx
  command: nginx -t
  changed_when: false

# handlers/main.yml del rol php-fpm
---
- name: Reiniciar PHP-FPM
  service:
    name: php{{ php_version }}-fpm
    state: restarted

- name: Recargar PHP-FPM
  service:
    name: php{{ php_version }}-fpm
    state: reloaded```


Verificación del despliegue

Después de ejecutar el playbook, es fundamental verificar que todo funciona correctamente. Podemos hacerlo con comandos ad-hoc y curl directamente desde el nodo controlador o mediante tareas de verificación incluidas en el propio playbook.

```
# Verificación desde el controlador
ansible webservers -i inventory/production.yml -m shell -a "curl -s -o /dev/null -w '%{http_code}' http://localhost/"

# Verificación detallada
ansible webservers -i inventory/production.yml -m shell -a "curl -I http://localhost/"

# Tarea de verificación en el playbook
- name: Verificar que Nginx responde
  uri:
    url: "http://localhost/"
    status_code: 200
    return_content: yes
  register: health_check
  failed_when:
    - health_check.status != 200

- name: Mostrar resumen del despliegue
  debug:
    msg:
      - "Nginx: OK ({{ health_check.status }})"
      - "PHP-FPM: {{ 'OK' if 'text/html' in health_check.content_type else 'ERROR' }}"
      - "Servidor: {{ ansible_hostname }} ({{ ansible_default_ipv4.address }})" ```


Con este playbook ya tienes un stack web completo y automatizado. En el próximo artículo veremos cómo organizar proyectos Ansible a escala, aplicar buenas prácticas, usar ansible-vault para secretos e integrar CI/CD.

Infografía Ansible: Desplegando servidores web con Nginx y PHP-FPM
Infografía: Ansible: Desplegando servidores web con Nginx y PHP-FPM