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 🚀
En aquest article presento el meu nou bot, estructurant-ho en les següents seccions:
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.
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
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ó.
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.
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.
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
).
Com a qualsevor bot en Python per a Mastodon, necessito dues coses principalment:
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.
Com explico més amunt, aquest bot té diferents modes de funcionament però els passos per a instalar-lo són principalment els mateixos:
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 -
git clone git@github.com:XaviArnaus/janitor.git
cd janitor
cp config.yaml.dist config.yaml
nano config.yaml
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:
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ó.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.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)make init
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
Fer funcionar el bot és molt fàcil. A continuació enumero les comandes per a cada mode de funcionament:
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.
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
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
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ó:
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
.POST
- La petició ha de ser de tipus POST.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
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ó.
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 tascawhen
és una expressió crontab
que defineix quan s'ha d'executar aquesta tasca.action
és un dels possibles valors: "sysinfo_local" o "sysinfo_remote".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,
Editem el crontab del nostre sistema
crontab -e
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.
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.
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.
En aquesta secció vull presentar un parell de casos pràctics on aquest bot està essent de gran ajuda.
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:
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!
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.
Aquí per exemple hi ha una captura amb aquests dos punts en 3 missatges:
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:
Dependrà del temps i dels altres projectes que tinc entre mans.