Sirin: una herramienta para lanzar playbooks de Ansible bajo demanda.

per Victor Carceler darrera modificació 2021-01-08T09:15:13+01:00

alkonost-sirin.jpg

Ansible_logo.svg.pngEn el centro llevamos años utilizando Ansible para gestionar los ordenadores del instituto y sin esta herramienta difícilmente los ordenadores se encontrarían en un estado adecuado para desempeñar su función.

Los playbooks que utilizamos este curso se pueden consultar en:

Estos playbooks definen toda la configuración que hay que realizar en los equipos a partir de una instalación desatendida. Así se encargan de gestionar el software (ya sea en formato .deb, flatpak o snap), configurar las herramientas y los servicios que se van a utilizar o declarar los usuarios de la máquina.

En un ordenador con disco SSD la ejecución del playbook justo después de la instalación puede tardar unos 30 minutos y en alguno de los pocos equipos que quedan con disco mecánico la tarea puede alargarse durante un par de horas.

Pero Ansible no solo debe ejecutar un playbook cuando se ha reinstalado un equipo. La configuración de los equipos no es inamovible y con frecuencia se decide instalar alguna nueva herramienta, cambiar la configuración de algún servicio o modificar, por ejemplo, las cuentas de usuario. Todas estas tareas necesitan una nueva ejecución del playbook actualizado para aplicar los cambios.

Además no siempre que se lanza un playbook se encuentran encendidos todos los ordenadores a los que afecta. Así que conviene lanzarlo de nuevo cada cierto tiempo para que los cambios se apliquen en los ordenadores cuando los encuentre conectados. Por todo esto hasta ahora se han utilizado un par de tareas de crontab para para lanzar los playbooks de Ansible dos veces al día: a las 10h y a las 16h.

Como las tareas de los módulos de Ansible son idempotentes no provocan cambios si no son necesarios. Ejecutar un playbook en un ordenador que ya está en el estado deseado no provoca ningún cambio y lleva poco más de 1 minuto.

Problema con la ejecución periódica de playbooks.

Sin embargo la ejecución periódica de playbooks también tiene sus limitaciones:

  • No siempre están encendidos (o con red) todos los equipos a los que va dirigido el playbook. Esto no es un problema demasiado grave pues Ansible descarta a todos aquellos equipos con los que no puede conectar y continúa con el resto. Pero en herramientas como ARA Records Ansible aparecerán todos los hosts incluidos aquellos con los que no se ha podido conectar.
  • Hay equipos esquivos como los portátiles de los carros que se utilizan en el aula. Estos portátiles se sacan del carro y se encienden dependiendo de la materia que se imparte en clase. Evidentemente convendría lanzar el playbook cuando estén encendidos, el problema es que cada grupo tiene su propio horario y además este horario puede variar a lo largo del curso.

Por último, ejecutar playbooks de manera continua para intentar pillar a todos los equipos en algún momento tampoco parece una opción muy eficiente pues consume recursos de manera indiscriminada. Aunque para un equipo que ya está en el estado deseado ejecutar un playbook no supone mucho trabajo sí que supone algo de trabajo y para el equipo en el que está instalado Ansible y ARA Records Ansible supone un trabajo inútil.

La solución pasa por ejecutar los playbooks con cierta frecuencia —por ejemplo una vez al día para propagar las nuevas configuraciones con cierta agilidad— pero no en ejecutarlos de manera excesiva. Y además hay que tener en cuenta que no todos los ordenadores estarán encendidos a la vez, especialmente los portátiles tienden a tener un horario de uso irregular.

Al pensar en todo esto he valorado utilizar una herramienta como ansible-pull que invierte la arquitectura típica de Ansible (push). Con esta herramienta cada equipo descarga de un repositorio git los playbooks y después los ejecuta. Quizás la herramienta podría haber sido de utilidad en nuestro caso pero es una opción que no he llegado a valorar demasiado porque me encuentro cómodo con el modo de funcionamiento habitual de Ansible en el que un ordenador conecta con el parque de equipos que gestiona y ejecuta en ellos los playbooks. Además en ese equipo en nuestro caso se está ejecutando ARA Records Ansible para poder revisar la actividad de todos los equipos de manera centralizada. Y no quiero renunciar a esto.

