Ansible desde cero: Agentless, Push‑Based y YAML Declarativo
Introducción
Ansible es una herramienta de automatización de TI que ha ganado popularidad masiva gracias a su enfoque agentless (sin agente), su modelo push‑based (empuje) y su sintaxis YAML declarativa. Si querés empezar a gestionar infraestructura, implementar aplicaciones o ejecutar tareas repetitivas de manera confiable, Ansible es un excelente punto de partida.
Este artículo está dirigido a lectores que nunca usaron Ansible, pero que ya están familiarizados con conceptos básicos de TI o programación. A lo largo del post, verás cómo funciona Ansible internamente, por qué su arquitectura agentless es importante, cómo se envían tareas al remote host y cómo escribir playbooks YAML claros que expresen qué querés lograr, no cómo hacerlo paso a paso.
Al finalizar, vas a poder:
- Entender el modelo de controller‑node y por qué no se necesita un agente en cada host.
- Escribir un inventario básico y utilizar el plugin de redes para conectarte a dispositivos.
- Crear un playbook simple con tareas, including, loop y handlers.
- Ejecutar el playbook y leer su salida, identificando el orden de ejecución y el formato de los resultados.
- Evitar errores comunes como conflictos de variables, violaciones de SELinux o problemas de permisos de SSH.
Todo se demostrará con ejemplos concretos y sus salidas esperadas, para que puedas seguir paso a paso sin adivinar qué es lo que debería ocurrir.
¿Qué es Ansible y por qué es “agentless”?
Arquitectura típica
Ansible se compone de un solo nodo controller (la máquina donde corrés ansible‑playbook) y uno o más nodos remote (los equipos, contenedores, dispositivos de red, etc.). El controller mantiene un inventario que describe los hosts remotos, mientras que cada nodo remoto ejecuta únicamente un cliente SSH ligero (el “módulo de conexión”) cuando el controller le envía una tarea.
Como no hay un servicio daemon corriendo en los hosts remotos, el modelo es agentless. Esto reduce el overhead operativo, elimina la necesidad de mantener un agente actualizado en cada máquina y simplifica las operaciones de seguridad. El controller empuja (push) tareas al nodo remoto, las cuales se ejecutan sobre la conexión SSH existente.
Modelo push‑based
Contrastá esto con un modelo pull‑based (como Puppet o Chef, donde el agente solicita trabajo del servidor). En Ansible, el controller es proactivo: crea un ejecutivo de tareas (task executor) por cada conexión SSH, lo ejecuta, recopila el resultado y cierra la conexión. Dado que cada conexión es efímera, la escalabilidad se beneficia de la naturaleza asincrónica de SSH y del modelo de trabajo ligero de Ansible.
Sintaxis declarativa YAML
Un playbook Ansible está escrito en YAML, un formato de datos estructurado y legible por humanos. La declaración es declarativa: le decís al sistema qué querés lograr, no cómo lograrlo. Bajo el capó, Ansible traduce estas instrucciones a módulos específicos del sistema operativo, implementaciones de controladores de red, scripts PowerShell, etc.
El estilo declarativo reduce el error humano porque estás declarando el estado deseado, no una secuencia de comandos imperativos que pueden pasar por alto condiciones.
Instalación – Configuración mínima
Ansible suele instalarse con pip o mediante el gestor de paquetes del sistema. A partir de Ansible 9.x, Python 3.10+ es obligatorio.
# Uso típico de Linux/macOS con pip (requiere sudo solo para la instalación del sistema)
python3 -m pip install --user ansible
# Verificar versión
ansible‑playbook --versionDespués de la instalación, tendrás estos binarios:
ansible– comando de línea para acciones de inventario y depuración.ansible‑playbook– ejecutor principal de playbooks.ansible‑doc– documentos de referencia de módulos.ansible‑GALAXY– gestor de colecciones comunitarias.
Consejo: Si trabajás en un entorno empresarial, es posible que prefieras usar Ansible‑AWX (una UI de código abierto) o Ansible Tower (la versión comercial). Las mismas funciones subyacentes están disponibles, solo que empaquetadas en una interfaz web.
Creando tu primer inventario
Un inventario es un mapeo de hosts a grupos, variables y opciones de conexión. El formato más simple es el estilo de archivo de hosts:
# inventario.ini
webservers:
192.168.1.10
192.168.1.11
databases:
192.168.1.20Guardá este contenido en inventario.ini. Ansible también puede leer desde /etc/ansible/hosts o desde una fuente de datos dinámica como ansible‑inventory --list.
Uso del inventario
Podés listar hosts:
ansible‑all -i inventario.ini --list-hosts webserversSalida esperada:
HOSTS:
192.168.1.10
192.168.1.11También podés agrupar hosts para aplicar políticas de ejecución por grupo, lo cual es útil para escenarios de rolling updates.
Escribiendo tu primer playbook
Un playbook es una lista ordenada de plays. Cada play opera sobre un grupo de hosts, define tareas y puede incluir handlers.
Bloque mínimo
# primer_playbook.yml
---
- name: Ejemplo inicial – saludar a los webservers
hosts: webservers
remote_user: admin
tasks:
- name: Mostrar saludo
debug:
msg: "¡Hola desde {{ inventory_hostname }}!"Explicando el YAML:
---– separador de documentos YAML.name:– etiqueta descriptiva para el play (aparece en la salida).hosts:– referencia al grupo definido en inventario (webservers).remote_user:– usuario SSH usado para conectar.tasks:– lista de tareas, cada una conname:(descriptivo) y un módulo (debug:).
Ejecución y salida esperada
ansible‑playbook -i inventario.ini primer_playbook.ymlSalida típica (mostrada con colores, pero aquí en texto plano):
PLAY [Ejemplo inicial – saludar a los webservers] *******************************************************************
TASK [Mostrar saludo] ******************************************************************
: ok: [192.168.1.10] => {"msg": "¡Hola desde 192.168.1.10!"}
: ok: [192.168.1.11] => {"msg": "¡Hola desde 192.168.1.11!"}
PLAY RECAP ********************************************************************
192.168.1.10 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 time=12ms
192.168.1.11 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 time=13msEl módulo debug imprime el mensaje al STDOUT, pero no realiza cambios en el host. Es perfecto para pruebas rápidas.
Módulos comunes – “qué podés hacer”
Ansible incluye cientos de módulos integrados. Algunos módulos básicos son:
| Módulo | Función | Comando de ejemplo |
|---|---|---|
ping |
Test de conectividad SSH liviano | ansible webservers -m ping -i inventario.ini |
setup |
Recopilar facts del sistema (variables de ejecución) | ansible webservers -m setup -i inventario.ini |
copy |
Copiar archivos a hosts remotos | ansible webservers -m copy -a src=./app.conf dest=/etc/app.conf |
shell |
Ejecutar un comando de shell (imperativo) | ansible webservers -m shell -a 'systemctl restart apache2' |
command |
Ejecutar un comando sin shell (imperativo) | ansible webservers -m command -a uptime |
include_tasks |
Reutilizar tareas de otro YAML | include_tasks: common.yml |
import_tasks |
Igual que include_tasks, pero evalúa el archivo de destino una vez |
import_tasks: common.yml |
Uso del módulo ping
ansible -i inventario.ini all -m pingSalida esperada:
192.168.1.10 : pong
192.168.1.11 : pong
192.168.1.20 : pongEl módulo ping es útil para verificar rápidamente que la conexión SSH está funcionando y que el inventario incluye los hosts correctos.
Tareas avanzadas – including, loop y handlers
Including tareas
Cuando tenés un conjunto de tareas reutilizables (por ejemplo, una tarea de “reiniciar servicio”), podés incluirlas en múltiples playbooks:
# common.yml
- name: Asegurar que el servicio está instalado
apt:
name: myapp
state: present
when: ansible_os_family == "Debian"
- name: Asegurar que el servicio está instalado (RPM)
yum:
name: myapp
state: present
when: ansible_os_family == "RedHat"Luego, un playbook puede referenciar este archivo:
- hosts: webservers
tasks:
- include_tasks: common.ymlLoops – iterar sobre listas o rangos
Suposé que querés crear múltiples usuarios en un host:
- name: Crear usuarios de ejemplo
user:
name: "{{ item }}"
state: present
shell: /bin/bash
loop:
- alice
- bob
- carlaSalida esperada (para cada usuario):
: changed: [192.168.1.10] => {"item": "alice"}
: changed: [192.168.1.10] => {"item": "bob"}
: changed: [192.168.1.10] => {"item": "carla"}También podés usar with_* de Ansible (por ejemplo, with_inventory, with_file). loop es el más moderno y legible.
Handlers – tareas que se ejecutan solo al final
Handlers son como tareas, pero solo corren si algún servicio precedente falló (o cuando se notifican explícitamente). Son ideales para finales de servicio, recolección de logs o limpieza.
- name: Iniciar el servicio web
service:
name: apache2
state: started
notify: Registrar reinicio
- name: Configurar el servicio web
template:
src: apache.conf.j2
dest: /etc/apache2/apache2.conf
handlers:
- name: Registrar reinicio
debug:
msg: "Servicio web reiniciado en {{ inventory_hostname }}"Si la tarea template falla, la notificación Registrar reinicio se ejecutará al final del play. Si todo está bien, el handler se ignora.
Variables y templates
Variables – overrides de configuración
Podés definir variables en múltiples niveles:
- Variables globales –
/etc/ansible/ansible.cfgoansible.cfgen el current directory. - Variables de inventario –
inventario.inicon sintaxis clave=valor. - Variables de host –
hosts.inicon cada host como sección. - Variables de grupo –
grupo.ymlbajo cada grupo. - Variables de papel – archivos
vars/*.ymldentro de una colección.
Ejemplo de variable de grupo (grupo.yml):
grupo_databases:
db_user: admin
db_port: 5432Luego, usala en una tarea:
- name: Obtener estado de la base de datos
community.general.postgresql_query:
login_user: "{{ grupo_databases.db_user }}"
login_host: "{{ inventory_hostname }}"
query: "SELECT 1"Templates – plantillas dinámicas
Las plantillas se usan comúnmente con jinja2. Por ejemplo, un template apache.conf.j2 puede insertar variables de host:
# apache.conf.j2
ServerName {{ ansible_default_ipv4.address }}
User {{ ansible_user }}La tarea template renderiza la plantilla en el host remoto.
Ejecutando desde la CLI – niveles de verbosidad
Ansible ofrece niveles de verbosidad -v, -vv, -vvv, etc.:
-v– muestra el nombre de la tarea a medida que avanza.-vv– agrega los nombres de los hosts.-vvv– muestra la salida completa del comando remoto.
Un comando típico con salida detallada:
ansible‑playbook -i inventario.ini primer_playbook.yml -vvLa salida incluye logs de conexión SSH, comandos remotos y el resultado final.
Prácticas recomendadas
- Usá roles para organizar códigos reutilizables. Un rol es un directorio con
tasks/,vars/,defaults/,handlers/, etc.ansible‑galaxy initcrea uno rápidamente. - Versioná tus playbooks y plantillas. El control de versiones ayuda a rastrear cambios e invertir si algo falla.
- Separá entornos (
desarrollo,testing,producción) con inventarios y grupos diferentes. - Aplicá políticas de seguridad con SELinux y restricciones de ejecución. La auditoría de tareas (
ansible‑audit) puede ayudar a asegurar que no haya código peligroso. - Usá módulos de texto en lugar de
shell/commandcuando sea posible. Los módulos ofrecen serialización segura, manejo claro de errores y salida estructurada. - Realizá chequeos de sintaxis con
ansible‑playbook --syntax-check. Esto detecta errores de YAML antes de la ejecución.
Errores comunes y cómo evitarlos
| Error | Causa típica | Solución rápida |
|---------|----------------|-----------|-
| Error de clave SSH desconocida | No hay clave pública en el host remoto o está en un ubicación incorrecta. | Generá una clave (ssh‑keygen) y copiala (ssh‑copy‑id admin@host). Usá -i para especificar la clave. |
| Falla de módulo debido a una versión antigua | Algunos módulos evolucionan rápidamente; el inventario puede estar en un entorno antiguo. | Ejecutá ansible‑playbook --version y mantenelo al día (pip install -U ansible). |
| Iteración de bucle infinita | loop con una lista malformada o un with_* mal escrito. | Validá el YAML del bucle con yamllint y ejecutá un test con un solo host. |
| Violación de SELinux | SELinux en modo enforce rechaza escrituras en /etc u otros directorios. | setenforce 0 temporalmente o agregá la política adecuada (semanage). |
| Conflictos de variable | Variables globales vs de grupo vs de host sobrescribiendo inesperadamente. | Listá todas las fuentes (ansible -i inventario.ini all -m setup) y verifica ansible‑debug. |
| Fallo de conexión SSH después de muchos éxitos | Tiempo de espera del cliente o límite de intentos fallidos. | Aumentá Timeout=30 en ansible.cfg o usá PersistentConnections=yes. |
Conclusión – Lo que ahora podés lograr
Has recorrido los conceptos fundamentales de Ansible: su modelo agentless, push‑based, su sintaxis YAML declarativa y los componentes esenciales para escribir y ejecutar playbooks.
Ahora podés:
- Configurar un inventario de hosts y grupos.
- Escribir un playbook con tareas,
include,loopy handlers. - Ejecutar y analizar la salida, corrigiendo errores comunes.
- Escalar a roles y colecciones para reutilizar código.
El siguiente paso natural es explorar roles (ansible‑galaxy init mi‑rol) y colecciones (ansible‑galaxy install comunidad.mi‑rol). Unirse a la comunidad de Ansible (documentación, Stack Overflow, Discord) acelerará tu aprendizaje.
Con una base sólida, ahora podés automatizar desde la implementación de un único servicio web hasta la orquestación de clústeres Kubernetes completos, todo sin instalar agentes en cada nodo—pureza push‑based a la que podés confiar.
Recordá: no te preocupes por dominar todo al principio. La práctica constante, probar con un escenario pequeño (como el saludo inicial) y mejorar gradualmente los playbooks te dará confianza. ¡Empezá a automatizar hoy mismo!
DUGLAS MORENO
Comentarios (0)
Sé el primero en comentar.