Saltar al contenido principal

Inventarios y comandos ad-hoc 📋

Terminamos el capítulo anterior lanzando un comando ad-hoc ping contra nuestros hosts. Esto es solo la punta del iceberg. En este capítulo vamos a profundizar en cómo Ansible organiza y gestiona los hosts a través de los inventarios y cómo ejecutar comandos rápidos sin necesidad de escribir un Playbook completo.

Veremos sobretodo la potencia de ansible para gestionar infraestructuras complejas, numerosas e incluso dinámicas si nos apoyamos en la nube.

Video pendiente de grabación

Inventario estático (hosts, grupos)

Antes de que Ansible pueda configurar algo, necesita saber qué está configurando. Aquí es donde entra el Inventario.

Imagina tu lista de contactos en el teléfono. No tienes a todo el mundo mezclado sin orden. Probablemente los tengas organizados mentalmente (o con etiquetas):

  • Familia: Mamá, Papá, Hermanos.
  • Trabajo: Jefe, RRHH, Equipo DevOps.
  • Amigos: Los del fútbol, Los de la uni.

Si quieres enviar un mensaje de "¡Feliz JARGÜELIN grupo!" a todos, no vas uno por uno; seleccionas el grupo "Familia" o "Amigos".

En Ansible, el inventario es esa agenda. Es un archivo donde listas las direcciones IP o nombres de dominio de tus servidores y los organizas en grupos para aplicar automatizaciones de forma masiva.

Formatos: INI vs YAML

Ansible soporta dos formatos principales para definir inventarios estáticos: el clásico INI y el moderno YAML.

Formato INI (el clásico)

Es simple, fácil de leer para humanos y muy parecido a los archivos de configuración antiguos de Windows.

[madrid]
target1 ansible_port=55000 ansible_host=localhost
target2 ansible_port=55001 ansible_host=localhost

[barcelona]
target3 ansible_port=55002 ansible_host=localhost

[servers:children]
madrid
barcelona

[all:vars]
ansible_user=ansible
ansible_ssh_pass=ansible

Formato YAML (el recomendado) ✅

Aunque INI es más corto, te recomiendo encarecidamente usar YAML. ¿Por qué? Porque es consistente con el resto de Ansible (Playbooks, Roles) y maneja mucho mejor las variables complejas.

# inventory.yml
all:
children:
madrid:
hosts:
target1:
ansible_host: localhost
ansible_port: 55000
target2:
ansible_host: localhost
ansible_port: 55001
barcelona:
hosts:
target3:
ansible_host: localhost
ansible_port: 55002
servers:
children:
madrid:
barcelona:

Conceptos clave

1. Grupos y hosts

Los Hosts son las máquinas individuales. Los Grupos son conjuntos de hosts. Un host puede pertenecer a múltiples grupos: en nuestro ejemplo, target1 pertenece tanto a madrid como a servers (porque servers agrupa a madrid).

2. Grupos de Grupos (children)

Esta es una característica potente. Puedes crear "meta-grupos". En el ejemplo anterior, el grupo servers no contiene hosts directamente, sino que agrupa madrid y barcelona. Si ejecutas una tarea contra servers, Ansible la ejecutará en target1, target2 y target3 a la vez. Pero también puedes apuntar solo a madrid para afectar únicamente a target1 y target2.

3. Variables de inventario

Puedes asignar variables directamente en el inventario, aunque la mejor práctica es separarlas en ficheros group_vars y host_vars. Esto mantiene el inventario limpio y las variables versionadas de forma independiente.

# inventory.yml con variables inline (válido pero no recomendado a escala)
all:
children:
madrid:
hosts:
target1:
ansible_host: localhost
ansible_port: 55000
http_port: 8080 # Variable específica para este host
target2:
ansible_host: localhost
ansible_port: 55001
barcelona:
hosts:
target3:
ansible_host: localhost
ansible_port: 55002
servers:
children:
madrid:
barcelona:
vars:
ansible_user: ansible # Variable para todos los hosts
ansible_ssh_pass: ansible

La alternativa recomendada es usar directorios group_vars/ y host_vars/ junto al inventario:

