ChatOps utilizando Errbot
Hace un tiempo, uno de nuestros clientes nos contactó por una necesidad recurrente: obtener copias de las bases de datos de sus sistemas para que el equipo de desarrollo y QA puedan analizar datos. El problema, es que el acceso a estas bases de datos está restringido y los usuarios no acceden a servidores en producción.
Fue así como propusimos el desarrollo de un robot que se encargue de realizar las tareas en nombre de aquellos usuarios permitidos para solicitar estos pedidos. La forma más simple de hacerlo, fue pensar en cómo nos solicitaban los pedidos al personal de Mikroways y replicar el comportamiento de personas con un robot. Como la herramienta de comunicación con el cliente es Slack, procedimos en configurar un ChatBot que atienda pedidos en un canal al que solamente determinados usuarios pertenecen.
Hay que recalcar que esta tendencia, también conocida como ChatOps, presenta una mejora sustancial a los procesos manuales ya que permite a los usuarios finales ejecutar tareas repetitivas de manera desatendida y con la seguridad de no cometer errores.
Siendo así, en este post realizaremos un breve aporte respecto al uso de robots de chat para la automatización de tareas relacionadas a la operación de herramientas de software. Por lo cual seguiremos dos ejemplos, uno básico que muestra las estructuras principales de la herramienta utilizada y otro donde se ejemplifica un hipotético caso real.
Errbot
Errbot es un ChatBot desarrollado en Python, creado para conectar servicios de chats llamados Backends, con distintos plugins Python llamados BotPlugins. Su instalación se puede realizar siguiendo la documentacion oficial. En este tutorial haremos uso de la imagen oficial de Docker para las pruebas. Por eso sera necesario contar con Docker instalado previamente.
Nota: Los archivos que necesitaremos en este tutorial serán creados sobre la carpeta
/tmp/errbot/
para organizar el trabajo.
BotPlugins
Para comprender el funcionamiento de los BotPlugins se suele recurrir a mostrar ejemplos. De esta forma, para comenzar, vamos a crear un plugin de HelloWorld. El primer paso entonces será el de crear la carpeta en donde se alojará nuestro plugin:
mkdir -p /tmp/errbot/plugins/hello/
Como paso siguiente debemos ejecutar el comando para crear un archivo cuyo contenido es un simple BotPlugin de ejemplo que llamaremos helloworld.py:
cat <<EOT > /tmp/errbot/plugins/hello/helloworld.py
from errbot import BotPlugin, botcmd
class HelloWorld(BotPlugin):
"""Example 'Hello, world!' plugin for Errbot."""
@botcmd
def hello(self, msg, args):
"""Say hello to the world."""
return "Hello, world!"
EOT
Se puede observar que este plugin agrega la posibilidad de ejecutar el comando
!hello
(debido al decorator
sobre la función)
en nuestro Backend y como resultado devolverá el mensaje "Hello, world!"
al
ser invocado. Además, el comando !help
devolverá la ayuda que se corresponde
con el docstring diciendo: Say hello to
the world.
Los plugins demás deben estar acompañados de un archivo .plug que contiene los metadatos del plugin. Con el siguiente bloque de código crearemos el archivo que contiene la estructura de metadatos del plugin llamado helloworld.plug.
cat <<EOT > /tmp/errbot/plugins/hello/helloworld.plug
[Core]
Name = HelloWorld
Module = helloworld
[Python]
Version = 3
[Documentation]
Description = Example "Hello, world!" plugin
EOT
Así tendremos nuestro primer plugin llamado helloworld
creado.
Backends
Los Backends son interfaces definidas en el nucleo de Errbot que implementan un conjunto de comandos para poder comunicarse con diferentes servicios de chat. Algunos ejemplos de éstos son Discord, Slack, Telegram, entre otros. Además debemos tener en cuenta los Backends desarrollados por la comunidad, por ejemplo el de SlackV3 e inclusive Mattermost. La ventaja que tiene esta herramienta es que los incorpora transparentemente, es decir, si en algún momento se necesita cambiar de un Backend a otro se puede realizar sin mayores cambios en los plugin que hemos desarrollado. De todas formas, no necesariamente implementaran todas las funciones de la interfaz. Por ejemplo, el backend de Discord oficial no tiene implementada la función de envió de archivos. Esto puede verificarse desde la implementacion de cada backend.
Para seguir con el tutorial del plugin HelloWorld crearemos el archivo config.py que contiene la configuración de Errbot e indicaremos un backend a utilizar. Para esto podremos ejecutar el siguiente comando:
cat <<EOT > /tmp/errbot/config.py
import logging
BACKEND = "Text"
BOT_IDENTITY = {
"username": "@errbot"
}
STORAGE = "Memory"
BOT_DATA_DIR = "/tmp/"
BOT_LOG_FILE = None
BOT_LOG_LEVEL = logging.INFO
BOT_EXTRA_PLUGIN_DIR = "plugins/"
BOT_ADMINS = ("@localhost",)
EOT
Esta configuración, elige como backend Text
el cual es muy útil para entornos de
prueba ya que inicia Errbot en modo consola.
Finalmente tendremos que ejecutar el servicio desde nuestra consola con el comando que observaremos a continuación:
# Creara una consola en el servicio de Errbot y nos permite ejecutar comandos
docker run -it --rm -v /tmp/errbot/:/home/errbot/ errbotio/errbot
# Para ver la lista de comandos disponibles
!help
# Para ejecutar el comando del plugin
!hello
Solución propuesta
Para solucionar el problema mencionado en la introducción, crearemos un nuevo plugin llamado dumps que utiliza la libreria subprocess para invocar procesos en el host. La intención es utilizar herramientas externas como mysqldump y gzip para hacer los resguardos de la base y luego comprimirlos.
Nuevo plugin
De esta forma, procedemos con la creación del plugin considerando únicamente
el motor MySQL o Mariadb, realizando un dump y luego mostrando el resultado del
comando mysqldump
en la consola. Entonces, primero debemos crear la carpeta
donde se alojará nuestro plugin:
mkdir -p /tmp/errbot/plugins/dumps/
A continuación creamos el archivo dumps.py. Podemos ejecutar el siguiente comando para crearlo:
cat <<EOT > /tmp/errbot/plugins/dumps/dumps.py
# Importamos las librerias necesarias
import os
import sys
import time
import pipes
import subprocess
import time
from errbot import BotPlugin, arg_botcmd, botcmd, ValidationException
class Dumps(BotPlugin):
# Creamos el comando dumpdb al cual se le pasa un argumento
# Este argumento es el nombre de la base de datos
@arg_botcmd('database', type=str)
def dumpdb(self, msg, database=None):
"""Realiza el dump de de la db especificada."""
# En este string se genera el comando que se ejecuta
# Hay que recalcar que mysqldump debe estar instalado en el host
dumpcmd = ["mysqldump",
"-h","mariadb",
"-u","user",
"-p"+"password",
database]
# Ejecutamos el comando de mysqldump
dump = subprocess.Popen(dumpcmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = dump.communicate()
# Devolvemos el resultado
return out.decode() + err.decode()
EOT
Y siguiendo tenemos su correspondiente dumps.plug. Podemos ejecutar el comando para crearlo:
cat <<EOT > /tmp/errbot/plugins/dumps/dumps.plug
[Core]
Name = Dumps
Module = dumps
[Documentation]
Description = A errbot plugin for mysql dumps.
[Python]
Version = 3
EOT
Con ambos archivos tendremos nuestro plugin dumps
armado.
Configuración
El siguiente paso es configurar Errbot teniendo en cuenta el Backend que utilizaremos. La documentación oficial nos provee un archivo de configuración de ejemplo en este enlace. Debemos observar con atención que en cada Backend las identidades que necesitan configurarse son distintas.
Para seguir el ejemplo, podremos utilizar el archivo de configuración que ya
hemos creado, aunque se sugiere verificar la documentación para trabajar con
otros Backends y agregar la funcionalidad de send_stream_request
para dejar
los resguardos en un servicio de mensajería (por ejemplo con SlackV3).
Para finalizar, podremos ejecutar los siguiente comandos para verificar que el nuevo plugin que hemos armado funciona correctamente:
# Creamos una nueva red Docker donde se hara la prueba
docker network create errbot-test
# Iniciamos la base de datos Mariadb en un contenedor
docker run --detach --network errbot-test --name mariadb -e MARIADB_USER=user \
-e MARIADB_PASSWORD=password -e MARIADB_ROOT_PASSWORD=password2 \
-e MARIADB_DATABASE=test mariadb:latest
# Iniciarmos Errbot y deja la consola activa para interactuar
# Nota: la imagen se modifico para agregarle el binario de mysqldump
docker run -it --network errbot-test --name errbot --rm \
-v /tmp/errbot/:/home/errbot/ manuelchichi/errbot-mysqldump:1.0
# Ejecutamos el comando en la consola del contenedor
!dumpdb test
# Podemos ver el backup arrojado como texto plano por la herramienta
# Una vez finalizado podremos ejecutar los siguiente comandos para limpiar la
# prueba
docker stop mariadb
docker rm mariadb
docker network rm errbot-test
rm -rf /tmp/errbot
El siguiente video muestra cómo es la interacción con el bot:
Conclusiones
La solución que se propuso demostró gran satisfacción del lado del cliente ya que se automatizo una tarea que era muy frecuente y que muchas veces ralentizaba el trabajo de un área de desarrollo completa. De nuestro lado, saco el trabajo adicional ocasionado por tener que realizar estas tareas en situaciones especificas.
Como experiencia nos hemos llevado la sorpresa de que configurar la herramienta es una tarea sencilla, que tiene mucha versatilidad y que podría ser de gran utilidad en equipos de DevOps. De nuestro lado, estamos ansiosos de identificar otros escenarios en los cuales puedan ofrecerse este tipo de soluciones.