Creiamo un servizio automatizzato collegato ad un Bot Telegram per Raspberry PI
Oggi ospitiamo il Blogger Antonio Musarra, noto per le proprie attività di divulgazione relative all’automazione di sistemi distribuiti dinamici.
Gli lasciamo immediatamente la parola.
In questo mio primo maggio 2020 trascorso dentro casa, ho cercato di “cucinare” qualcosa di creativo a base di Raspberry Pi, Bot Telegram, Display LCD e Relè. Da questa idea è nato il progetto e poi l’articolo Un primo maggio 2020 a base di Raspberry Pi, Bot Telegram, Display LCD e Relè.
Il Bot Telegram scritto in Python, è avviato direttamente via shell con il comando
./raspberry_pi_telegram_bot.py --token $TELEGRAM_BOT_TOKEN_API.
In questo articolo vedremo come sia possibile creare il Bot Telegram come servizio del sistema operativo, con l’obiettivo di poter essere gestito attraverso il gestore di sistema e di servizi per Linux, systemd.
Creando il servizio per il Bot Telegram, potremmo:
- Fare in modo che il Bot Telegram sia avviato sempre allo start del sistema operativo;
- Eseguire le operazioni di start, stop e restart del Bot Telegram;
- Verificare lo stato del servizio;
- Centrallizzare i file di log.
Come creare il servizio
Batteziamo il nuovo servizio con il nome raspberry-pi-telegram-bot. La procedura di creazione di questo nuovo servizio è davvero semplice e prevede i seguenti passi:
- Creare il cosiddetto unit che istruisca systemd su cosa eseguire e come;
- Aggiornare la configurazione di systemd in maniera che recepisca le direttive del nostro unit.
In systemd, un’unità (o unit) si riferisce a qualsiasi risorsa su cui il sistema sappia come operare e gestire. Questo è l’oggetto principale che gli strumenti di systemd sanno come gestire. Queste risorse sono definite usando file di configurazione chiamati unit file.
Systemd classifica le unità in base al tipo di risorsa che descrivono. Il modo più semplice per determinare il tipo di unità è con il suffisso type, che viene aggiunto alla fine del nome della risorsa. Esistono diversi tipi di risorse, quella di nostro interesse è service.
Un’unità di servizio (o service unit) descrive come gestire un servizio o un’applicazione sul server. Ciò includerà come avviare o arrestare il servizio, in quali circostanze dovrebbe essere avviato automaticamente e le informazioni sulle eventuali dipendenze e ordine di avvio.
Per maggiori informazioni su systemd consiglio vivamente la lettura delle risorse disponibili sul sito systemd System and Service Manager o a quello specifico della distribuzione Debian o ancora alla documentazione Usage systemd sul sito Raspberry Pi.
Prima di procedere con quanto necessario per realizzare il servizio per il Bot Telegram, dobbiamo fare il checkout o clone del progetto amusarra/raspberry-pi-telegram-bot. Per fare questo, accediamo alla console del Raspberry Pi ed eseguiamo il comando a seguire. Diamo per assunto di aver fatto accesso alla console con l’utente pi (la cui home directory è /home/pi)
1 |
$ git clone https://github.com/amusarra/raspberry-pi-telegram-bot.git |
Chiameremo il nostro unit file con il nome raspberry-pi-telegram-bot.service avente il contenuto a seguire.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[Unit] Description=Raspberry Pi Telegram Bot Documentation=https://www.dontesta.it/2020/05/07/primo-maggio-2020-base-raspberry-pi-bot-telegram-display-lcd-rele/ After=network-online.target [Service] Environment=TELEGRAM_BOT_TOKEN_API= Environment=TELEGRAM_BOT_DEBUG=true WorkingDirectory=/home/pi/raspberry-pi-telegram-bot ExecStart=/usr/bin/python3 raspberry_pi_telegram_bot.py --debug ${TELEGRAM_BOT_DEBUG} --token ${TELEGRAM_BOT_TOKEN_API} StandardOutput=syslog StandardError=syslog SyslogIdentifier=raspberry-pi-telegram-bot Restart=no User=pi [Install] WantedBy=multi-user.target |
Lo unit file è diviso in quelle che sono dette sezioni, all’interno delle quali sono presenti una serie di direttive. Il file deve essere creato all’interno della directory /etc/systemd/system/.
1 |
sudo vi /etc/systemd/system/raspberry-pi-telegram-bot.service |
Nel nostro caso il servizio sarà avviato solo dopo che tutte le interfacce si rete sia in stato up (direttiva After=network-online.target). All’interno della sezione Service abbiamo definito:
Due enviroment che specificano rispettivamente il token per accedere alle API di Telegram e l’opzione per attivare o no la modalità di debug per il Bot Telegram. Per l’environment TELEGRAM_BOT_TOKEN_API, dovreste sostituire il placeholder con il vostro token;
- Quale comando deve essere eseguito per l’avviare il servizio;
- La Working Directory;
- Di utilizzare il sistema standard syslog per il logging del servizio;
- La politica di restart del servizio;
- Qual è l’utente proprietario del servizio;
- Che il servizio deve essere abilitato nella modalità multi user del sistema operativo.
A seguire la descrizione delle sezioni e direttive utilizzate dal nostro unit file.
- Unit: Questa sezione è generalmente usata per definire i metadati per l’unità e configurare la relazione tra l’unità e le altre unità. Sebbene l’ordine delle sezioni non sia importante per systemd durante l’analisi del file, questa sezione viene spesso posizionata in alto perché fornisce una panoramica dell’unità;
- Description: Questa direttiva può essere utilizzata per descrivere il nome e la funzionalità di base dell’unità. Viene restituito da vari strumenti di sistema, quindi è bene impostare questa direttiva su qualcosa di breve, specifico e informativo;
- Documentation: Questa direttiva fornisce un elenco di URI per la documentazione. Possono essere pagine man disponibili internamente o URL accessibili dal web. Il comando di stato systemctl esporrà queste informazioni, consentendo una facile individuazione;
- After: Le unità elencate in questa direttiva verranno avviate prima di avviare l’unità corrente. Ciò non implica una relazione di dipendenza.
- Description: Questa direttiva può essere utilizzata per descrivere il nome e la funzionalità di base dell’unità. Viene restituito da vari strumenti di sistema, quindi è bene impostare questa direttiva su qualcosa di breve, specifico e informativo;
- Service: Questa sezione è utilizzata per fornire una configurazione applicabile solo ai servizi.Una delle cose di base che dovrebbero essere specificate nella sezione è il tipo di servizio (simple il default). Questo classifica i servizi in base al loro processo e comportamento demone. Questo è importante perché dice a systemd come gestire correttamente il servizio e scoprirne lo stato;
- Environment: Questa direttiva imposta le variabili di ambiente per i processi eseguiti. Prende un elenco separato da spazi di assegnazioni di variabili. Questa opzione può essere specificata più di una volta, nel qual caso verranno impostate tutte le variabili elencate;
- ExecStart: Questa direttiva specifica il percorso completo e gli argomenti del comando da eseguire per avviare il processo. Se il percorso del comando è preceduto da un trattino “-“, gli stati di uscita (o exit code) diversi da zero saranno accettati senza contrassegnare l’attivazione dell’unità come non riuscita;
- WorkingDirectory: Questa direttiva imposta la directory di lavoro per i processi eseguiti. Se impostato su “~”, viene utilizzata la home directory dell’utente specificato in User. Se non impostato, il valore predefinito è la directory principale quando systemd è in esecuzione come istanza di sistema e la directory principale dell’utente corrispondente se eseguita come utente;
- StandardOutput: Questa direttiva controlla dov’è collegato il descrittore di file 1 (stdout) dei processi eseguiti. I valori ammessi sono diversi. Nel nostro caso syslog;
- StandardError: Questa direttiva controlla dov’è collegato il descrittore di file 2 (stderr) dei processi eseguiti. I valori ammessi sono diversi. Nel nostro caso syslog;
- SyslogIdentifier: Imposta il nome del processo (“tag syslog”) come prefisso per le righe di log inviate. Se non impostato, il valore predefinito è il nome del processo eseguito. Questa opzione è utile solo quando StandardOutput o StandardError sono impostati su journal, syslog o kmsg e si applica solo ai messaggi di log scritti su stdout o stderr;
- Restart: Questa direttiva indica le circostanze in cui systemd tenterà di riavviare automaticamente il servizio. Questo può essere impostato su valori come always, on-success, on-failure, on-abnormal, on-abort o on-watchdog;
- User: Questa direttiva imposta l’utente o il gruppo UNIX in cui i processi vengono eseguiti, rispettivamente. Accetta un singolo nome utente o gruppo o un ID numerico come argomento.
- Install: Questa sezione è facoltativa e viene utilizzata per definire il comportamento. Le direttive all’interno dettano cosa dovrebbe accadere quando l’unità è abilitata;
- WantedBy: Questa direttiva è il modo più comune per specificare come abilitare un’unità. Questa direttiva consente di specificare una relazione di dipendenza in modo simile alla direttiva Wants nella sezione Unit. Quando un’unità con questa direttiva è abilitata, sarà creata una directory all’interno di /etc/systemd/system che prende il nome dell’unità specificata con .wants aggiunto alla fine. All’interno di questo, verrà creato un collegamento simbolico all’unità corrente, creando la dipendenza. Ad esempio, se l’unità corrente ha WantedBy=multi-user.target, sarà creata una directory chiamata multi-user.target.wants all’interno di /etc/systemd/system (se non già disponibile) e un collegamento simbolico all’unità corrente sarà inserito all’interno. La disabilitazione di questa unità rimuove il collegamento e rimuove la relazione di dipendenza.
A questo punto non dobbiamo fare altro che aggiornare la configurazione di systemd in modo che recepisca le direttive del nostro unit. Per applicare questa configurazione basta eseguire i comandi a seguire.
1 2 |
$ sudo systemctl daemon-reload $ sudo systemctl enable raspberry-pi-telegram-bot |
L’output del comando di abilitazione del servizio dovrebbe essere come quello mostrato a seguire. Questo è l’effetto della direttiva WantedBy all’interno della sezione Install dello unit file.
1 |
Created symlink /etc/systemd/system/multi-user.target.wants/raspberry-pi-telegram-bot.service → /etc/systemd/system/raspberry-pi-telegram-bot.service. |
Se tutto dovesse andare per il verso giusto, systemd dovrebbe aver caricato (loaded) il servizio Bot Telegram. Se in futuro volessimo (anche temporaneamente) disabilitare il servizio, possiamo usare il comando a seguire.
1 |
$ sudo systemctl disable raspberry-pi-telegram-bot |
Gestione del nuovo servizio
Il nuovo servizio raspberry-pi-telegram-bot può essere adesso gestito tramite i comandi messi a disposizione da systemd. A seguire sono mostrati i principali comandi.
1 2 3 4 5 6 7 8 9 10 11 |
# Verifica lo stato del servizio $ sudo systemctl status raspberry-pi-telegram-bot # Avvia il servizio $ sudo systemctl start raspberry-pi-telegram-bot # Ferma il servizio $ sudo systemctl stop raspberry-pi-telegram-bot # Riavvia il servizio $ sudo systemctl restart raspberry-pi-telegram-bot |
In Figura 1 è mostrato l’output del comando sudo systemctl status raspberry-pi-telegram-bot dopo l’avvio del servizio. Grazie al fatto di aver specificato d’inviare lo standard output sul syslog di sistema, è possibile notare come il comando mostri le ultime entry di log del Bot Telegram e il tag raspberry-pi-telegram-bot.
In Figura 2 è invece mostrato l’output dopo aver eseguito lo stop del servizio. Quando il servizio è giù, la visulizzazione dello stato mostra molte informazioni utili, alcune delle quali sono quelle che abbiamo definito sullo unit file.
In Figura 3 è mostrato tramite il comando htop lo stato del servizio del Bot Telegram.
Alcuni dei vantaggi più convincenti di systemd sono quelli che riguardano il log di processi e sistemi. Quando si utilizzano altri strumenti, i log sono generalmente dispersi in tutto il sistema, gestiti da demoni e processi diversi e possono essere abbastanza difficili da interpretare quando si estendono su più applicazioni.
Systemd tenta di risolvere questi problemi fornendo una soluzione di gestione centralizzata per la registrazione di tutti i processi kernel e userland. Il sistema che raccoglie e gestisce questi registri è noto come journal.
Il journal è implementato con il demone journald, che gestisce tutti i messaggi prodotti dal kernel, initrd, servizi, ecc.
Noi possiamo usare l’utilità journalctl per accedere ai log specifici del nostro servizio. Come possiamo fare? Semplice, in questo modo.
1 2 3 4 5 6 7 8 9 10 11 |
# Visionare l'intero diario $ sudo journalctl -u raspberry-pi-telegram-bot # Visionare gli ultimi log $ sudo journalctl -u raspberry-pi-telegram-bot -f # Visionare i log filtrati per data $ sudo journalctl -u raspberry-pi-telegram-bot --since yesterday # Visionare i log filtrati per data e in formato json $ sudo journalctl -u raspberry-pi-telegram-bot --since yesterday -o json-pretty |
La Figura 4 mostra i file di log del Bot Telegram utilizzando il comando
1 |
sudo journalctl -u raspberry-pi-telegram-bot -f |
Conclusioni
In semplici step abbiamo visto come sia possibile trasformare i nostri script o programmi in servizi gestiti interamente dal sistema Linux systemd. Usare systemd offre diversi vantaggi, alcuni dei quali:
- Fornire un manager di sistema e servizi;
- Fornire una notevole capacità di parallelizzazione;
- Usa socket e D-Bus per l’avvio dei demoni;
- Offre un avvio su richiesta dei demoni;
- Tiene traccia dei processi con l’utilizzo del control groups di Linux;
- Supporta lo snapshotting e il restore dello stato del sistema, mantiene i punti di mount e di automount e implementa un elaborato servizio di controllo logico basato sulle relazioni delle dipendenze;
- Include un demone per il logging, utility per controllare aspetti base del sistema come hostname, data, locale, lista degli utenti loggati;
- Può avviare containers e macchine virtuali.
Adesso vi lascio alla creazione del vostro servizio. Ricordo che tutto il materiale di riferimento è all’interno del repository GitHub amusarra/raspberry-pi-telegram-bot