Ansible#
El nodo de control, es la máquina que tiene instalado Ansible. Desde ella administraremos el resto de nodos.
El Inventario es el archivo host para Ansible. En él se incluye información, estática o dinámica, de los nodos administrados y su información. Datos como IP o nombre.
Los Módulos, como en otros lenguajes, serían las librerías que ejecuta Ansible.
Los Playbooks se encargan de definir todas las tareas que debemos realizar sobre un conjunto de nodos administrador.
Los Roles es una agrupación de ficheros, tareas y plantillas
Una Task se podría definir como una acción a realizar.
Instalamos ansible:
apt install ansible -y
Añadimos al final del archivo :
root@compute-0-0:~# cat /etc/ansible/hosts
[server]
server0 ansible_host=172.16.0.10
server1 ansible_host=172.16.0.11
server2 ansible_host=172.16.0.12
[all:vars]
ansible python interpreter=/usr/bin/python3
Para consultar el inventario
root@compute-0-0:~# ansible-inventory --list -y
all:
children:
server:
hosts:
server0:
ansible python interpreter: /usr/bin/python3
ansible_host: 172.16.0.10
server1:
ansible python interpreter: /usr/bin/python3
ansible_host: 172.16.0.11
server2:
ansible python interpreter: /usr/bin/python3
ansible_host: 172.16.0.12
ungrouped: {}
Comprobamos la conexión
root@compute-0-0:~# ansible all -m ping -u root
server0 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Comandos ad hoc#
Cualquier comando que ejecute normalmente en un servidor remoto a través de SSH puede ejecutarse con Ansible en los servidores especificados en su archivo de inventario. Como ejemplo, puede verificar la utilización del disco en todos los servidores con lo siguiente:
root@compute-0-0:~# ansible all -a "df -h /dev/sda2" -u root
server2 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 79G 6,0G 69G 9% /
server1 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 79G 6,4G 68G 9% /
server0 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 79G 6,5G 68G 9% /
Podemos instalar en todos los nodos el comando tree:
ansible all -m apt -a "name=tree" -u root
Podemos comprobar que se ha instalado ejecutando:
ansible all -a "tree" -u root
Podemos especificar múltiples hosts separándolos con comas:
ansible server1,server2 -m ping -u root
Plabook#
Ansible ad hoc estan bien para algo rápido, sin embargo para organizar varios modulos se utilizan los playbook, por ejemplo:
ansible all -m apt -a "name=vim state=latest" -u root
Podríamos escribir lo mismo con un plabook:
root@compute-0-0:~# cat playbook.yml
- name: Playbook
hosts: all
become: yes
become_user: root
tasks:
- name: install vim latest
apt:
name: vim
state: latest
Comprobamos la lista de tareas
root@compute-0-0:~# ansible-playbook playbook.yml --list-tasks
playbook: playbook.yml
play #1 (all): Playbook TAGS: []
tasks:
install vim latest TAGS: []
Comprobamos la lista de maquinas dobre la que va actuar
root@compute-0-0:~# ansible-playbook playbook.yml --list-host
playbook: playbook.yml
play #1 (all): Playbook TAGS: []
pattern: ['all']
hosts (3):
server1
server0
server2
Ejecutamos con ansible el playbook
root@compute-0-0:~# ansible-playbook playbook.yml
PLAY [Playbook] *************************************************************************
TASK [Gathering Facts] ******************************************************************
ok: [server2]
ok: [server0]
ok: [server1]
TASK [install vim latest] ***************************************************************
ok: [server1]
ok: [server0]
ok: [server2]
PLAY RECA P******************************************************************************
server0 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0
server1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0
server2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0
Podríamos ejecutarlo solo en el server1
ansible-playbook -l server1 playbook.yml
Para cambiar el grupo de hosts por defecto.
ansible-playbook playbook.yml -i ansible_hosts
Roles#
Cuando trabajamos con Ansible, es habitual que los playbooks crezcan y se vuelvan difíciles de mantener. Para solucionar esto, Ansible introduce el concepto de roles, que permiten organizar la configuración en partes reutilizables y bien estructuradas.
Un rol es una forma de dividir un playbook en componentes más pequeños, cada uno con una función concreta (instalar paquetes, configurar servicios, gestionar usuarios, etc.).
Creación de un rol#
Podemos crear un rol con el siguiente comando:
ansible-galaxy init --offline nombre_rol
Este comando genera automáticamente una estructura de directorios estándar como la siguiente:
nombre_rol/
├── defaults
│ └── main.yml
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
Estructura del rol#
Cada uno de estos directorios tiene un propósito específico:
tasks/main.yml Define las tareas principales del rol. Es donde se realiza el trabajo (instalar paquetes, copiar archivos, ejecutar comandos, etc.).
handlers/main.yml Contiene acciones que se ejecutan solo cuando son notificadas por una tarea, como reiniciar un servicio.
vars/main.yml Define variables internas del rol con alta prioridad.
defaults/main.yml Define variables por defecto (baja prioridad), pensadas para ser modificadas por el usuario.
meta/main.yml Incluye información del rol, como dependencias de otros roles.
tests/ Permite probar el rol con un inventario y un playbook de ejemplo.
README.md Documentación del rol (uso, variables, descripción, etc.).
Consideraciones#
Las variables pueden definirse tanto en defaults como en vars, pero normalmente se elige uno según la prioridad que necesitemos.
La lógica principal del rol debe ir en tasks.
Es recomendable mantener los roles simples y reutilizables.
Uso de un rol en un playbook#
Un rol se utiliza dentro de un playbook de la siguiente forma:
- hosts: servidores
roles:
- nombre_rol
De esta forma, todas las tareas definidas en el rol se ejecutarán sobre los hosts indicados.
Ventajas de usar roles#
Facilitan la reutilización del código.
Mejoran la organización y legibilidad.
Permiten trabajar de forma modular.
Se pueden versionar con herramientas como git.
Se pueden compartir en plataformas como Ansible Galaxy.
Caso práctico: compute-0-3#
Partimos de un cluster formado por:
compute-0-0:
Tarjeta red modo "Red Nat 10.0.2.10/24 utiliza el puerto 2222 del anfitrión"
Tarjeta de red modo "Red interna" : 172.16.0.10/16
Servidor y cliente LDAD con usuarios (tunombre1, tunombre2, tunombre3 y grupo tupellido)
Utiliza NFS y autofs para exportar el home de los usuarios
compute-0-1
Tarjeta de red modo "Red interna" : 172.16.0.11/16 (tiene internet a través de compute-0-0)
Cliente LDAP y monta el home de los usuarios con autofs
compute-0-2
Tarjeta de red modo "Red interna" : 172.16.0.12/16 (tiene internet a través de compute-0-0)
Cliente LDAP y monta el home de los usuarios con autofs
Utiliza el gestor de tareas Slurm y modules environment
Vamos a crear el compute-0-3 utilizando ansible, para ello crea un rol llamado cluster-tunombre
ansible-galaxy init --offline cluster-tunombre
# tree cluster-tunombre/
cluster_tunombre/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
│ ├── auto.home.j2
| ├── auto.master.j2
│ ├── ldap.conf.j2
│ └── nsswitch.conf.j2
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
En el archivo cluster-tunombre/tasks/main.yml tenemos:
# tasks file for cluster-tunombre (Versión Moderna con libnss-ldapd)
- name: Instalar paquetes cliente LDAP modernos
apt:
name:
- libnss-ldap # Para NSS (getent passwd, grupos)
- libpam-ldap # Para PAM (autenticación)
- ldap-utils # Utilidades como ldapsearch
state: present
update_cache: yes
- name: Configurar nslcd.conf
template:
src: nslcd.conf.j2
dest: /etc/nslcd.conf
owner: root
group: root
mode: '0644'
backup: yes
- name: Reiniciar nslcd
systemd:
name: nslcd
state: restarted
- name: Reiniciar cache nscd
systemd:
name: nscd
state: restarted
- name: Configurar NSS para usar LDAP
template:
src: nsswitch.conf.j2
dest: /etc/nsswitch.conf
owner: root
group: root
mode: '0644'
backup: yes
- name: Inserta linea si no existe
lineinfile:
path: /etc/hosts
line: "172.16.0.10 ldap.tunombre.local"
state: present
- name: Instalar paquete nfs-common
apt:
name: nfs-common
state: present
update_cache: yes
- name: Instalar autofs
apt:
name: autofs
state: present
update_cache: yes
- name: Configurar auto.master
template:
src: auto.master.j2
dest: /etc/auto.master
owner: root
group: root
mode: '0644'
backup: yes
- name: Configurar auto.home
template:
src: auto.home.j2
dest: /etc/auto.home
owner: root
group: root
mode: '0644'
backup: yes
- name: Iniciar y habilitar autofs
systemd:
name: autofs
state: restarted
enabled: yes
Para aplicar el rol podemos usar aplicar_cliente_ldap.yml
- name: Configurar clientes LDAP
hosts: cliente3
become: yes
tasks:
- name: Importar todas las tareas del rol
import_role:
name: cluster-tunombre
con el siguiente inventario /etc/ansible/hosts
[servidor]
server0 ansible_host=172.16.0.10
[clientes]
cliente1 ansible_host=172.16.0.11
cliente2 ansible_host=172.16.0.12
cliente3 ansible_host=172.16.0.13
[all:vars]
Podemos ejecutar el playbook de la siguiente manera
# Verificar sintaxis
ansible-playbook --syntax-check aplicar_cliente_ldap.yml
# Ejecutar en modo prueba
ansible-playbook --limit cliente3 --check --diff aplicar_cliente_ldap.yml
# Podemos listar los hosts
ansible-playbook aplicar_cliente_ldap.yml --list-hosts
# Aplicar configuración solo sobre uno
ansible-playbook --limit cliente3 aplicar_cliente_ldap.yml
Docker + ansible#
Vamos a empezar creando la imagen desde el siguiente Dockerfile
FROM ubuntu:26.04
RUN apt update && apt install -y \
iproute2 \
iputils-ping \
net-tools \
openssh-server \
vim \
sudo \
&& mkdir -p /run/sshd \
&& echo 'root:alumno' | chpasswd \
&& sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
CMD service ssh start && sleep infinity
Hacemos las imágenes (si hace falta) y levantamos los contenedores en segundo plano docker compose up -d --build.
Utilizaremos el siguiente docker-compose.yml que define un mini clúster de 3 contenedores que simulan máquinas de un clúster
services:
compute-0-0:
build: .
container_name: compute-0-0
privileged: true
tty: true
hostname: compute-0-0
networks:
internal_net:
ipv4_address: 172.16.0.10
compute-0-1:
build: .
container_name: compute-0-1
privileged: true
tty: true
hostname: compute-0-1
networks:
internal_net:
ipv4_address: 172.16.0.11
compute-0-2:
build: .
container_name: compute-0-2
privileged: true
tty: true
hostname: compute-0-2
networks:
internal_net:
ipv4_address: 172.16.0.12
networks:
internal_net:
driver: bridge
ipam:
config:
- subnet: 172.16.0.0/16
gateway: 172.16.0.1
Podemos levantar el cluster docker compose up -d --build y conectarnos con docker exec -it --user root compute-0-0 bash
Instalamos ansible en compute-0-0 y exportamos la clave publica a todos los nodos.
Descarga el rol dgtrabada.ansible desde Ansible Galaxy, entramos en su directorio de pruebas y ejecutamos el playbook sobre el inventario cluster.yml para aplicar la automatización en los hosts definidos.
ansible-galaxy role install dgtrabada.ansible
cd ~/.ansible/roles/dgtrabada.ansible/tests
ansible-playbook -i inventory cluster.yml
Chequeamos el servidor ldapsearch -xLLL -b "dc=ldap,dc=tunombre,dc=local", y creamos la esctructura y los usuarios con el siguiente comando, cambia en test.ldif tunombre por tu nombre
ldapadd -x -D "cn=admin,dc=ldap,dc=tunombre,dc=local" -W -f test.ldif