Controlando el descontrol de la automatización

Controlando el descontrol de la automatización

Controlando el descontrol de la automatización

Tiempo de lectura: 6

AWX es un componente esencial en el flujo de automatización de una organización de TI sin embargo hay veces que un job se queda en algunas de las tareas por un tiempo indefinido sin ningún mensaje por la consola de salida que nos indique que está ocurriendo. En esta entrada daremos algunas ideas para poder solucionar esta situación. En concreto veremos los siguientes puntos:

  • Cómo controlar que ciertos jobs no se queden en un cierto estado de manera indefinida
  • Típicos casos de uso.
  • La importancia del logging en nuestro servicio

AWX en una cáscara de nuez

Para muchas organizaciones usar Ansible desde la línea de comandos no es una solución viable entre otras razones porqué

  • Existe la necesidad de obtener un reporte detallado de los trabajos realizados y sus correspondientes fallos, especialmente si hay procesos de auditoría. 
  • Una organización puede estar formada por diferentes equipos con diferentes permisos y necesidades de gestión de “playbooks”, inventarios y credenciales. 
  • Una historia visual completa de las ejecuciones actuales o históricas de las ejecuciones realizadas y el estado del servidor ayuda a identificar potenciales problemas antes que tengan un impacto en el servicio final. 
  • Posibilidad de planificar “playbooks” para ayudar a mantener la infraestructura en un estado conocido.

AWX provee un mecanismo para que los equipos puedan usar Ansible desde una interfaz gráfica.  


“AWX es un proyecto de código abierto upstream de Ansible Tower y ayuda a los equipos a administrar implementaciones complejas de varios niveles al agregar control, conocimiento y delegación a los entornos que usan Ansible.”

Posibles problemas en el uso de AWX

Existen dos casos típicos que pueden llevar a tener jobs en un cierto estado durante más tiempo del deseado:

  • Operadores que han ejecutado un job y lo han dejado ejecutándose sin que realmente se controle qué acciones está realizando. 
  • Ejecución de acciones de remediación de manera desatendida. 

El primer caso es una consecuencia natural del uso de la herramienta por parte de multitud de personas dentro de una organización IT. El segundo caso está más relacionado con una parte del flujo de monitorización que utilizamos. 

Solucionando problemas con AWX

AWX ofrece una API de su servicio desde el que se pueden realizar diferentes acciones entre ellas ejecutar playbooks. En este caso el problema que pretendemos resolver es cómo podemos asegurar que un servicio se soluciona de manera desatendida en caso que haya un problema. En nuestro caso usamos Zabbix cómo pieza central para gestionar incidencias y alertas dentro de todo el flujo de TI. 

“Zabbix es una solución open source que ofrece una solución de monitorización que cubre todas las necesidades de una organización TI cómo son: SSO, monitorización distribuida, sin límite en términos de retención de datos, seguridad y mucho más”

Zabbix entre otras funcionalidades tiene la capacidad de ejecutar acciones basadas en eventos tales cómo caída de un servicio

En este caso, tenemos un cierto servicio en el que se han levantado incidencias ya sea porqué algún puerto no está levantado, no exista un determinado fichero en una ruta o por una determinada entrada en un log por citar algunos ejemplos.  

En caso de que alguna de estas alertas se abran se puede configurar Zabbix para que además de notificar a los responsables del servicio ejecute una serie de acciones con la finalidad de resolver dicho incidente. Una acción en Zabbix puede ser:

  • Mandar un mensaje a un único usuario a un grupo y por uno o varios medios. Además los mensajes pueden ser customizados y pueden tener condiciones
  • Ejecutar comandos remotos: ejecución de un script en el servidor o en el agente, protocolos IPMI, SSH, telnet o un tener un script global.

Es decir podemos configurar esta acción para que llame a AWX y así poder ejecutar un job con una serie de tareas definidas en un “playbook” o “role” en el servicio afectado. En el caso de que vaya bien el servicio se recuperara y la alerta dejará de estar activa en Zabbix. Hay casos que se ejecutan acciones de manera desatendida pero los jobs por alguna razón se quedan “colgados” o llevan más tiempo del esperado ejecutándose.

Controlando el descontrol de la automatización

Por defecto Ansible ejecuta las tareas de manera síncrona, manteniendo la conexión en el nodo remoto hasta que se han realizado las tareas. Esto quiere decir, que cada tarea bloquea la siguiente por defecto y al ser una ejecución secuencial las tareas subsiguientes no se podrán ejecutar si hay una tarea anterior que no se ha ejecutado. Por ejemplo, una tarea puede llevar más tiempo en completarse que el tiempo de la sesión SSH causando un timeout. Ansible tiene mecanismos para poder controlar este comportamiento mediante parámetros cómo async y poll, parámetros de configuración de la sesión SSH etc. Observar que este caso el encargado de paralelizar la ejecución de jobs es AWX. Las típicas tareas en las que Ansible se puede quedar colgado son (no solamente)

  • Problemas de conexión ya sea por que no se tiene acceso a la red dónde está la máquina, las claves SSH no están correctamente configuradas etc.
  • Problemas con el módulo de setup o gathering facts: El módulo de setup puede en algunas ocasiones quedarse colgado cuando recoge información de hardware (información relativa a discos con altos flujos de entrada y salida, puntos de montaje erróneos, etc.)

Hasta dónde hemos llegado sabemos que podemos tener jobs en AWX que se han quedado en un estado determinado sin tener respuesta de este. 

Para poder controlar esto hay varias opciones más o menos automáticas y/o sofisticadas. Pero una primera medida es usar la API de AWX para obtener los “jobs” que se han quedado en un cierto estado durante un cierto tiempo. En todos los despliegues que se realizan de AWX se incluye un componente ad-hoc llamado AWX Cleaner que funciona como un “watchdog”. Y a alto nivel la funcionalidad se podría representar de la siguiente manera:

“AWX tiene definidos una serie de estados en los que se puede encontrar un job como por ejemplo running, waiting, pending o new se puede consultar más información sobre la API y los diferentes métodos en [1] 

  1. Obtenemos los jobs
$ curl -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/ | jq '.results[]'

2. Filtramos los jobs obtenidos por el tiempo que llevan ejecutándose, el estado y si lo especificamos el nombre.

$ curl -s -u admin:admin http://<awx-host>/api/v2/jobs/ | jq '(.results[]| select(.status | test("successful"))|select(.elapsed>100))|.id'
...
34343
...

3. Hacemos una llamada a la API preguntando si el job con cierto ID se puede cancelar.

$ curl -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/34343/cancel/ | jq '.'

True

4. Almacenamos en una lista los jobs que podemos cancelar

5. Cancelamos los jobs y sacamos información del evento para poder hacer un análisis posterior

$ curl -X POST -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/34343/cancel/ | jq '.'

Podemos sacar una lista de los jobs filtrados por status y tiempo transcurrido usando el siguiente comando

$ curl -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/ |\ 
jq -r '(["ID","Elapsed", "Limit", "     Name"]),\
(.results[]| select(.status | test("runnning"))|select(.elapsed>100)|[.id,.elapsed, .limit, .name])|@tsv'

ID      Elapsed         Limit        Name
34343   181.185      hostX      Reinicio servicios X
...
34367   558.483      hostX       Reinicio servicios X

En esta línea de código bash lo primero que hacemos es obtener el json con todos los jobs, le indicamos a jq que vamos a construir una tabla con los campos ID, Elapsed, Limit y Name para finalmente obtener dichos valores aplicando unos filtros en los resultados devueltos (.results[]) y generar una tabla separada por valores (@tsv). A veces puede resultar de utilidad explorar el “esquema” del json con las siguientes llamadas. Pueden probar a sustituir la cadena “running” por otro estado cómo “succesful”. Para ver otro ejemplo de uso de jq a un nivel intermedio puede consultar [2]

$ curl -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/ | jq '.|keys'
[
  "count",
  "next",
  "previous",
  "results"
]

$ curl -s -u <user>:<pass> http://<awx-host>/api/v2/jobs/ | jq '.results[]|keys'

[
  "allow_simultaneous",
  "artifacts",
  "ask_credential_on_launch",
...
]

No se ha incluido el código Python de esta sección puesto que sería muy largo y no es muy difícil generar un código funcional básico dejando a parte funcionalidades extra añadidas ad hoc a las necesidades de cliente. 

La importancia del logging

Añadir telemetría (logging, métricas y trazas) a nuestros servicios es fundamental para poder correlacionar la salud de los componentes a nivel individual con la salud del servicio a nivel de negocio. Lejos han quedado aquellos tiempos en los que encontrar los logs de una aplicación era toda una odisea y en el peor de los casos había que pedirlos a un determinado equipo. Hoy día es muy fácil generar trazas de logs estructurados para posteriormente generar agregados en otras herramientas como por ejemplo Elasticsearch. En este caso la configuración utilizada de logging sería la siguiente

#!/usr/bin/env python

import sys
import argparse
from ruamel.yaml import YAML
import logging.config
from pythonjsonlogger import jsonlogger

FORMAT = "[%(asctime)s %(levelname)-5s|%(name)s|%(filename)20s:%(lineno)-3s|%(funcName)20s()] %(message)s"
logger = logging.getLogger(__name__)

En el siguiente enlace podemos encontrar un esqueleto funcional para generar una interfaz de linea de comandos o en su nomenclatura anglosajona Command Line Interpreter (CLI) para nuestra aplicación. https://gist.github.com/oscaromeu/aeb7424cd4b65d182bbaae38b747afdd

https://gist.github.com/oscaromeu/310780a26c76d45896b2b9c76053f3cb

Para ejecutar la aplicación con su correspondiente configuración de logging de la siguiente manera

python3 cli_demo.py logging.yaml -vvv
{"asctime": "2022-01-11 11:32:23", "msecs": 514.5304203033447, "levelname": "INFO", "name": "__main__", "process": 368056, "processName": "MainProcess", "filename": "cli_demo.py", "lineno": 22, "message": "info-level log message"}
{"asctime": "2022-01-11 11:32:23", "msecs": 514.6398544311523, "levelname": "DEBUG", "name": "__main__", "process": 368056, "processName": "MainProcess", "filename": "cli_demo.py", "lineno": 23, "message": "debug-level log message"}
{"asctime": "2022-01-11 11:32:23", "msecs": 514.7027969360352, "levelname": "ERROR", "name": "__main__", "process": 368056, "processName": "MainProcess", "filename": "cli_demo.py", "lineno": 24, "message": "error-level log message"}
Óscar Romeu

Lift as you climb

Óscar Romeu

Óscar Romeu

Lift as you climb
¿Te ha resultado interesante?

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

SÍGUENOS

CATEGORÍAS

ÚLTIMAS ENTRADAS