Ansible desde cero: Agentless, Push‑Based y YAML Declarativo

DUGLAS MORENODUGLAS MORENO 👁 2

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 --version

Despué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.20

Guardá 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 webservers

Salida esperada:

HOSTS:
192.168.1.10
192.168.1.11

Tambié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 con name: (descriptivo) y un módulo (debug:).

Ejecución y salida esperada

ansible‑playbook -i inventario.ini primer_playbook.yml

Salida 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=13ms

El 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 ping

Salida esperada:

192.168.1.10 : pong
192.168.1.11 : pong
192.168.1.20 : pong

El 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.yml

Loops – 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
    - carla

Salida 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.cfg o ansible.cfg en el current directory.
  • Variables de inventarioinventario.ini con sintaxis clave=valor.
  • Variables de hosthosts.ini con cada host como sección.
  • Variables de grupogrupo.yml bajo cada grupo.
  • Variables de papel – archivos vars/*.yml dentro de una colección.

Ejemplo de variable de grupo (grupo.yml):

grupo_databases:
  db_user: admin
  db_port: 5432

Luego, 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 -vv

La salida incluye logs de conexión SSH, comandos remotos y el resultado final.

Prácticas recomendadas

  1. Usá roles para organizar códigos reutilizables. Un rol es un directorio con tasks/, vars/, defaults/, handlers/, etc. ansible‑galaxy init crea uno rápidamente.
  2. Versioná tus playbooks y plantillas. El control de versiones ayuda a rastrear cambios e invertir si algo falla.
  3. Separá entornos (desarrollo, testing, producción) con inventarios y grupos diferentes.
  4. 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.
  5. Usá módulos de texto en lugar de shell/command cuando sea posible. Los módulos ofrecen serialización segura, manejo claro de errores y salida estructurada.
  6. 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, loop y 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!

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario