Portava temps que em rondava pel cap la idea d'un bot que comprovés algunes mètriques del sistema i si algun valor es dispara llavors em publiqui una alerta a Mastodon. El que va començar com a un projecte senzill ha acabat una mica més treballat, capaç de comunicar entre bots i fer de centraleta d'alarmes i missatges de log.

En aquest article explico què fa i com posar-lo en marxa 🚀

A grans trets

En aquest article presento el meu nou bot, estructurant-ho en les següents seccions:

  1. Què és, què fa i quin és l'objectiu?
    1. Mode local
    2. Mode servidor
    3. Mode client
  2. Mastodon, Pleroma o Akkoma?
  3. Què necessito?
  4. Instal.lació dels diferents modes de funcionament
  5. Executar els diferents modes de funcionament
  6. Executant-lo periòdicament
  7. Altres configuracions
  8. Aplicació pràctica
  9. Cloenda, desenvolupament personal i següents passos

1. Què és, què fa i quin és l'objectiu?

El Janitor és un bot escrit en Python que recull periòdicament mètriques del sistema i les compara amb uns limits definits, i en cas de que els valors els sobrepassin publica una alerta a un servidor Mastodon en forma de post d'usuari. També permet a vàries màquines comunicar les metriques a una que farà de servidor i que centralitzarà la definició dels límits i la publicació de les alertes. I ja que hi som, aquest servidor també permet rebre missatges arbitraris, de forma que podem fer que els nostres scripts i processos publiquin missatges de log i alertes.

Janitor significa "conserge" en anglès, i aquest és el rol que voldria per aquest bot en el futur: que m'avisi dels problemes i que hi pugui interactuar per a fer certes tasques, però això ja és cosa del futur.

GitHub Janitor in GitHub
Bot in Python that collects system metrics and publishes them via a Mastodon-like API in case of crossing thresholds. Also publishes arbitrary messages in a client-server API fashion for generic monitoring.

A continuació explico els diferents modes d'execució amb una mica més de detall

1. Mode local

Aquesta és la execució típica del bot: Llegirà les mètriques del sistema, les compararà amb uns valors definits en l'arxiu de configuració i si cal, formatejarà un missatge d'acord a unes plantilles i el publicarà a un servidor Mastodon a través de l'usuari definit també a la configuració.

2. Mode servidor

Aquesta execució deixa un process corrent en el sistema encarregat d'escoltar crides a dos endpoints d'una API: una per rebre les mètriques d'un bot remot, i una altra per rebre missatges arbitraris. Sobre el primer, un cop rebudes les mètriques el reste d'accions és el mateix: comparar els valors i si cal crear un post i publicar-lo via l'API de Mastodon. Sobre el segon, en parlarem més endavant en la secció Executar en els diferents modes de funcionament.

3. Mode client

Aquesta execució llegeix les mètriques del sistema i les envia a un bot en mode servidor per a que les interpreti i les publiqui si cal. És un mode ideal per tenir corrent en un conjunt de Raspberries de forma que n'hi hagi una que funcioni de servidor, reduint el manteniment general de la infrastructura.

2. Mastodon, Pleroma o Akkoma?

De fet dóna una mica igual: tots tres funcionen amb la API de Mastodon. Hi ha unes petites diferències que la pròpia API maneja per si mateixa sempre que se li afegeixi els paràmetres adequats, que el bot maneja tot solet. Només cal definir en la configuració si estem comunicant-nos amb un servidor Mastodon (mastodon) o un servidor Pleroma / Akkoma (pleroma).

3. Què necessito?

Com a qualsevor bot en Python per a Mastodon, necessito dues coses principalment:

  1. Una conta a un servidor Mastodon, Pleroma o Akkoma.
  2. Una màquina capaç de còrrer aplicacions en Python. Habitualment estem parlant d'un host a internet, amb un Linux o no, i molt probablement podrà ser el servidor Mastodon mateix, un servidor web o fins i tot una Raspberry Pi a casa conectada a internet.

Pel primer cal crear una conta en un dels servidors que hi han online o reutilitzar alguna altra conta que es tingui. En el meu cas he creat una conta "Serveis" en la instància Akkoma que vaig crear fa unes setmanes. Pel segon és complicat recomanar què és millor ja que cadascú tindrà una situació diferent. El meu cas particular està més enfocat a controlar el que les meves Raspberries fan a casa així que el més natural és utilitzar-ne una d'elles per això, però val a dir que en les meves proves també l'he instal.lat al hosting d'una de les meves pàgines web i és perfectament valid. Per al cas d'aquest article assumim que tenim una Raspberry Pi instal.lada, llesta i conectada a internet, i amb Python 3 instal.lat al sistema.

