Lennart Poettering, l’initiateur du projet, a remis en cause l'un des fondements d'Unix.
L'objectif de cet article n'est aucunement tenter de convaincre le DevOps qui sommeille en chacun de nous que systemd est indispensable et qu'il serait une hérésie de s'en passer, mais plutôt de lister 5 cas d'utilisation de systemd qui peuvent être utiles lors d'un projet.
La configuration de systemd est décrite à l'aide de ce qu'on appelle des units : ce sont de simples fichiers dont la syntaxe s'approche des fameux fichiers INI, qui peuvent être placés à différents endroits (comme par exemple /usr/lib/systemd/system
et /etc/systemd/system
, mais d'autres chemins sont possibles) en fonction des cas.
Différents types d'unit existent : on trouve par exemple les services qui correspondent plus ou moins aux fichiers que l'on trouvait dans /etc/init.d
à l'époque d'init, les mounts et automounts que l'on peut assimiler à la fstab, etc.
Lorsque l'une de ces units est modifiée, il est nécessaire de notifier qu'une mise à jour doit être prise en compte par systemd à l'aide de la commande suivante : systemctl daemon-reload
.
Les bases étant posées, nous sommes prêt à rentrer dans le vif du sujet... Allons-y !
A l’instar de cron, systemd peut programmer des tâches récurrentes. Dans le jargon de systemd, on parle alors de timer.
Pour illustrer cette fonctionnalité, nous allons faire que systemd lance toutes les dix secondes une commande qui va écrire Bip!
dans un fichier /var/log/spoutnik.log
. Pour cela :
spoutnik.service
qui va définir le service de type oneshot
qui va écrire dans notre fichier de logspoutnik.timer
qui va décrire la fréquence à laquelle notre spoutnik.service
va être lancéL’avantage majeur que systemd a par rapport à cron est dû au fait que la commande que l'on lance est wrappée par un service : il est alors possible de profiter de la myriade d’options qui permettent de contrôler finement la manière de lancer nos commandes (user, niceness, etc.).
Il est possible de lister les différents timers à l'aide de la commande systemctl list-timers
(ce qui d'ailleurs de nous rendre compte que le nettoyage de /tmp
est programmé à l'aide de systemd au travers du service systemd-tmpfiles-clean.service
).
Notons au passage qu’il est également possible de différer l'exécution d’une commande “à la volée” à l'aide de l'utilitaire systemd-run
fourni avec systemd : systemd-run --on-active=10 /bin/bash -c "echo 'Bip!' >>'/var/log/spoutnik.log'"
.
systemd fournit un mécanisme assez puissant pour changer le comportement de unit déjà existantes : les drop-ins. Le principe est simple : dans /etc/systemd/system
, il faut créer un dossier ayant le nom du service suffixé par .d
et y placer des fichiers .conf contenant les déclarations à surcharger.
Pour l’exemple, nous allons à présent imaginer que notre service spoutnik.service
provient d’un package installé à l’aide de yum
ou apt-get
et qu'il va lancer un script spoutnik.sh
. La nuance est importante car :
spoutnik.service
va se trouver dans /usr/lib/systemd/system
et non plus dans /etc/systemd/system
spoutnik.service
ne va plus être de type oneshot
mais de type simple
, ce qui signifie que systemd lui-même va se charger de daemonizer notre script (pas besoin d'utilitaire de type daemonize
Spoutnik Inc. qui s'est chargé du packaging a bien fait les choses puisqu’il est à présent possible de configurer le message écrit dans /var/log/spoutnik.log
en utilisant la variable d’environnement SPOUTNIK_MESSAGE
.
L’idée à présent va être de créer de changer le comportement de notre spoutnik.service
sans pour autant alterer le fichier /usr/lib/systemd/system/spoutnik.service
(ça ne serait pas une solution pérenne puisqu'étant donné que le fichier est issu d'un package, il risque d'être réécrit lors d'une mise-à-jour, par exemple). Pour cela, nous allons créer un drop-in qui va assigner une autre valeur notre variable d’environnement SPOUTNIK_MESSAGE
en utilisant la directive Environment
et ainsi influer sur le message écrit par spoutnik.sh
.
Et voilà ! Petite astuce (qui peut éviter quelques migraines...) : si la valeur que l’on veut assigner à cette variable d’environnement contient des espaces, il est nécessaire d'utiliser l'utilitaire systemd-escape
. A quelques rares excéptions, cette règle est valable pour l'ensemble des directives de systemd.
Il est aisé de se rendre compte que, couplé à un outil de provisionning, ce mécanisme peut se révéler très pratique !
systemd possède en effet un ensemble de directives qui lui permet de relancer les services lorsque ces derniers tombent en erreur.
Pour l’exemple, imaginons la dernière mise-à-jour du script spoutnik.sh
n'est pas de la meilleure qualité, et qu'il a la facheuse tendance à s'arrêter de manière impromptue.
Voilà qui est embêtant... Pour résoudre ce problème et redémarrer automatiquement le service en cas d'erreur, nous allons créer un nouveau drop-in ha.conf
qui contient la directive Restart
Notons qu’il est possible d’aller beaucoup plus loin dans le contrôle de la manière dont systemd redémarre les services (temps d’attente avant le redémarrage, nombre maximal de tentatives, etc.) et dans quels cas il doit le faire (codes de retour particuliers, signaux, etc.).
En complément du redémarrage automatique présenté ci-dessus, il est possible d’utiliser la directive OnFailure
pour indiquer à systemd d'exécuter une action particulière lorsque le service tombe en erreur. A l'instar des tâches programmé à l'aide des timers, cette action est décrite sous la forme d’un service de type oneshot
.
Pour notre exemple, nous allons créer un service notify-hq@.service
(qui va lui-même appeler le script notify-hq.sh
qui se charger d'afficher des messages sur le TTY) qui sera lancé lorsque spoutnik.service
tombe en erreur, et ajouter la directive OnFailure
à notre drop-in ha.conf
.
Nous allons ici profiter des template units pour passer le message que l'on veut afficher comme paramètre du service.
Le fait d’afficher le nom des services qui tombent en erreur sur le TTY n’a qu’une valeur ajoutée très relative... Mais étant donné qu’il est possible d’utiliser n’importe quelle commande, on peut parfaitement imaginer un petit script Python qui va envoyer un message sur le channel #monotoring
du Slack de l'équipe à l’aide de la librairie Slacker.
Cette fonctionnalité de systemd est souvent connue, mais parfois mal comprise. Il est en effet possible de lier le comportement de plusieurs services entre eux, ce à l’aide des directives BindTo
et Wants
(ou Require
) et des directives After
et Before
.
Pour notre exemple, Spoutnik Inc. fourni à présent un utilitaire spoutnik-truncate.sh
accompagné de son spoutnik-truncate.service
, chargé de purger le fichier /var/log/spoutnik.log
si sa taille excède un certain nombre de lignes. Afin de ne pas perdre des cycles CPU inutilement, nous allons lier les services de manière à ce que spoutnik-truncate.service
démarre et s'arrête en même temps que spoutnik.service
.
Il suffit pour cela de créer deux drop-ins : l'un pour spoutnik.service
avec les directives Wants
et Before
, et l'autre pour spoutnik-truncate.service
avec la directive BindTo
.
Cette fonctionnalité peut par exemple être intéressante s’il on accompagne un service qui expose une API REST d'un autre service chargé de l'annoucement avec etcd
et ajouter de manière quasiment transparente une couche de discovery à notre projet.
Ces quelques astuces ne sont qu'un infime aperçu des fonctionnalités de systemd et de l'aide qu'il peut apporter sur un projet. Il est en effet par exemple possible de :
journalctl
systemd-nspawn
La documentation de systemd est ici (et accessible via votre commande man
préférée) et la mailing list est également une précieuse source d'information et d'inspiration. Enfin, pour les plus curieux, les sources de spoutnik.sh
et de ses amis spoutnik-truncate.sh
et notify-hq.sh
sont ici !