L'article qui suit est ma traduction d'un article du blog de Lennart Poettering, l'auteur de systemd. J'ai réalisé la traduction pour en faire une dépêche sur linuxfr. Je la stocke également ici au cas où et pour me souvenir de ce travail. Je publierai également les autres traductions dans des articles de blog distincts.

Voici un lien vers l'article original.

Comment puis-je convertir un script d'init SysV en fichier de service systemd ?

Traditionnellement, les services Unix et Linux (les démons) sont démarrés par des scripts d'init SysV. Il s'agit de scripts pour le shell Bourne qui résident généralement dans un répertoire tel que /etc/rc.d/init.d/ et qui, lorsqu'ils sont appelés avec un des quelques arguments standards (verbes) tels que start, stop ou restart, respectivement démarrent, arrêtent ou relancent le service en question. Pour le démarrage, cela implique généralement d'invoquer le binaire du démon qui forke un processus en tâche de fond (plus précisément, qui devient un démon). Les scripts shell ont tendance à être lents, assez lourds à lire, très verbeux et fragiles. Bien qu'ils soient immensément flexibles (après tout, ce n'est jamais que du code), certaines choses sont difficiles à réaliser avec des scripts shell comme mettre en ordre une exécution en parallèle, superviser correctement des processus ou encore simplement configurer les contextes d'exécution dans leurs moindres détails. Systemd fournit des éléments de compatibilité avec ces scripts shell mais, en raison des points négatifs invoqués précédemment, il est recommandé d'installer des fichiers de service Systemd pour l'ensemble des démons installés. De plus, en contraste avec les scripts d'init SysV qui ont besoin d'être adapté à la distribution, les fichiers de service systemd sont compatible avec n'importe quelle distribution exécutant systemd (ce qui arrive de plus en plus fréquemment ces derniers temps...). Dans ce qui suit, vous trouverez un guide succinct sur comment convertir un script d'init SysV en un fichier de service natif systemd. Dans l'idéal, les projets en amont devraient distribuer et installer des fichiers de service dans leur archives tar. Si vous avez converti avec succès un script SysV en suivant les recommandations, il serait de bon ton de soumettre un patch au projet amont. La préparation d'un tel patch sera discuté lors d'une prochaine session et il suffit de vous informer que la page de manuel de daemon(7), distribuée avec systemd contient de nombreuses informations utiles sur ce sujet.

Plongeons-nous dedans. À titre d’exemple, nous allons convertir le script d'init du démon ABRT en fichier de service systemd. ABRT est un composant standard de toute installation de Fedora et il s'agit d'un acronyme pour Automatic Bug Reporting Tool, ce qui décrit bien mieux ce dont il s'agit, à savoir, un service pour collecter les dumps de crash. Le fichier de script SysV est disponible ici.

La première étape à suivre pour convertir un tel script consiste simplement à le lire (!) et à récupérer l'information essentielle distillée tout au long de ce script volumineux. Dans la majorité des cas, le script est formé d'un ensemble de code qui est assez similaire dans tous les scripts d'init et il est généralement copié-collé d'un script à l'autre. Extrayons maintenant l'information essentielle du script ci-dessus:

  • Une chaîne de caractère qui décrit le service: "Daemon to detect crashing apps". Les commentaires de l'en-tête du script présentent plusieurs chaînes de description, certaines décrivant davantage le script d'init réalisant le démarrage du service que le service en lui-même. Les services Systemd ont également besoin d'une description et ils devraient décrire le service et non le fichier de service.
  • L'en-tête LSB[1] contient les informations de dépendance. Grâce à son architecture basée sur l'activation par socket, systemd n'a généralement pas besoin (ou un tout petit peu) de définir manuellement les dépendances. (Pour plus d'informations sur l’activation par socket lisez l'article originel du blog.) Dans ce cas, la dépendance vis-à-vis de $syslog (qui indique que arbtd a besoin d'un démon syslog) et la seule information d'intérêt. Même si l'en-tête indique une autre dépendance ($local_fs), celle-ci est redondante dans systemd car les services normaux systemd sont toujours démarrés lorsque tous les systèmes de fichiers locaux sont disponibles.
  • L'en-tête LSB suggère que ce service doit être démarré dans les runlevels 3 (multi-utilisateur) et 5 (graphique).
  • Le binaire du démon est /usr/sbin/abrtd.