Сирин / Sirin

Pensando en todo esto la idea que ha ido germinando en mi cabeza es:

  • Conviene que los equipos puedan solicitar la ejecución de un playbook cuando arrancan.
  • Alguien debe tomar nota de estas notificaciones o peticiones de los equipos.
  • A partir de las peticiones (y en función de cuándo se lanzó en ese equipo por última vez un playbook) se pueden lanzar playbooks bajo demanda.

Y esto es básicamente lo que hace Сирин / Sirin: https://github.com/vcarceler/sirin

Sirin es una pequeña aplicación web implementada con Django que:

  1. Recibe las peticiones de los clientes cada vez que arrancan.
  2. Proporciona una lista de equipos para utilizar con el parámetro --limit del comando ansible-playbook.

De esta manera se pueden lanzar playbookscuando hay ordenadores encendidos para ellos y sin ejecutarlos de manera innecesaria, pues si Sirin recibe una nueva petición de un equipo antes de que transcurra el periodo definido (por defecto EXCLUSION_TIME son 24h) desde que se tramitó la última simplemente ignora la petición.

Además un timer de systemd o una tarea crontab pueden ir consultando a Sirin cada 15 o 20 minutos para lanzar un playbook que incluya a todos los equipos con solicitudes pendientes. Y todo esto se puede hacer con gran sencillez para cada grupo de ordenadores que tiene su propio playbook.

Peticiones de los equipos

En los ordenadores del centro Vasilisa se encarga de realizar una petición web a Sirin 1 minuto después de arrancar.

La petición se puede realizar con el comando wget. Por ejemplo los ordenadores del aula Torvalds realizan la siguiente petición:

wget http://192.168.10.18:8000/launcher/?label=torvalds-u1804.yml

Sirin recibe:

  • La IP del equipo que hace la solicitud
  • El parámetro label

La lógica de Sirin es muy sencilla:

  1. Si no existe en la BBDD una solicitud previa de este equipo se registra como pendiente de procesar.
  2. Si existe en la BBDD una solicitud previa de este equipo únicamente se actualiza la petición (como pendiende de procesar) si ha transcurrido EXCLUSION_TIME desde la solicitud anterior.

Esto permite registrar la solicitud de un equipo tan pronto como arranca pero ignorarla si recientemente ya se había recibido la petición de este equipo.

El parámetro label facilita agrupar los equipos por playbook. Así a la hora de obtener la lista de equipos con peticiones pendientes de procesar se puede filtrar utilizando este parámetro.

Consulta de las peticiones pendientes

Sirin considera pendientes todas aquellas peticiones que se han registrado en su BBDD y para las que todavía no ha devuelto las direcciones de sus hosts para que sean procesados.

Se puede consultar la lista de peticiones pendientes utilizando un navegador web en la dirección http://<IP>:<PORT>/launcher/listpendingrequests.

Por ejemplo:

Listado de peticiones pendientes de Sirin.

Y también desde la línea de comandos ejecutando: python manage.py listpendingrequests

Por ejemplo:

python manage.py listpendingrequests
ID       LABEL                            ADDRESS                          DATETIME                        
9        label2                           192.168.1.1                      2020-12-01 10:42:26             
10       label2                           192.168.1.2                      2020-12-02 00:00:00             
11       label1                           10.118.10.23                     2020-12-27 20:43:46             
23       label1                           192.168.1.144                    2020-12-30 09:31:23

Desde la línea de comandos también se puede obtener el número de peticiones pendientes para una determinada etiqueta ejecutando: python manage.py getnumberofrequests <etiqueta>

Por ejemplo:

python manage.py getnumberofrequests label1
2

De esta manera es posible comprobar si para una etiqueta hay peticiones pendientes y si las hay (o si superan el umbral establecido) lanzar el playbook.

Es importante notar que todas estas operaciones de consulta no modifican la BBDD de Sirin.

Obtención del listado de hosts para el parámetro --limit

Al lanzar un playbook con el comando ansible-playbook es posible utilizar el parámetro --limit para limitar los equipos afectados.

El comando python manage.py gethosts <label>permite obtener la lista de equipos con peticiones pendientes para la etiqueta indicada.