inventario/
├── inventory.yml
├── group_vars/
│ ├── servers.yml # Variables para todos los hosts (madrid + barcelona)
│ ├── madrid.yml # Variables solo para el grupo madrid
│ └── barcelona.yml # Variables solo para el grupo barcelona
└── host_vars/
└── target1.yml # Variables exclusivas de target1
# group_vars/servers.yml — aplica a todos los hosts del inventario
ansible_user: ansible
ansible_ssh_pass: ansible
# host_vars/target1.yml — sobrescribe o añade variables solo para target1
ansible_port: 55000
ansible_host: localhost
http_port: 8080

De este modo todos los hosts heredan las credenciales de servers.yml. Si el grupo madrid necesita una variable específica (p.ej. un proxy distinto), se define en madrid.yml sin tocar el resto. Y si solo target1 tiene algo particular (como http_port), va a host_vars/target1.yml.

Host patterns: selección precisa de objetivos

Los host patterns son la forma de especificar sobre qué hosts quieres ejecutar comandos o playbooks. Es como el lenguaje para "apuntar" a tus máquinas.

Patrones básicos

PatrónDescripciónEjemplo
all o *Todos los hosts del inventarioansible all -m ping
'hostname'Un host específicoansible target1 -m ping
'groupname'Todos los hosts de un grupoansible madrid -m ping
'host1':'host2'Múltiples hosts o grupos (OR lógico)ansible madrid:barcelona -m ping
'host1':'host2'Intersección (hosts en ambos grupos)ansible servers:&madrid -m ping
'host1':'host2'Exclusión (hosts en host1 pero NO en host2)ansible servers:!barcelona -m ping

Patrones con comodines y rangos

# Wildcards (comodines)
ansible 'target*' -m ping # target1, target2, target3, etc.

# Rangos numéricos
ansible 'target[1:3]' -m ping # target1, target2, target3
ansible 'target[1:3:2]' -m ping # target1, target3 (salto de 2)

# Rangos alfabéticos
ansible 'target[a:c]' -m ping # targeta, targetb, targetc

Ejemplos prácticos de patrones

# Solo los servidores de madrid
ansible 'madrid' -m service -a "name=nginx state=restarted"

# Todos los servers excepto barcelona (equivale a solo madrid)
ansible 'servers:!barcelona' -m apt -a "name=vim state=latest"

# Hosts en servers Y en madrid a la vez (intersección)
ansible 'servers:&madrid' -m shell -a "df -h"

# Todos los hosts menos target3
ansible 'all:!target3' -m ping
Uso con --limit

Puedes usar patrones con la opción --limit en tus playbooks para restringir la ejecución:

ansible-playbook deploy.yml --limit "servers:!barcelona"

Inventarios dinámicos (Cloud)

El problema del inventario estático

En un entorno tradicional on-premise, los servidores son como mascotas: tienen nombre, los cuidas y rara vez cambian de IP. Un archivo de texto estático funciona bien.

Pero en la Nube (AWS, Azure, Google Cloud), los servidores son ganado. Se crean y destruyen automáticamente según la demanda (Auto Scaling). Mantener un archivo inventory.yml a mano es imposible; estaría desactualizado en minutos.

La solución: Inventory Plugins

Antiguamente se usaban scripts ejecutables complejos. Hoy en día, Ansible utiliza Inventory Plugins. Estos plugins consultan la API de tu proveedor de nube en tiempo real para saber qué máquinas existen ahora mismo.

¿Cómo funciona?

  1. Ansible lee la configuración del plugin (ej: "dame todas las máquinas con el tag Environment: Production").
  2. El plugin pregunta a la nube.
  3. La nube responde con la lista actual.
  4. Ansible ejecuta el Playbook sobre esa lista fresca.

Caso práctico: AWS EC2 Plugin

Para usar el inventario dinámico con AWS, usamos el plugin aws_ec2. El archivo de configuración debe terminar en aws_ec2.yml o aws_ec2.yaml.

Ejemplo de configuración (demo_aws_ec2.yml)