4. Instal.lació dels diferents modes de funcionament

Com explico més amunt, aquest bot té diferents modes de funcionament però els passos per a instalar-lo són principalment els mateixos:

0. Assegura't que tenim el que necessitem

Aquest projecte utilitza Poetry. Assegura't que tens Poetry instal.lat al sistema, i si no, segueix els passos aquí, que bàsicament en una màquina amb Linux o Mac, és executar la següent comanda i seguir els passos per pantalla:

curl -sSL https://install.python-poetry.org | python3 -

1. Clonar el repositori

git clone git@github.com:XaviArnaus/janitor.git

2. Mou-te al directori del repositori

cd janitor

3. Crea l'arxiu de configuració a partir de l'exemple

cp config.yaml.dist config.yaml

4. Edita el nou arxiu de configuració

nano config.yaml

5. Canvia els paràmetres segons el mode de funcionament del bot

Aquí la cosa varia una mica. Hi han uns paràmetres de configuració que són imprescindibles per alguns modes de funcionament i d'altres que s'ignoren completament. L'arxiu de configuració està ben documentat, amb comentaris (en anglès) a cada secció i paramàmetre per tal que s'entengui què fa cadascun. Tanmateix, a continuació exposo els paràmetres imprescindibles per cada mode:

Típic mode local

Aquest mode comproba les mètriques del sistema i publica a Mastodon, però no fa servir la funcionalitat de client / servicor.

  • mastodon.api_base_url: Defineix la URL del servidor Mastodon on tenim l'usuari que publicarà els posts.
  • mastodon.instance_type: És el tipus d'instància del nostre servidor. Per a un servidor Mastodon definirem mastodon i per un servidor Pleroma o Akkoma definirem pleroma
  • mastodon.credentials.user: Defineix aquí l'usuari (user) i la contrassenya (password). S'utilitzarà per crear l'arxiu user.secret que serveix per autenticar-nos al servidor al publicar posts. Quan l'arxiu està creat, podem netejar els valors, ja que no es faran servir més.
  • app.run_control.dry_run: Defineix-ho a False quan estiguis a punt per publicar. Aquesta opció permet fer funcionar el bot sense que acabi publicant res enlloc (només genera logs)
  • status_post.content_type: Per a servidors Mastodon ha de ser obligatòriament text/plain. Per a servidors Pleroma o Akkoma pot ser qualsevor dels valors definits en el comentari de l'arxiu de configuració.

Mode servidor

Aquest mode publica els informes de les mètriques del sistema i els missatges arbitraris rebuts, però no llegeix cap mètrica de la màquina local. Cal definir els mateixos paràmetres que en el mode local i a més:

  • app.service.listen: Què cal escoltar. Definint host a 0.0.0.0 escoltarà a totes les IPs que arribin. port defineix quin port escoltarà, ignorant els altres.

Mode remot

Aqiest mode només llegeix les dades de la màquina local i les envia a un bot que estigui en mode servidor. Per tant, ni comproba ni publica. Només cal que definim els següents paràmetres:

  • app.service.remote_url: On enviar les mètriques llegides.
  • app.run_control.dry_run: Defineix-ho a False quan estiguis a punt per enviar les mètriques. Aquesta opció permet fer funcionar el bot sense que acabi enviant res enlloc (només genera logs)

6. Instal.lar les dependències de Python

make init

7. Crear l'App

Per publicar al servidors Mastodon primer hem d'assegurar-nos que tenim l'autenticació definida. Per això, un cop hem definit l'usuari i la contrassenya (i el tipus de servidor) en l'arxiu de configuració, cal que executem el següent per tal de crear els arxius "secrets":

make create_app

5. Executar els diferents modes de funcionament

Fer funcionar el bot és molt fàcil. A continuació enumero les comandes per a cada mode de funcionament:

Mode local

Simplement executa la següent comanda des del directori principal del repositori:

make run_local

Si el paràmetre logger.loglevel està definit a 20 (INFO) i no hi han mètriques que sobrepassin els limits definits en 'arxiu de configuració, el log del programa que apareix en pantalla hauria de ser similar al següent:

[2023-03-17 08:24:57,891] INFO     janitor Init Local Runner
[2023-03-17 08:24:57,891] INFO     janitor Run local app
[2023-03-17 08:24:58,893] INFO     janitor No issues found. Ending here.

Mode servidor

Aquesta execució arranca un servei amb una API i es queda a l'espera escoltant per peticions als endpoints definits, no mostra res per pantalla. Cal executar la següent comanda des del directori principal del repositori:

make listen

El servei treballa a nivell de sistema, així que en aquest punt podriem tancar la conexió amb el servidor, si estem conectats via SSH. Podem veure què va passant en el servei observant l'arxiu log a log/listen_in_background.log.

Per acabar el servei cal matar el procés, i per a fer tal cosa cal saber quin és el ID del procés. Per saber l'ID podem executar la seüent comanda en un entorn Linux:

ps aux | grep listen.py | grep -v grep

Ens hauria de donar un "process ID". Diguem que és 12345. A continuació, executarem la següent comanda per "matar" el procès:

kill -9 12345

Mode remot

Simplement executa la següent comanda des del directori principal del repositori:

make run_remote

Si el paràmetre logger.loglevel està definit a 20 (INFO), el log del programa que apareix en pantalla hauria de ser similar al següent:

[2023-03-17 08:24:57,891] INFO     janitor Init Remote Runner
[2023-03-17 08:24:57,891] INFO     janitor Run remote app
[2023-03-17 08:24:58,893] INFO     janitor Sending sys_data away

Enviant missatges arbitraris al bot servidor

En el moment en que tenim un bot funcionant com a servidor, tenim un endpoint esperant rebre missatges arbitraris que es convertiran en posts publicats a Mastodon. Aquests són els paràmetres de la petició:

  • Endpoint: http://[ip-del-servidor]:[port-del servidor]/message - on [ip-de-servidor] és la IP de la màquina que té funcionant el bot en mode servidor i [port-de-servidor] és el port configurat en l'arxiu de configuraci´d'aquest mateix bot en mode servidor. Per defecte el port és el 5000.
  • Mode: POST - La petició ha de ser de tipus POST.
  • Paràmetres:
    • hostname - [obligatori] És el nom de la màquina que envia el missatge. Pot ser qualsevor cadena de texte, i s'usarà com a identificador de la màquina.
    • message - [obligatori] És el missatge principal a enviar.
    • summary - [opcional] És un text que s'usarà com a resum o entrada al post. En els posts de Mastodon s'anomena spoiler_text i en algunes instàncies Mastodon o Akkoma el post queda amagad darrera 'aquest resum fins que es clica a un botó de "veure més". És ideal per un missatge curt i fer servir el message per tot el missatge de debug del possible error.
    • message_type = [opcional] És una cadena de text que identifica un dels possibles tipus de missatges que hi han, i que farà que aparegui un emoji al principi del missatge (si no hi ha summary) o al principi del resum (si hi ha summary). Els possibles valors són els següents:
      • none: No posarà cap icona. És el valor per defecte.
      • info: Pensat per missatges informatius. Presentarà l'emoji ℹ️
      • warning: Pensat per missatges d'alerta. Presentarà l'emoji ⚠️
      • error: Pensat per missatges d'error. Presentarà l'emoji 🔥
      • alert: Pensat per missatges generals d'alarma. Presentarà l'emoji 🚨

Per a sistemes Linux, una forma comú d'enviar un missatge seria fent servir el curl, per exemple:

curl -X POST -d "hostname=MyHostname&message=This+is+a+test+message" http://server:5000/message

6. Executant-lo periòdicament

A part del mode servidor, que l'executes un sol cop i queda en memòria fins que el paris, normalment voldràs que els altres dos modes s'executin periòdicament, cada cert temps. Per això el més habitual és afegir-ho al cron del sistema. Òbviament, pots configurar les comandes per l'execució local i de client remot en el cron i llestos.

Pensant en això i en futures funcionalitats, hi ha un altre executor disponible que un cop configurat, et permet fer els ajustaments directament en l'arxiu de configuració.

1. Defineix l'execució periòdica

A l'arxiu de configuració hi ha un paràmetre app.schedules que accepta una llista d'objectes per cada tasca a realitzar. Cada objecte està format pels següents paràmetres:

  • name és el nom de la tasca
  • when és una expressió crontab que defineix quan s'ha d'executar aquesta tasca.
  • action és un dels possibles valors: "sysinfo_local" o "sysinfo_remote".

2. Afegir l'executor al nostre crontab