Et c'est déjà tout ! Le reste de ce script de 115 lignes est simplement du code générique ou redondant: du code qui gère la synchronisation et l'ordonnancement du démarrage (du code qui gère les fichiers de lock) ou qui génère des messages d'état (le code qui appelle echo), ou simplement qui analyse les arguments (le gros bloc du case).

À partir de l'information extraite ci-dessus, nous pouvons maintenant écrire notre fichier de service systemd:

[Unit]
Description=Daemon to detect crashing apps
After=syslog.target

[Service]
ExecStart=/usr/sbin/abrtd
Type=forking

[Install]
WantedBy=multi-user.target

Un peu d'explications du contenu de ce fichier: la section [Unit] contient de l'information générique sur le service. Systemd gère des services systèmes mais également des périphériques, des points de montage, des timers, et d'autres composants du système. Le terme générique pour tous ces objets dans systemd est une Unit. La section [Unit] stocke l'information qui s'applique non seulement aux services mais également à tous les autres types d'Unit systemd. Dans notre cas, nous ajoutons les paramètre d'Unit suivants: nous ajoutons une chaîne de caractère de description et nous configurons que le démon doit être lancé après Syslog[2], de la même manière à ce qui est indiqué dans l'en-tête LSB du script d'init originel. Pour gérer cette dépendance à Syslog, nous créons une dépendance de type After= sur l'Unit systemd nommée syslog.target. Cette dernière est une Unit particulière et elle représente le nom standard pour l'implémentation de syslog dans systemd. Pour plus d'informations sur ces noms standardisés, consultez la page de manuel systemd.special(7). Notez qu'une dépendance du type After= représente seulement une suggestion d'ordre de démarrage; elle ne se traduit pas par le démarrage de syslog lorsque abrtd démarre. C'est exactement ce que nous voulons puisque arbtd fonctionne correctement, même lorsque syslog n'est pas disponible. Néanmoins, si les deux sont démarrés (c'est généralement le cas), alors l'ordre dans lequel ils sont lancés est contrôlé avec cette dépendance.

La section qui suit se nomme [Service] et elle s'occupe de l'information sur le service en lui-même. Elle contient tous les paramètres qui s'appliquent uniquement à des services et non aux autres type d'Unit systemd (points de montage, périphériques, timers, ...). Deux paramètres sont utilisés ici: ExecStart= qui stocke le chemin vers le binaire pour l'exécuter lorsque le service doit être démarré. Avec Type=, on configure la manière dont le service notifie le système d'init qu'il a terminé son démarrage. Étant donné que les démons traditionnels Unix le font en émettant un code de retour au processus parent après avoir forké et initialisé un démon en tâche de fond, nous utilisons le type forking ici. Cela indique à systemd d'attendre jusqu'à ce que le binaire de démarrage envoie un code retour et de gérer les processus qui tournent toujours après ceux du démon.

La dernière section est [Install]. Elle stocke l'information sur comment devrait être l'installation suggérée, c’est-à-dire, dans quelles circonstances et par quels déclencheurs le service devrait être démarré. Dans notre cas, nous indiquons simplement que le service doit être démarré lorsque l'Unit multi-user.target est activée. Il s'agit également d'une Unit spéciale qui prend globalement le rôle du Runlevel 3[3] de SysV. Le paramètre WantedBy= a peu d'effet sur le fonctionnement courant du démon. Il est uniquement lu par la commande systemctl enable qui est le moyen recommandé d'installer un service dans systemd. Cette commande s'assure simplement que notre service est automatiquement activé dès que l'Unit multi-user.target est requise, ce qui est le cas lors des séquences de boot normales[4].

Et c'est tout ! Nous avons maintenant un fichier de service systemd fonctionnel et simple. Pour le tester, il faut le copier dans /etc/systemd/system/abrtd.service et lancer systemctl daemon-reload. Cela permettra à systemd de prendre en compte notre fichier et dès cet instant, nous pouvons démarrer ce service en lançant: systemctl start abrtd.service. Nous pouvons en vérifier l'état grâce à systemctl status abrtd.service. Et nous pouvons arrêter le service via systemctl stop abrtd.service. Finalement, nous pouvons l'installer de manière à ce qu'il soit activé par défaut lors des prochains boots avec la commande systemctl enable abrtd.service.

Le fichier de service ci-dessus, bien que suffisant et représentant une traduction globale du script d'init SysV, peut encore être amélioré. En voici une petite mise à jour:

[Unit]
Description=ABRT Automated Bug Reporting Tool
After=syslog.target

[Service]
Type=dbus
BusName=com.redhat.abrt
ExecStart=/usr/sbin/abrtd -d -s

[Install]
WantedBy=multi-user.target

Voyons ce qui a changé. Deux choses: nous avons amélioré la description et, plus important, nous avons changé le type de service à dbus et configuré le nom D-Bus du service. Pourquoi avoir fait cela ? Comme déjà évoqué précédemment, les services classiques SysV deviennent des démons après le démarrage ce qui implique généralement un double fork et le fait de se détacher de tout terminal. Bien que cette approche soit utile et nécessaire lorsque les démons sont lancés par un script, elle est non nécessaire (et lente) ainsi que contre-productive lorsqu'un système efficace de gestion des processus tel que systemd est employé. La raison à cela est que le processus du démon qui a forké n'a généralement plus de lien avec le processus démarré par systemd (la procédure de transformation en démon consiste à supprimer ce lien), ce qui rend difficile pour systemd de distinguer, après le fork, dans les processus appartenant au service, lequel est le processus principal et quels sont les processus auxiliaires. Cette information est pourtant cruciale pour disposer d'une gestion de processus aux petits oignons, c’est-à-dire pour superviser un processus, le relancer automatiquement lors des arrêts anormaux, collecter l'information de crash et les codes de sortie. Pour faciliter la vie de systemd pour repérer quel est le processus principal, nous avons changé le type de service à dbus. La syntaxe de ce type de service est faîte pour tous les services qui prennent un nom sur le bus système D-Bus comme dernière étape de leur initialisation[5]. ABRT est un de ces services. Avec ce paramètre, systemd lancera le processus ABRT qui ne forkera plus (configuré via les options -d -s du démon) et systemd considérera le service comme complètement démarré dès que com.redhat.abrt apparaîtra sur le bus. Ainsi, le processus lancé par systemd sera le processus principal du démon et systemd dispose d'un moyen fiable pour vérifier si le démon est complètement démarré; systemd pourra le superviser plus facilement.

Et c'est tout ce qu'il y a besoin de faire. Nous avons un simple fichier de service systemd qui fournit plus d'information dans 10 lignes que le script SysV originel en 115 lignes. Dès maintenant, il reste encore une grande marge d'amélioration en utilisant plus de fonctionnalités de systemd. Par exemple, nous pourrions configurer Restart=restart-always pour indiquer à systemd de relancer automatiquement le service lorsque celui-ci échoue. Ou encore, nous pourrions utiliser OOMScoreAdjust=-500 pour demander au noyau de laisser ce processus lorsque OOM killer est employé. Ou bien, nous pourrions utiliser CPUSchedulingPolicy=idle pour s'assurer que les processus abrtd crashent en tâche de fond uniquement, en autorisant le noyau à donner sa préférence à tout ce qui fonctionne et qui a besoin de temps CPU.

Pour plus d'informations sur les options de configuration mentionnées ci-dessus, consultez les pages de manuelles respectives systemd.unit(5), systemd.service(5), systemd.exec(5). Ou bien, consultez toutes les pages de manuel systemd.

Bien sûr, tous les scripts SysV ne se convertissent pas aussi facilement que celui que nous avons étudié. Néanmoins une grande majorité devrait pouvoir l'être.

C'est tout pour aujourd'hui, à bientôt pour la prochaine session de cette série.

Fin de la traduction.