Guillaume Fenollar DevOps et SysAdmin Freelance

Guillaume FENOLLAR

Ingénieur Linux/DevOps Indépendant

− Montpellier −

Utiliser Systemd et SDNotify pour gérer ses démons Python

Je ne vais pas m’étendre sur une intro interminable, Systemd étant le superviseur de service de facto pour les distros Linux les plus importantes, il devient vite intéressant d’en apprendre les fonctionnalités un peu méconnues pour profiter pleinement de sa plateforme sans avoir à installer quoi que ce soit. Il m’arrive souvent d’avoir à créer des démons pour interroger tout type de bases de données ou systèmes distants. J’utilise généralement Python pour ces tâches, qui me permet de mettre en place ce que j’ai en tête très vite. Pour palier à l’effet ’amateur’ de la chose, j’utilise donc systemd qui va gérer les services de façon fiable, tout du moins plus fiable qu’un simple nohup ;)

Partant d’un inutile script pour l’exemple suivant:

from time import sleep

## Ici se trouvera ma procédure de connexion à la base de donnée ou 
# tout autre service distant à contacter d’abord

try:
  while True:
    print('Hello world!')
    sleep(5)
except KeyboardInterrupt:
     print('Exiting...')
finally:
# Ici se trouveront les fonctions de shutdown de mes connexions ouvertes plus haut
  pass

Comment rendre ce morceau de code plus robuste ? L’utilisation de Systemd sera déjà un bon pas en avant :)

La librairie sdnotify

Systemd prévoit des méthodes de notification entre la couche système et le programme démarré. Il s’agit là de laisser les programmes en question informer systemd de changements d’état, de son bon fonctionnement, etc…

La librairie python sdnotify permet d’implémenter facilement ces appels. Dans notre exemple aujourd’hui nous allons faire deux choses:

  1. Avertir Systemd que le programme a correctement démarré. Si au bout de 90 secondes, le message «je suis prêt» n’est pas arrivé, le script redémarrera automatiquement. Le message utilisé sera READY.
  2. Continuer à notifier Systemd que tout se passe bien pendant la durée de vie du programme. Si le temps configuré WatchdogSec dans le fichier service est dépassé sans nouvelle du programme, celui-ci sera terminé d’une balle entre les deux yeux. Le message utilisé sera WATCHDOG.

Pour installer sdnotify, pip peut être utilisé d’habitude. À noter que cette librairie est compatible python2 et python3.

pip install sdnotify

L’instanciation de sdnotify (à faire une seule fois), et l’envoi de notifications est des plus simples:

notifier = sdnotify.SystemdNotifier()
notifier.notify(MESSAGE)

Voici l’intégration de tout ceci dans notre code original:

from time import sleep

## Ici se trouvera ma procédure de connexion à la base de donnée ou 
# tout autre service distant à contacter d’abord

n = sdnotify.SystemdNotifier()
n.notify("READY=1")

try:
  while True:
    print('Hello world!')
    n.notify("WATCHDOG=1")
    sleep(5)
except KeyboardInterrupt:
     print('Exiting...')
finally:
# Ici se trouveront les fonctions de shutdown de mes connexions ouvertes plus haut
  pass

Côté systemd

Maintenant que notre script fait des choses merveilleuses, il est maintenant possible d’adapter un fichier service pour que Systemd soit à l’écoute de notre bébé.

[Unit]
Description=Python program

[Service]
# Note: setting PYTHONUNBUFFERED is necessary to see the output of this service in the journal
# See https://docs.python.org/2/using/cmdline.html#envvar-PYTHONUNBUFFERED
Environment=PYTHONUNBUFFERED=true

# Adjust this line to the correct path to test.py
ExecStart=/usr/bin/python /mon/script/python.py

Type=notify
WatchdogSec=30
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Voilà, Type=notify déclenchera la mise en écoute du programme dès son démarrage jusqu’à ce que le message READY=1 soit envoyé, et WatchdogSec mettra en place la surveillance continuelle du processus, qui devra envoyer toutes les maximum 30 secondes un message WATCHDOG=1 sous peine d’être tué. Dans tous les cas, le processus redémarrera toujours grâce à la commande Restart=on-failure.

Une fois que vous êtes à l’aise avec ces concepts, allez voir la documentation des appels Systemd sd_notify, car il en existe d’autre pas abordées ici qui vous seront peut-être utiles !