# demo_aws_ec2.yml
plugin: aws_ec2

# Regiones donde buscar
regions:
- us-east-1
- eu-west-1

# Filtros: Solo quiero instancias que estén corriendo
filters:
instance-state-name: running

# Agrupación automática (La magia ✨)
# Crea grupos de Ansible basados en tags de AWS
keyed_groups:
- key: tags.Environment
prefix: env
separator: "_"
- key: tags.Role
prefix: role
separator: "_"

# Variables de host
compose:
ansible_host: public_ip_address

Resultado

Si tienes una instancia en AWS con los tags Environment: Production y Role: Webserver, Ansible generará automáticamente los grupos:

  • env_Production
  • role_Webserver

Ahora puedes lanzar tu playbook así:

ansible-playbook deploy.yml -i demo_aws_ec2.yml --limit env_Production

Combinar varios inventarios dinámicos

Si tu infraestructura es híbrida (AWS + Azure + on-prem), apunta -i a un directorio y Ansible los combinará automáticamente:

inventories/prod/
├── static_onprem.ini
├── demo_aws_ec2.yml
├── demo_azure_rm.yml
└── demo_gcp_compute.yml
ansible-playbook deploy.yml -i inventories/prod/

💡 Pro tip: usa ansible-inventory -i inventories/prod/ --graph para ver qué hosts y grupos genera cada plugin antes de lanzar nada.

Comandos ad-hoc (ping, shell, setup)

¿Ad-hoc o Playbook?

Hasta ahora hemos hablado de automatización, pero a veces no necesitas escribir una "receta" completa (Playbook) para algo sencillo.

  • Playbooks: Son como la artillería pesada. Orquestación compleja, múltiples pasos, idempotencia garantizada, guardados en Git. Úsalos para despliegues y configuraciones.
  • Comandos Ad-Hoc: Son como un francotirador. Tareas rápidas, de una sola vez, diagnósticos o comprobaciones rápidas. No se suelen guardar.

¿Cuándo usar Ad-Hoc?

  • "¿Están todos los servidores encendidos?"
  • "Necesito reiniciar Nginx en todos los frontales YA."
  • "¿Qué versión de Kernel tienen mis máquinas?"
  • "Copia este archivo de log a mi máquina local."

Sintaxis básica

La estructura es simple:

ansible [grupo_o_host] -i [inventario] -m [modulo] -a "[argumentos]"
  • -m: El módulo a usar (ping, shell, copy, apt...).
  • -a: Los argumentos específicos del módulo (si los necesita).

Tabla de módulos comunes para ad-hoc

MóduloUso PrincipalEjemplo
pingVerificar conectividad y autenticación SSH (no es un ping ICMP).ansible all -m ping
commandEjecutar comandos simples (sin pipes `ni redirecciones>`). Es el default.
shellEjecutar comandos complejos de shell (con pipes, variables de entorno, etc).ansible barcelona -m shell -a "ps aux | grep nginx"
apt / yumInstalar o actualizar paquetes rápidamente.ansible all -m apt -a "name=vim state=latest"
serviceGestionar servicios (start, stop, restart).ansible servers -m service -a "name=nginx state=restarted"
setup¡Importante! Recolecta y muestra los "Facts" (información del sistema).ansible target1 -m setup

Ejemplos prácticos

1. Verificar conexión masiva

El "Hola Mundo" de Ansible. Si esto responde "pong" en verde, tienes acceso SSH y Python instalado.

ansible all -m ping

2. Reiniciar un servicio en producción

Imagina que hay una incidencia y necesitas reiniciar Apache en 50 servidores.

ansible servers -m service -a "name=apache2 state=restarted" --become

(Nota: --become es para usar sudo)

3. Espiar los "Facts" de un servidor

El módulo setup es increíblemente útil. Te devuelve un JSON gigante con toda la info de la máquina: IPs, memoria, discos, SO, etc.

ansible target1 -m setup

Puedes filtrar para ver solo lo que te interesa:

ansible target1 -m setup -a "filter=ansible_distribution*"
# Salida: Ubuntu, CentOS, RedHat...