I would like to talk about another side project that kept me busy lately (apart from ITAQA, on which I’m still working!): the development of a small Python library to handle and orchestrate services (or daemons) in Linux.
Fantastic bots and where to find them
Lately I developed four Telegram bots using the really nice library python-telegram-bot.
For the ones that don’t know what a Telegram bot is, refer to this page. Briefly, in this context a bot is an application able to interact with users/groups directly on Telegram, answering to messages and accepting commands.
I will probably write about some of these bots in the future, for now I just want to talk about their hosting and management.
Hardware
A bot is a standard application (in this specific case, a Python script). To interact with it at any time, it needs to be constantly running. Two are the possible solutions:
- Hosting on external services
Services like Microsoft Azure, Heroku, OpenShift, Amazon AWS provide virtual machines (VMs) for different usages, on which is possible to run applications continuously. This alternative is the simplest (the machine is accessible everywhere, always active, with almost no maintenance needed), but it’s also limited by reduced performances, a cap on the amount of runnable applications and nerfed responsiveness. Although almost all the listed services offer free plans for non-commercial projects, to obtain a machine with better performances a monthly subscription is needed (around ~10€/month). - Self hosting on personal hardware
The “domestic” solution is to dedicate personal hardware for the hosting. This is possible keeping a PC constantly running (may be inconvenient) or using a small low-cost hardware platform, like a single-board computer.
For different reasons1 I choose the second solution: hosting using a Raspberry PI Zero W. With 512MB of RAM and a 1GHz single-core CPU, it’s not exactly a TOP500 PC, but considering the low amount of resources that my bots need, it’s enough. The key points of the Zero are the small size, the built-in WiFi and the possibility to powering it directly through Micro-USB (using a simple phone charger).
Software
Chosen the hardware and installed a Linux distro like Raspberry Pi OS (historically known as Raspbian), the bots need to be configured so they start automatically on system startup.
The easiest way to automatically start applications is to create ad-hoc services. In order to do this systemd, an administrative/configuration suite native in Linux systems, can be used. For every new service a unit file is written and put in /usr/lib/systemd/user/
:
[Unit]
Description=My bot
[Service]
ExecStart=/usr/bin/python3 /home/user/bot/main.py
Environment=PATH=/bin:/usr/bin:/usr/local/bin
WorkingDirectory=/home/user/bot/
[Install]
WantedBy=multi-user.target
It’s now sufficient to mark which services need to be started automatically using the command systemctl --user enable MyBot.service
.
All done. It suffices to turn the Raspberry on, forget it, to have everything working fine forever.
Great.
There is just a small detail: often2 things don’t go as planned, therefore it would be nice to be able to restart the bots, to check theirs logs, to kill them if needed, and so on. All things easy to do when connected to the same network of the Raspberry, more complex from somewhere else.3
It would be great if these operations were easy to perform from any place, maybe… creating a new bot…
It will be a humble “bot-manager”, with a modest name and a low-key profile picture. I don’t want to create an evil bot overlord.
Mh.
One library to bring them all and in the darkness bind them
A evil bot driven by power bot-manager4 cannot do anything if it’s not able to interact and control systemd
services. What it needs is a library to wrap systemctl
.
The ones that have already used Python know that there is a library for every need.5 I looked around for something that would suits me. I didn’t find anything useful, I decided to create one.
systemd-servicehandler
The library is called systemd-servicehandler, or just servicehandler.6
(I’m very proud of the banner)
Current state
servicehandler is still in an experimental phase, it’s nothing exceptional but it currently works. There are still some features to implement, together with tests and fixes.
Since there was no such library yet, I also decided to pack and distribute it on PyPI (servicehandler on PyPI), so that it can be used by other developers via pip (pip install servicehandler
).
Currently the base functionalities (service abstraction and utilities control methods7) are implemented, making it possible to control a bot (or any service) in the following way:
import servicehandler as sh
# Create a ServiceHandler object that represents the bot service
my_bot = sh.ServiceHandler('Bot','MyBot.service')
# Check bot status
my_bot.state()
<ServiceState.STOPPED: 2>
# Start the bot
my_bot.start()
Bot changed state to ServiceState.STOPPED
<Response.OK: 1>
# Stop the bot
my_bot.stop()
Bot changed state to ServiceState.RUNNING
<Response.OK: 1>
Coming soon
Some things to implement:
- Log/file monitoring, to access relevant files linked to a specific service
- Creation of a “watchdog class” able to monitor multiple ServiceHandler objects and to react automatically, following user-defined rules
- Exploring system services monitoring (root permissions needed)
Like the other projects in this blog, also servicehandler is completely open-source and available on GitHub: servicehandler.
See you soon, thanks for reading!
-
I know that any virtual machine of the free-tier would be enough. But, I try to maximize the “DIY” approach in my projects. Also, I decided to resurrect the poor RPi Zero that was sleeping in a drawer for years. I assume he is bored ↩
-
Always ↩
-
It’s of course possible to configure port-forwarding or use VNC, they are both valid solutions ↩
-
Who watches the watchers? Of course LordOfTheBots must be controlled manually (through SSH, for example). It must have a robust design, as it should be very stable ↩
-
I know I missed my chance to call the library
systemd-onering
or something similar, but I still have a glimmer of seriousness (laugh track)Â ↩ -
There are methods to start, stop, enable, disable, kill, get state ↩