Iniciar sesion en docenas de servidores uno por uno para instalar paquetes, editar archivos de configuracion y reiniciar servicios es tedioso y propenso a errores. Un paso olvidado en un servidor genera desviacion de configuracion, y de repente tus servidores “identicos” se comportan de manera diferente. Ansible resuelve esto permitiendote definir toda la configuracion de tu servidor en archivos YAML simples y aplicarla consistentemente en cientos de maquinas con un solo comando. Como Ansible no requiere agente y se comunica por SSH, no hay nada que instalar en tus servidores gestionados — si puedes conectarte por SSH a un host, Ansible puede gestionarlo.
Que Es Ansible?
Ansible es una herramienta de automatizacion de codigo abierto creada por Red Hat que gestiona la configuracion de servidores, el despliegue de aplicaciones y la orquestacion. A diferencia de herramientas que requieren un agente dedicado ejecutandose en cada servidor gestionado, Ansible usa SSH para conectarse a hosts remotos y ejecutar tareas.
Caracteristicas principales:
- Sin agente — No hay software que instalar o mantener en los nodos gestionados. Ansible usa conexiones SSH existentes
- Basado en push — El nodo de control envia configuraciones a los hosts gestionados bajo demanda, en lugar de que los hosts consulten un servidor central
- Idempotente — Ejecutar un playbook multiples veces produce el mismo resultado. Ansible verifica el estado actual antes de hacer cambios
- Basado en YAML — Los playbooks se escriben en YAML legible por humanos, haciendolos accesibles para cualquiera que pueda leer un archivo de configuracion
- Extensible — Miles de modulos integrados para gestionar paquetes, archivos, servicios, recursos cloud y mas
Como Funciona Ansible
Ansible sigue un modelo de ejecucion directo:
- Escribes un playbook (archivo YAML) que describe el estado deseado de tus servidores
- Defines un inventario listando que hosts gestionar
- Ejecutas
ansible-playbook, y Ansible se conecta a cada host por SSH - Ansible transfiere pequenos programas llamados modulos al host remoto
- Los modulos se ejecutan, aplican el estado deseado y reportan resultados
- Ansible elimina los modulos y muestra un resumen
Ningun demonio se ejecuta en los hosts gestionados. Ninguna base de datos rastrea el estado. Cada ejecucion de playbook es autocontenida.
Requisitos Previos
Antes de comenzar, asegurate de tener:
- Una maquina Ubuntu 22.04 o 24.04 como nodo de control (donde se ejecuta Ansible)
- Uno o mas servidores destino accesibles via SSH
- Autenticacion SSH basada en claves configurada entre el nodo de control y los hosts gestionados
- Python 3 instalado en todos los nodos gestionados (preinstalado en la mayoria de distribuciones Linux)
- Una cuenta de usuario con privilegios sudo en los hosts gestionados
Consejo: Si aun no has configurado la autenticacion SSH por claves, sigue nuestra guia de endurecimiento SSH para configurar acceso seguro basado en claves antes de continuar.
Instalar Ansible en Ubuntu
Usando el gestor de paquetes del sistema
sudo apt update
sudo apt install ansible -y
Usando pip (recomendado para la ultima version)
sudo apt install python3-pip python3-venv -y
python3 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
pip install ansible
Verificar la instalacion
ansible --version
Deberias ver una salida mostrando la version de Ansible, la ruta del archivo de configuracion y la version de Python:
ansible [core 2.16.x]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules']
python version = 3.12.x
Archivos de Inventario
El inventario le indica a Ansible que hosts gestionar. Puedes escribir inventarios en formato INI o YAML.
Formato INI (simple)
# inventory.ini
[webservers]
web1.example.com
web2.example.com
192.168.1.50
[dbservers]
db1.example.com ansible_port=2222
db2.example.com
[loadbalancers]
lb1.example.com
[production:children]
webservers
dbservers
loadbalancers
[all:vars]
ansible_user=deployer
ansible_ssh_private_key_file=~/.ssh/id_ed25519
Formato YAML
# inventory.yml
all:
vars:
ansible_user: deployer
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
192.168.1.50:
dbservers:
hosts:
db1.example.com:
ansible_port: 2222
db2.example.com:
loadbalancers:
hosts:
lb1.example.com:
production:
children:
webservers:
dbservers:
loadbalancers:
Probar conectividad
Verifica que Ansible puede alcanzar todos los hosts:
ansible all -m ping -i inventory.ini
Salida esperada para una conexion exitosa:
web1.example.com | SUCCESS => {
"changed": false,
"ping": "pong"
}
Tu Primer Playbook
Un playbook es un archivo YAML que contiene uno o mas “plays.” Cada play apunta a un grupo de hosts y define tareas a ejecutar.
Instalar y configurar nginx
# site.yml
---
- name: Configurar servidores web
hosts: webservers
become: true
tasks:
- name: Actualizar cache de apt
apt:
update_cache: true
cache_valid_time: 3600
- name: Instalar nginx
apt:
name: nginx
state: present
- name: Copiar configuracion de nginx
copy:
src: files/nginx.conf
dest: /etc/nginx/sites-available/default
owner: root
group: root
mode: "0644"
notify: Reiniciar nginx
- name: Habilitar sitio nginx
file:
src: /etc/nginx/sites-available/default
dest: /etc/nginx/sites-enabled/default
state: link
notify: Reiniciar nginx
- name: Asegurar que nginx esta ejecutandose y habilitado
service:
name: nginx
state: started
enabled: true
handlers:
- name: Reiniciar nginx
service:
name: nginx
state: restarted
Ejecutar el playbook:
ansible-playbook -i inventory.ini site.yml
Ejecutar con salida detallada para depuracion:
ansible-playbook -i inventory.ini site.yml -v
Realizar una ejecucion en seco para ver que cambiaria sin aplicar:
ansible-playbook -i inventory.ini site.yml --check --diff
Modulos de Ansible
Los modulos son las unidades de trabajo en Ansible. Cada modulo maneja una tarea especifica. Aqui estan los modulos mas utilizados:
Gestion de paquetes (apt)
- name: Instalar multiples paquetes
apt:
name:
- nginx
- curl
- ufw
- fail2ban
state: present
update_cache: true
- name: Eliminar un paquete
apt:
name: apache2
state: absent
purge: true
Gestion de servicios (service)
- name: Iniciar y habilitar un servicio
service:
name: nginx
state: started
enabled: true
- name: Reiniciar un servicio
service:
name: mysql
state: restarted
Gestion de archivos (copy, template, file)
- name: Copiar un archivo al host remoto
copy:
src: files/app.conf
dest: /etc/app/app.conf
owner: www-data
group: www-data
mode: "0644"
- name: Desplegar una plantilla con variables
template:
src: templates/vhost.conf.j2
dest: /etc/nginx/sites-available/myapp.conf
owner: root
group: root
mode: "0644"
- name: Crear un directorio
file:
path: /var/www/myapp
state: directory
owner: www-data
group: www-data
mode: "0755"
Ejecucion de comandos (command, shell)
- name: Ejecutar un comando
command: /usr/bin/myapp --init
args:
creates: /var/lib/myapp/initialized
- name: Ejecutar un comando shell con pipes
shell: cat /var/log/syslog | grep error | wc -l
register: error_count
changed_when: false
Gestion de usuarios (user)
- name: Crear un usuario de despliegue
user:
name: deployer
groups: sudo,www-data
shell: /bin/bash
create_home: true
state: present
- name: Agregar clave SSH autorizada
authorized_key:
user: deployer
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
state: present
Variables y Facts
Las variables hacen tus playbooks flexibles y reutilizables. Ansible soporta varias fuentes de variables.
Definir variables en playbooks
---
- name: Desplegar aplicacion
hosts: webservers
become: true
vars:
app_name: myapp
app_port: 8080
app_user: www-data
packages:
- nginx
- python3
- python3-pip
tasks:
- name: Instalar paquetes requeridos
apt:
name: "{{ packages }}"
state: present
- name: Crear directorio de la aplicacion
file:
path: "/var/www/{{ app_name }}"
state: directory
owner: "{{ app_user }}"
mode: "0755"
Variables de grupo y de host
Crea directorios para variables especificas por grupo y por host:
project/
inventory.ini
group_vars/
all.yml
webservers.yml
dbservers.yml
host_vars/
web1.example.com.yml
db1.example.com.yml
playbook.yml
# group_vars/webservers.yml
http_port: 80
https_port: 443
document_root: /var/www/html
max_workers: 4
# host_vars/web1.example.com.yml
server_name: web1.example.com
ssl_certificate: /etc/ssl/certs/web1.pem
Recopilar facts
Ansible recopila automaticamente facts del sistema (SO, direcciones IP, memoria, disco) de los hosts gestionados. Usalos en plantillas y condicionales:
- name: Mostrar informacion del sistema
debug:
msg: >
Hostname: {{ ansible_hostname }},
SO: {{ ansible_distribution }} {{ ansible_distribution_version }},
IP: {{ ansible_default_ipv4.address }},
RAM: {{ ansible_memtotal_mb }} MB
- name: Instalar paquetes segun el SO
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: Instalar paquetes en Red Hat
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
Handlers y Notificaciones
Los handlers son tareas especiales que se ejecutan solo cuando son notificados por otra tarea. Se activan al final de un play, despues de que todas las tareas regulares se han completado. Esto previene reinicios innecesarios de servicios.
---
- name: Configurar servidor web
hosts: webservers
become: true
tasks:
- name: Instalar nginx
apt:
name: nginx
state: present
- name: Desplegar configuracion principal de nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- Validar configuracion de nginx
- Reiniciar nginx
- name: Desplegar virtual host
template:
src: templates/vhost.conf.j2
dest: /etc/nginx/sites-available/{{ domain }}.conf
notify:
- Validar configuracion de nginx
- Reiniciar nginx
- name: Habilitar virtual host
file:
src: /etc/nginx/sites-available/{{ domain }}.conf
dest: /etc/nginx/sites-enabled/{{ domain }}.conf
state: link
notify:
- Validar configuracion de nginx
- Reiniciar nginx
handlers:
- name: Validar configuracion de nginx
command: nginx -t
listen: "Validar configuracion de nginx"
- name: Reiniciar nginx
service:
name: nginx
state: restarted
listen: "Reiniciar nginx"
Aunque tres tareas notifican a los handlers, Reiniciar nginx se ejecuta solo una vez al final del play.
Roles y Estructura de Directorios
Los roles organizan tus playbooks en componentes reutilizables y compartibles. Cada rol encapsula tareas, handlers, variables, plantillas y archivos para una funcion especifica.
Crear un rol
ansible-galaxy init roles/webserver
Esto genera la siguiente estructura:
roles/
webserver/
tasks/
main.yml
handlers/
main.yml
templates/
files/
vars/
main.yml
defaults/
main.yml
meta/
main.yml
Tareas del rol
# roles/webserver/tasks/main.yml
---
- name: Instalar nginx
apt:
name: nginx
state: present
update_cache: true
- name: Desplegar configuracion de nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reiniciar nginx
- name: Desplegar virtual host
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ domain }}.conf"
notify: Reiniciar nginx
- name: Habilitar virtual host
file:
src: "/etc/nginx/sites-available/{{ domain }}.conf"
dest: "/etc/nginx/sites-enabled/{{ domain }}.conf"
state: link
notify: Reiniciar nginx
- name: Eliminar virtual host por defecto
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Reiniciar nginx
- name: Asegurar que nginx esta ejecutandose
service:
name: nginx
state: started
enabled: true
Handlers del rol
# roles/webserver/handlers/main.yml
---
- name: Reiniciar nginx
service:
name: nginx
state: restarted
Valores por defecto del rol
# roles/webserver/defaults/main.yml
---
domain: example.com
http_port: 80
https_port: 443
document_root: /var/www/html
worker_processes: auto
worker_connections: 1024
Usar roles en un playbook
# site.yml
---
- name: Configurar infraestructura
hosts: webservers
become: true
roles:
- role: webserver
vars:
domain: knowledgexchange.xyz
- role: security
- role: monitoring
Ejemplos Practicos
Despliegue de stack LAMP
# lamp.yml
---
- name: Desplegar stack LAMP
hosts: webservers
become: true
vars:
mysql_root_password: "{{ vault_mysql_root_password }}"
php_version: "8.3"
tasks:
- name: Instalar Apache y PHP
apt:
name:
- apache2
- libapache2-mod-php{{ php_version }}
- php{{ php_version }}
- php{{ php_version }}-mysql
- php{{ php_version }}-curl
- php{{ php_version }}-xml
- php{{ php_version }}-mbstring
state: present
update_cache: true
- name: Instalar servidor MySQL
apt:
name: mysql-server
state: present
- name: Iniciar y habilitar MySQL
service:
name: mysql
state: started
enabled: true
- name: Iniciar y habilitar Apache
service:
name: apache2
state: started
enabled: true
- name: Desplegar pagina PHP info para pruebas
copy:
content: "<?php phpinfo(); ?>"
dest: /var/www/html/info.php
owner: www-data
group: www-data
mode: "0644"
Gestion de usuarios en todos los servidores
# users.yml
---
- name: Gestionar cuentas de usuario
hosts: all
become: true
vars:
admin_users:
- name: jcarlos
key: "ssh-ed25519 AAAAC3Nza... jcarlos@workstation"
groups: sudo,adm
- name: deployer
key: "ssh-ed25519 AAAAC3Nza... deployer@ci"
groups: www-data
removed_users:
- oldadmin
- tempuser
tasks:
- name: Crear usuarios administradores
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
shell: /bin/bash
create_home: true
state: present
loop: "{{ admin_users }}"
- name: Agregar claves SSH para usuarios administradores
authorized_key:
user: "{{ item.name }}"
key: "{{ item.key }}"
exclusive: true
loop: "{{ admin_users }}"
- name: Eliminar usuarios antiguos
user:
name: "{{ item }}"
state: absent
remove: true
loop: "{{ removed_users }}"
Endurecimiento de seguridad
# harden.yml
---
- name: Endurecimiento de seguridad
hosts: all
become: true
tasks:
- name: Instalar paquetes de seguridad
apt:
name:
- ufw
- fail2ban
- unattended-upgrades
state: present
update_cache: true
- name: Configurar valores por defecto de UFW
ufw:
direction: incoming
policy: deny
- name: Permitir SSH a traves de UFW
ufw:
rule: allow
port: "2222"
proto: tcp
- name: Permitir HTTP y HTTPS
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "80"
- "443"
- name: Habilitar UFW
ufw:
state: enabled
- name: Desplegar configuracion de fail2ban
copy:
src: files/jail.local
dest: /etc/fail2ban/jail.local
notify: Reiniciar fail2ban
- name: Habilitar actualizaciones de seguridad automaticas
copy:
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
dest: /etc/apt/apt.conf.d/20auto-upgrades
- name: Deshabilitar inicio de sesion SSH como root
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
notify: Reiniciar sshd
handlers:
- name: Reiniciar fail2ban
service:
name: fail2ban
state: restarted
- name: Reiniciar sshd
service:
name: sshd
state: restarted
Ansible Vault para Secretos
Nunca almacenes contrasenas o claves API en texto plano. Ansible Vault cifra datos sensibles:
# Crear un archivo de variables cifrado
ansible-vault create group_vars/all/vault.yml
# Editar un archivo cifrado
ansible-vault edit group_vars/all/vault.yml
# Cifrar un archivo existente
ansible-vault encrypt secrets.yml
# Ejecutar un playbook con vault
ansible-playbook site.yml --ask-vault-pass
# Usar un archivo de contrasena en su lugar
ansible-playbook site.yml --vault-password-file ~/.vault_pass
Dentro del archivo vault, prefija las variables sensibles con vault_:
# group_vars/all/vault.yml (cifrado)
vault_mysql_root_password: "s3cur3P@ssw0rd"
vault_api_key: "abc123def456"
vault_ssl_private_key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
Referencia las variables vault en tu archivo de variables regular:
# group_vars/all/vars.yml
mysql_root_password: "{{ vault_mysql_root_password }}"
api_key: "{{ vault_api_key }}"
Solucion de Problemas
Fallos de conexion SSH
# Probar conectividad SSH manualmente
ssh -i ~/.ssh/id_ed25519 deployer@web1.example.com
# Ejecutar con modo detallado para ver detalles SSH
ansible all -m ping -i inventory.ini -vvvv
# Verificar si Python esta disponible en el host remoto
ansible all -m raw -a "python3 --version" -i inventory.ini
Errores de permiso denegado
# Asegurar que become (sudo) esta configurado
ansible-playbook site.yml -i inventory.ini --become --ask-become-pass
# Verificar configuracion de sudo en el host remoto
ssh deployer@web1.example.com "sudo -l"
Errores de modulo no encontrado
# Listar todos los modulos disponibles
ansible-doc -l | grep apt
# Ver documentacion de un modulo especifico
ansible-doc apt
# Verificar version de Ansible para compatibilidad de modulos
ansible --version
Errores de sintaxis en playbooks
# Validar sintaxis del playbook sin ejecutar
ansible-playbook site.yml --syntax-check
# Lint del playbook para mejores practicas
pip install ansible-lint
ansible-lint site.yml
Depurar salida de tareas
- name: Ejecutar comando y capturar salida
command: systemctl status nginx
register: nginx_status
ignore_errors: true
- name: Mostrar la salida
debug:
var: nginx_status.stdout_lines
Referencia de Comandos Esenciales
| Comando | Descripcion |
|---|---|
ansible --version | Mostrar version y configuracion de Ansible |
ansible all -m ping -i inventory.ini | Probar conectividad a todos los hosts |
ansible all -m setup -i inventory.ini | Recopilar y mostrar facts de los hosts |
ansible webservers -m apt -a "name=nginx state=present" -i inventory.ini --become | Instalar nginx en webservers via comando ad-hoc |
ansible-playbook site.yml -i inventory.ini | Ejecutar un playbook |
ansible-playbook site.yml --check --diff | Ejecucion en seco con salida diff |
ansible-playbook site.yml --limit web1.example.com | Ejecutar playbook en un solo host |
ansible-playbook site.yml --tags "nginx,security" | Ejecutar solo tareas con etiquetas especificas |
ansible-playbook site.yml --start-at-task "Install nginx" | Reanudar playbook desde una tarea especifica |
ansible-vault create secrets.yml | Crear un nuevo archivo cifrado |
ansible-vault edit secrets.yml | Editar un archivo cifrado |
ansible-galaxy init roles/myrole | Crear estructura de directorio de un nuevo rol |
ansible-galaxy install geerlingguy.nginx | Instalar un rol desde Ansible Galaxy |
ansible-inventory -i inventory.ini --graph | Mostrar grafo de hosts del inventario |
ansible-doc apt | Ver documentacion del modulo |
Resumen
Ansible proporciona un camino poderoso y accesible hacia la automatizacion de infraestructura. Su arquitectura sin agente significa que puedes comenzar a automatizar inmediatamente — si puedes conectarte por SSH a tus servidores, puedes gestionarlos con Ansible. Comienza con comandos ad-hoc simples, avanza a playbooks para tareas repetibles y organiza configuraciones complejas en roles. La combinacion de legibilidad YAML, ejecucion idempotente y miles de modulos integrados hace de Ansible la herramienta preferida para la gestion de configuracion en entornos Linux y Windows.
Para aprovisionar la infraestructura en si — crear servidores cloud, redes y registros DNS antes de configurarlos — explora nuestra guia de infraestructura como codigo con Terraform. Para asegurar que los servidores que gestiona Ansible esten seguros desde el inicio, sigue nuestra guia de endurecimiento SSH para proteger el acceso remoto con autenticacion basada en claves, fail2ban y reglas de firewall.