Si, és clar, utilitzem el crontab del sistema per cridar el nostre propi crontab. Pels qui estan acostumats als sistemes Linux trobaran que estem afegint complexitat on no cal, i en certa manera és cert. Però també afegim la possibilitat de controlar l'execució des de dins del programa, podent fer i desfer el que ens calgui. En futures versions espero introduir tasques de manteniment i neteja i tenir una crida periòdica a la que poder anar afegint tasques és molt útil.

Per tant,

  1. Editem el crontab del nostre sistema

    crontab -e
  2. Afegim la següent línia:

    * * * * * cd /ruta/al//repositori/janitor; make scheduler

    on haurem de canviar la ruta al repositori per la que toqui.

  3. Guardar i sortir

Ara, a cada minut el sistema cridarà el nostre executor, que s'encarregarà de disparar les tasques que toquin a cada moment.

7. Altres configuracions

Aquest bot està implementat pensant en poder-lo configurar el màxim possible. L'arxiu de configuració té un bon grapat de compentaris que expliquen cada paràmetre. Tanmateix, en el README del repositori n'hi han els més importants explicats amb una mica més de detall.

8. Aplicació pràctica

En aquesta secció vull presentar un parell de casos pràctics on aquest bot està essent de gran ajuda.

1. Control de l'estat de les Raspberry Pi

Com potser hauràs vist pels articles que escric últimament, a casa tinc unes quàntes Raspberries fent tasques diverses. El meu TOC fa que a vegades em posi nerviós si triguen una mica més del normal a respondre i he trobat a faltar un sistema de monitoreig per anar consultant. Utilitzant aquest bot, he muntat el següent:

  • 1 bot com a servidor
  • 4 bots com a clients remots (incloent la màquina que té el servidor tenint un segon bot com a client remot enviant a localhost)

Els 4 remots recullen les mètriques locals cada minut i les envien al servidor, que fa les comprobacions necessàries i publica un tut en cas que els valors siguin alts. Des que estan funcionant, he vist un sol cop un tut avisant que la CPU estava per sobre del 80%, durant una tasca que no tenia massa supervisada. Gràcies!

2. Control de funcionament d'un script

En un servidor web que tinc al hòsting, tinc uns scripts que fan coses i un d'ells es conecta a GitHub i fa un git pull periòdicament. La setmana passada GitHub va canviar la clau RSA sense que jo m'assebentés i l'script va fallar. Casualment dos dies abans havia arrancat un bot en mode servidor i actualitzat l'script per que m'avisés del seu resultat i dels seus possibles errors, i vaig poder capturar l'error a la primera falla, podent arreglar el problema ben ràpid. D'altra forma encara ara estaria pensant per què certes coses no estan funcionant com toca fins que em conectés a verificar els logs.

akkoma-screenshot

Aquí per exemple hi ha una captura amb aquests dos punts en 3 missatges:

  • El missatge superior és l'error del punt 2 referent al canvi en GitHub que va fer fallar l'script
  • El missatge del mig és una execució correcte de l'script
  • El missatge inferior és un informe de l'estat d'una de les meves Raspberries dient que la CPU està per sobre del límit.

9. Cloenda, desenvolupament personal i següents passos

Tot això és una joguina. M'agrada muntar-me les meves pròpies sol.lucions als problemes. Però passa que també sóc programador i una mica detallista, i treballo en un equip d'alemanys encara més perapunyetes que jo. Gràcies a ells he après molt d'estàndards i de com s'ha de cobrir el codi. Hi ha coses en les que no hi estic molt d'acord però sigui com sigui m'agrada practicar les normes en els meus projectes personals.

En aquest projecte he après a mantenir un paquet Python, tal com tinc el pyxavi a Github, tot i que em queda encara publicar-lo a PyPi i que funcioni com una dependència més. També m'he ficat més amb els tests en Python, aprenent diverses tècniques per mockejar i patchejar els objectes a testejar, tot per tenir els tests unitaris com m'agradaria. També he après com de fàcil és muntar una API en Python i com publicar a servidors Pleroma i Akkoma.

Però això no acaba aquí, tinc plans per afegir-hi algunes coses més:

  • Guardar les mètriques per poder treure gràfiques
  • Interactuar amb el bot, poder-li preguntar per l'estat o les mètriques d'alguna màquina (per calmar el TOC si no hi han alarmes per algun temps)

Dependrà del temps i dels altres projectes que tinc entre mans.

Previous Post Next Post