Una posible salida del comando es:

python manage.py gethosts label1
10.118.10.23,192.168.1.144,

Es importante notar que cuando Sirin devuelve la IP de una petición pasa a considerarla procesada.

Shell script para procesar las peticiones a Sirin

De tanto en tanto será menester consultar si hay peticiones pendientes de procesar en Sirin y, si es el caso, procesarlas con un playbook de Ansible.

En el instituto y de manera provisional se utiliza el script procesa_peticiones:

#!/bin/bash

#
# Procesa las peticiones pendientes para cada una de las etiquetas.
#

# venv's python
PYTHON="/home/vcarceler/sirin/venv/bin/python"
# Sirin's manage.py
MANAGE="/home/vcarceler/sirin/sirin/manage.py"

# ARA
playbooks="/home/vcarceler/playbooks-ubuntu2004"
export ANSIBLE_CALLBACK_PLUGINS="$(python3 -m ara.setup.callback_plugins)"

for x in esobatx-u2004.yml esobatx_dept-u2004.yml esobatx_wifi-u2004.yml
do
	# Comprobamos si hay peticiones pendientes
	n=`$PYTHON $MANAGE getnumberofrequests $x`
	echo "Para la etiqueta $x hay $n peticiones pendientes."
	if [ $n -gt 0 ]
	then
		hosts=`$PYTHON $MANAGE gethosts $x`
		echo "Lanzo ansible-playbook -i $playbooks/hosts $playbooks/$x --limit=$hosts"
		ansible-playbook -i $playbooks/hosts $playbooks/$x --limit=$hosts &
	else
		echo No hay nada que hacer
	fi
done

echo "Esperando que acaben las tareas ansible-playbook..."
wait
echo "Todos los playbooks de ansible se han completado."

De momento únicamente se ha implementado el nuevo sistema para los equipos de la ESO/BATX pero pronto se hará para el resto de equipos.

Para cada etiqueta se consulta a Sirin por el número de peticiones pendientes y si es superior a 0 se lanza ansible-playbook con la lista de hosts proporcionada por Sirin.

Los comandos ansible-playbook se lanzan como subprocesos (en paralelo) y finalmente se espera con wait a que todos concluyan. La actividad queda registrada en ARA Records Ansible y en el fichero de registro.

Un timer para lanzar procesa_peticiones

Para activar el shell script que procesa las peticiones he decidido utilizar un timer de systemd con su correspondiente servicio para que cada 15 minutos se compruebe si hay peticiones pendientes.

Fichero: /etc/systemd/system/sirin-ansible.timer

[Unit]
Description=Procesa con ansible-playbook las peticiones pendientes

[Timer]
OnCalendar=*:0/15

[Install]
WantedBy=timers.target

Fichero: /etc/systemd/system/sirin-ansible.service

[Unit]
Description=Procesa con ansible-playbook las peticiones pendientes

[Service]
User=vcarceler
Group=vcarceler
ExecStart=/home/vcarceler/playbooks-ubuntu2004/tools/sirin/procesa_peticiones

Todos los playbooks sin incidencias en ARA Records Ansible

Hasta ahora al lanzar un playbook de Ansible se intentaba conectar con todos los hosts definidos en el playbook y con frecuencia, por no decir siempre, había algún host que no contestaba lo que marcaba el estado del playbook en ARA Records Ansible como un error (por contener hosts con los que no se había podido conectar).

Pero desde que se utiliza Sirin para lanzar los playbooks únicamente se intenta conectar con aquellos hosts que han dado señales de vida, así que en la interfaz de ARA Records Ansible todos los playbooks aparecen con estado OK.

ARA Records Ansible con estado Ok.

De esta manera resultará mucho más sencillo comprobar de un vistazo que todo ha ido хорошо y prestar atención a los playbooks en los que realmente algún host ha encontrado algún problema.

Aún seguirá siendo posible que un host se apague, o desconecte, después de haber hecho la petición a Sirin y antes de que se lance el playbook o bien durante su ejecución. En este caso sí que aparecerá el playbook con el marcador de estado con problemas, pero supongo que esta no será una situación muy frecuente.

Más información: