5 services que systemd m'a déjà rendu
En un peu plus de 7 ans d'existence, systemd s’est peu à peu imposé comme le remplaçant par défaut du vieillissant init. On le trouve en effet installé et activé par défaut sur les distributions de Linux les plus couramment utilisées : Ubuntu depuis la 15.04, CentOS depuis la 7, et j'en passe...
systemd a fait couler beaucoup d’encre sur les forum de la communauté des utilisateurs de Linux, à tel point qu’un fork de Debian sans systemd a même été lancé à la fin de l'année 2014. Les raisons de ces débats sont multiples et parfois justifiées. Mais sans faire l'avocat du diable, je pense que pour beaucoup de détracteurs, systemd a surtout été une cause d’incompréhension : 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.
Quelques petits rappels sur la configuration systemd
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 !
systemd peut remplacer la crontab
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 :
- Tout d'abord, nous allons créer un fichier
spoutnik.service
qui va définir le service de typeoneshot
qui va écrire dans notre fichier de log - Puis, nous allons créer un fichier
spoutnik.timer
qui va décrire la fréquence à laquelle notrespoutnik.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 vous permet de surcharger des déclarations en utilisant des drop-ins
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 :
- Par convention, le fichier
spoutnik.service
va se trouver dans/usr/lib/systemd/system
et non plus dans/etc/systemd/system
- Notre
spoutnik.service
ne va plus être de typeoneshot
mais de typesimple
, ce qui signifie que systemd lui-même va se charger de daemonizer notre script (pas besoin d'utilitaire de typedaemonize
- Plus besoin de timer puisque le script se charge lui-même d'écrire à une fréquence donnée
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 peut redémarrer automatiquement des services
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.).
systemd peut notifier lorsqu'un service tombe en erreur
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.
systemd peut lier plusieurs services
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.
Pour aller plus loin
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 :
- Contrôler des services depuis une machine distante
- Avoir un suivi poussé des logs à l'aide de
journalctl
- Lancer des containers à l'aide de
systemd-nspawn
- Profiter d'un monitoring assez poussé en se basant sur l'interface DBus
- Etc.
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 !