Publié le: 2011-11-08

Migrate from Init to SystemD

SystemD est une alternative très intéressant à Init. Pourquoi ? Init est la base de toute architecture Linux/UNIX me direz vous, il est le coeur reliant tout le système au noyau.

Le soucis d’Init c’est qu’il est ancien et monotâche dirons nous. En effet, il traite de façon séquentielle les ordres. Une fois Init chargé, il va lancer les différents services et ainsi permettre le démarrage complet du système et des services associés. Pour se faire, il se base sur un arbre de dépendances défini sous Debian dans le dossier /etc/rc.d/, et ensuite lance séquentiellement les services.

Mais pourquoi on les lance un à un, après tout on pourrait lancer le service openLDAP en même temps que la base de données MySQL ou encore le serveur Apache en même temps que le service SSH, par exemple. SystemD permet déjà d’améliorer ceci en se basant sur son propre arbre et ses dépendances afin de lancer simultanément plusieurs services, en se basant non plus sur un ordre séquentiel mais sur des niveaux d’arborescences. Ainsi si syslog est nécessité par l’ensemble des services, il sera lancé en premier puis suivront dbus et avahi par exemple et ensuite openLDAP, MySQL et SSH.

Article externe (source)

Je vous copie ci-dessous un article très intéressant écrit par la communauté de Linux-FR.org explicant en détail le système.

État des lieux

Processus de PID 1

Sur un système UNIX, le tout premier processus (PID 1) a un rôle particulier :

  1. c’est le seul qui est lancé par le noyau ; il doit donc assurer toute l’initialisation du système, le lancement des différents services et des systèmes permettant la connexion des utilisateurs ;
  2. dans la hiérarchie des processus tournant sur le système, lorsqu’un processus devient orphelin, la norme et l’usage veut qu’il soit attaché au processus de PID 1. Dans ce cas lorsque le processus se termine, c’est au PID 1 qu’il revient de lire le code de retour du processus zombie, avant que le système puisse libérer certaines ressources associées.

L’évolution de l’informatique fait que le système évolue au fil du temps (ajout ou suppression de périphériques, lancement ou arrêt de services). Nous avons aussi des plantages de services ou des mises à jour.

SysVinit

Sous GNU/Linux, c’est généralement SysVinit, le système d’initialisation hérité d’UNIX Système V qui assure ce rôle, remplacé par Upstart dans plusieurs distributions.

Hélas, SysVinit reste très basique, peu performant et sujet à de nombreux problèmes. Il n’est pas très adapté à toutes les évolutions modernes que connaît Linux.

Changement dynamique du matériel et du logiciel

Pour l’instant, ces modifications du système sont réalisées par des scripts lancés dynamiquement, généralement par root ou par un autre service (udev) ; mais si un système moderne était chargé de maintenir le système opérationnel, il devrait aussi s’occuper de ces modifications.

Le fichier « /etc/inittab » permet historiquement d’assurer une forme basique de suivi des services et de définir plusieurs modes de fonctionnement ; mais dans les distributions actuelles, il est terriblement en perte de vitesse, toute la charge fonctionnelle étant déplacée dans les scripts SysVinit.

Performances

Lennart Poettering constate que le système d’initialisation SysV est vieux et lent. Pour améliorer les choses, il faudrait :

  • démarrer moins de choses ;
  • démarrer les choses en parallèle lorsque c’est possible.

Pour démarrer moins de choses, une solution éprouvée est de retarder le démarrage de certains services jusqu’au moment où ils sont effectivement nécessaires (démarrage à la demande). Par exemple, bluetoothd est inutile tant qu’aucun périphérique Bluetooth n’est présent et qu’aucune application ne s’adresse à lui via D-Bus. Pareillement, CUPS ne sert que pour configurer une imprimante ou pour imprimer, actions qui surviennent rarement dans les premières minutes de démarrage d’un système. Même SSH pourrait n’être démarré qu’au moment où arrive la première connexion entrante.

Pour démarrer les services en parallèle, il est nécessaire de tout lancer en même temps, et non de démarrer les services un à un comme le fait SysVinit.

Quasiment tous les systèmes qui tentent de paralléliser le démarrage de plusieurs services se servent d’un arbre de dépendances permettant d’imposer le lancement des services indispensables au démarrage d’un service donné. Avahi nécessite D-Bus, donc le système démarre D-Bus et attend qu’il soit prêt avant de lancer Avahi.

Paralléliser le lancement des services sur sockets (AF_UNIX)

AB (B est dépendant de A)

Cette approche des dépendances est coûteuse en temps, et contournable. Lorsqu’une dépendance existe, le second service (B) n’a pas réellement besoin que l’initialisation du premier service (A) soit terminée pour commencer à démarrer (charger son code en mémoire vive, lire son fichier de configuration…).

En poussant le raisonnement plus loin, la seule chose nécessaire pour ne pas provoquer d’erreurs lors du lancement du second service (B) est que l’interface du premier (A) soit en place et capable de temporiser les requêtes reçues. Le second service (B) pourra alors s’y connecter sans recevoir d’erreur. Le premier service (A) traitera les requêtes lorsqu’il sera prêt.

Dans le cas de services répondant sur des sockets UNIX (AF_UNIX, présents comme des fichiers de type « s » sur le disque), la simple existence du socket est suffisante. Le noyau s’assure que les données reçues sont mises en attente ou que l’émetteur est bloqué tant qu’elles ne sont pas lues. Les services utilisateurs du démon de journalisation d’événements syslog ont besoin de se connecter à un socket appelé « /dev/log », ceux de CUPS utilisent « /var/run/cups/cups.sock », etc. Si les sockets correspondants existent, alors il est possible de lancer toutes les dépendances, et la synchronisation se fera toute seule au fur et à mesure que les services seront prêts et répondront aux requêtes reçues.

Sur un système UNIX où les entrées‐sorties sont assimilées à des fichiers, il est très facile de créer ces sockets avant même de lancer le service associé.

Supprimer les dépendances explicites entre les services

Si la création des sockets est indépendante du lancement des services, le problème de dépendances disparaît. Dans une première phase, nous pourrions créer tous les sockets nécessaires au système, et dans un second temps, lancer tous les services en parallèle.

Prenons l’exemple de Syslog. Dès l’instant où « /dev/log » est créé, il est possible de lancer des services qui vont envoyer leurs événements dans « /dev/log ». Ces messages seront mis en attente tant que Syslog ne sera pas prêt. Dès qu’il aura démarré, Syslog lira tous les messages reçus et assurera leur traitement.

Démarrer moins de choses

Ce principe permet également de faire du démarrage à la demande. Si personne ne s’adresse à un service, il est inutile de le lancer. À l’instar d’inetd (et de son remplaçant xinetd) ; nous pouvons attendre qu’un service ait reçu une demande de connexion pour le lancer. Pour les postes de travail qui impriment trois pages par semaine, CUPS est un bon candidat. Tant qu’aucune impression n’est lancée, aucune donnée n’arrive et le service CUPS n’a pas de raison d’être démarré.

Un système plus résistant

Nous trouvons un nouvel avantage à ce système de démarrage : une certaine forme de résistance aux crashs d’un service. Si votre service syslog se plante lamentablement, les données qui arrivent dans « /dev/log » sont mises en attente par le noyau. Lorsque syslog est relancé, aucun message n’est perdu (si ce n’est ceux qu’il a déjà lus et perdus lors de son crash), même si le temps de démarrage est important.

De la même façon, en cas de redémarrage d’un service, par exemple suite à une mise à jour, aucun message n’est perdu.

Services réseau (AF_INET, AF_INET6 et D-Bus)

Sur un système moderne, les services ne se limitent pas à un socket, ils reçoivent des connexions via le réseau IP ou via D-Bus. Est‐ce que ce mécanisme est aussi adaptable à ce type d’interface ? La réponse est oui.

D-Bus permet un mode « bus-activation » où un service est démarré par la réception d’un message lui étant destiné.

Concernant le réseau IP, c’est exactement ce que propose inetd et ses successeurs depuis des années (en mode « wait »). Le socket réseau est créé par inetd. À la réception de données, le service est lancé déjà connecté au socket. La contrainte est que le service doit être capable de fonctionner sur un socket réseau déjà créé.

Résumé et Interlude

Par ce mécanisme, il est possible de démarrer tous les services en parallèle, sans aucun mécanisme de synchronisation. Il permet également de démarrer les services à la demande et non plus systématiquement. Ce système n’est pas nouveau, nous avons vu qu’inetd présentait déjà des aspects similaires. Apple a développé tous les points ci‐dessus dans son système Mac OS X, sous le nom de launchd. C’est cela qui permet à Mac OS X de démarrer très rapidement.

Les systèmes de fichiers

Le démarrage du système ne se limite pas au démarrage des services. La préparation des systèmes de fichiers est également une opération qui peut prendre beaucoup de temps. Il faut attendre que tous les périphériques listés dans « /etc/fstab » soient créés, que chaque système de fichiers soit vérifié avec fsck, que le système soit monté et éventuellement que les quotas soient contrôlés.

Pour accélérer cela, Harald Hoyer a eu l’idée d’utiliser le système autofs en mode direct. De la même façon que pour les sockets, autofs permet de monter un pseudo système de fichiers à la place du système réel, et de ne basculer sur le vrai système qu’au moment où un accès est fait. Comme pour la création de sockets, le montage du pseudo système de fichiers est quasiment instantané.

Si un accès est fait alors que le système de fichiers n’est pas prêt (fsck en cours, par exemple), alors le noyau bloque la requête (et celui qui l’a faite) jusqu’à ce que le système de fichiers soit disponible.

Pour la racine (« / »), cela ne présente pas d’intérêt ; mais pour les grosses volumétries qui peuvent être montées sur « /home », ou pour des partitions de données qui ne sont pas nécessaires au démarrage du système, cela présente un gain important.

L’intégration de autofs dans un système de démarrage peut faire peur à plus d’une personne. L’expérience montre que les processus bloqués par des fichiers qui ne sont pas disponibles peuvent être tués proprement, et qu’en cas d’erreur lors de la vérification du système de fichiers, il est possible de retourner une erreur au processus réclamant le fichier, de la même façon que cela se produit lorsqu’une partition tombe en erreur.

Gains fonctionnels

Avoir un petit PID utilisable

Les scripts sont rapides à coder, mais lents à exécuter. Tout le système de démarrage SysVinit est basé sur des scripts qui lancent des commandes externes. D’après Lennart cette approche est condamnée à être lente. Sur son système, les scripts dans « /etc/init.d/ » appellent grep au moins 77 fois, awk l’est 92 fois, cut, 23 fois, et sed, 74 fois. À chaque appel, un processus doit être démarré, les bibliothèques résolues, la localisation initialisée, etc.. Après une manipulation de quelques octets, les données doivent être retournées à l’appelant via un descripteur de fichier et le processus est détruit. En outre, les scripts sont très sensibles à l’environnement et peu adaptés à la gestion des erreurs et des exceptions.

Tous ces scripts, ne faisant pour beaucoup que des tâches triviales, et contenant beaucoup de code dupliqué, auraient plutôt leur place dans le processus d’initialisation ou dans l’un de ses sous‐programmes. Tout ré‐écrire en C n’est pas pertinent ; mais le système devrait être capable de gérer bien plus de choses par lui‐même, sans faire appel à des scripts.

Une métrique intéressante est le PID du premier processus utilisable une fois le système démarré. Avec les noyaux actuels, cela donne une bonne idée du nombre de processus lancés. Amorcer le système, lancer un terminal, faire « echo $$ » : sous Linux, le PID est 1823, sous Mac OS X c’est 154.

Suivre les processus

Une grosse part du mécanisme qui démarre les services devrait être la supervision : surveiller les services, pouvoir les identifier, redémarrer ceux qui plantent, collecter les traces.

Il devrait être capable de couper complètement un service. Cela semble facile mais est en réalité très difficile. Sous UNIX, un processus qui fait un double fork (le programme se relance lui‐même en tâche de fond) sort complètement de la supervision du processus qui l’a lancé. Prenons par exemple un script CGI lancé par un serveur Web. Lorsqu’il fait un double fork, il devient orphelin et est rattaché au processus de PID 1. L’arrêt du serveur Web ne le concerne pas, et aucun script système n’est en mesure de le retrouver automatiquement pour le couper.

Pour résoudre ce problème, le noyau Linux implémente un mécanisme appelé cgroups (control groups) qui, comme son nom l’indique, permet de créer un groupe et d’y placer des processus. Tout processus créé au sein d’un cgroup y reste, résolvant ainsi notre problème de CGI fou. Les cgroups présentent un autre avantage (c’était leur objectif initial) : il est possible de placer certaines restrictions (processeur, mémoire, entrées‐sorties) afin de limiter la consommation d’un service dans sa globalité.

Contrôler le contexte du service

Un bon superviseur devrait également être capable de placer le service dans un environnement adapté et sécurisé. Jetez un coup d’œil aux scripts dans « /etc/init.d/ », tout le code dupliqué concerne le traitement des options, le changement d’utilisateur et de groupe, le réglage des paramètres de sécurité, la création d’un environnement avec une racine isolée (à l’aide de chroot), la création d’un fichier contenant le PID, retrouver le processus pour lui envoyer un signal. Le même code, légèrement différent, se répète de script en script. Tout cela gagnerait à être normalisé et mutualisé.

Le système devrait permettre de régler les limites de ressources, la priorité, l’utilisateur et le groupe. Cela ne s’arrête pas là, le noyau Linux permet aussi de définir les capacités, les règles d’ordonnancement et d’entrées‐sorties, l’affinité avec les processeurs, supprimer l’accès en écriture à certaines partitions, et aussi tous les paramètres liés aux cgroups.

Enfin, sous UNIX, tout processus a une sortie standard et une sortie d’erreur, qui sont la première source d’information en cas d’anomalie. Aujourd’hui, sur la majorité des distributions, ces données sont difficiles à récupérer, le système de démarrage devrait au contraire les conserver précieusement ; au moins en les redirigeant vers le service de journalisation des événements.

Second interlude : Upstart et les autres systèmes

Upstart est une grosse avancée par rapport à SysVinit. Il s’occupe du démarrage et de l’arrêt des services en fonction d’événements. Il gère tout un arbre de dépendances pour déterminer quoi démarrer et dans quel ordre, afin d’arriver à une situation cible. Malheureusement, cette gestion de dépendances demande de coder manuellement toutes les contraintes d’un service. Cela a plusieurs effets pervers :

  • en cas d’anomalie, bien malin est celui qui retrouvera ce qui s’est produit et dans quel ordre ;
  • le travail du système de démarrage a considérablement augmenté, puisqu’avant de démarrer quelque chose, il doit avoir constitué au moins une partie du graphe de dépendances ;
  • il ne permet pas de réduire le démarrage aux services strictement nécessaires. Si un service (A) peut être utilisé par un autre (B), alors il doit être marqué comme dépendant et démarré avant le second (AB).

Lennart admet que certains de ces points sont en cours de correction, mais il s’agit de contournements du principe de fonctionnement d’Upstart. En dehors de SysVinit, Upstart et launchd (Mac OS X), un autre système mérite notre intérêt, c’est Solaris SMF. Il gère les dépendances entre les services, mais est très lié à Solaris et très complexe dans son fonctionnement (par exemple par son utilisation excessive de XML).

Systemd

L’implémentation

Ce qui a été décrit ci‐dessus est implémenté dans un système appelé systemd qui est en cours d’adoption par plusieurs distributions GNU/Linux.

Le fonctionnement de systemd est basé sur des unités (units) qui ont un nom et un type. Le nom correspond au nom du fichier décrivant l’unité.

Les types sont :

  • service : les plus simples correspondent à des services. Peuvent être démarrés, coupés, redémarrés, reconfigurés (start/stop/restart/reload), comme on le fait habituellement avec les scripts dans /etc/init.d/ ;
  • socket : correspond à une socket, que ce soit de type UNIX, de type Internet, de type fichier ; chaque socket a un service correspondant ;
  • mount : un système de fichiers ; ces unités viennent en plus du fichier /etc/fstab qui est utilisé directement ;
  • swap : pour les partitions d’échange ;
  • automount : un système de fichiers monté à la demande ;
  • target : une macro‐unité qui permet de grouper plusieurs unités, par exemple multi-user.target ou bluetooth.target ;
  • snapshot : comme target, ce sont des unités qui peuvent être utilisées pour sauver l’état actuel des services et les restaurer par la suite, par exemple avant de passer en veille ;
  • device : un périphérique dans le système Linux ; peut être une source de dépendance via udev ;
  • timer : activations à la sauce cron, en fonction de la date ;
  • path : sert pour l’activation basée sur la présence ou la création de fichiers ou répertoires.

Toutes ces unités peuvent avoir des dépendances (Requires) et des conflits (Conflicts) déclarés de façon explicite.

Les bonus

  • Pour chaque service, il est possible de régler de nombreux paramètres : limites, répertoire racine (chroot), umask, priorité, paramètre « OOM killer » (out of memory), les classes et les priorités d’ordonnancement et d’entrées‐sorties, l’affinité processeur, l’utilisateur, les groupes, les répertoires accessibles et les modes d’accès, les capacités, le répertoire temporaire, le réglage des cgroups
  • Les flux stdout et stderr peuvent être redirigés vers le service de journalisation, vers « /dev/kmsg », vers un terminal ou vers un fichier.
  • Intégration de SELinux, des « TCP wrappers », des modules d’authentification PAM.
  • Chaque service a son propre cgroup.
  • Chaque connexion via un terminal est aussi placée dans son propre cgroup, ce qui permet de fermer complètement une session et d’isoler un peu mieux les utilisateurs (processeur, mémoire).
  • La syntaxe des fichiers de configuration suit celle bien connue des fichiers « .desktop ».
  • Le système est compatible avec les anciens scripts SysVinit. Les informations LSB et chkconfig (de Red Hat) ainsi que celles de Debian et d’OpenSUSE sont utilisées lorsqu’elles sont disponibles. Sinon, l’ordre de démarrage dans « /etc/rc.d/ » est utilisé. systemd est également capable de lire le fichier PID pour retrouver un service. Ces données permettent d’émuler une configuration classique SysVinit, facilitant la transition.
  • Le fichier « /etc/fstab » est également lu comme s’il s’agissait d’un fichier de configuration de systemd.
  • Les fichiers natifs systemd masquent ces émulations, permettant de faire des paquets compatibles avec les deux systèmes, le nouveau masquant l’ancien si nécessaire.
  • Il existe un système de modèles pour éviter de dupliquer une configuration (par exemple, pour les 6 getty des terminaux virtuels habituellement lancés).
  • systemd fourni une couche de compatibilité avec « /dev/initctl » permettant d’utiliser les vieux binaires, tels que shutdown ou reboot. Les ordres sont transmis à systemd via D-Bus. Il prend en charge également kexec qui permet de relancer le noyau Linux.
  • Il gère utmp et wtmp, les journaux système enregistrant les connexions.
  • Chaque processus lancé est tracé par son heure de démarrage, de fin, son PID et son code de retour ; permettant d’analyser les anomalies.
  • systemd est capable de se ré‐exécuter lui‐même, afin de faire une mise à jour, ou de passer d’un processus provenant d’un « RAM‐disque » d’amorçage (initrd) au système final. Tout l’état courant est migré d’un processus à l’autre.
  • La mise en place des systèmes de fichiers virtuels est directement prise en charge par systemd (/proc, /sys, /dev), binfmt_misc et HugeTBLfs sont gérés via autofs, ce qui évite d’avoir à les configurer en root ou à charger le module correspondant.
  • De nombreuses petites fonctions sont prises en charge directement sans passer par le lancement d’un script : définir le nom de la machine, sauver / restaurer les données aléatoires, initialiser la localisation, nettoyer les répertoires temporaires, configurer la console, le clavier, charger des modules. Cela a pour conséquence d’unifier ces fichiers de configuration sur toutes les distributions utilisant systemd.
  • Il existe un mode de démarrage interactif qui permet de confirmer chaque lancement de service en passant au noyau le paramètre « systemd.confirm_spawn=1 ».
  • systemd gère le readahead qui permet d’optimiser les lectures disque au démarrage en pré‐chargeant les blocs qui vont être nécessaires par la suite (en se basant sur un enregistrement d’un démarrage précédent).

L’administration

L’administration se fait par l’intermédiaire de quelques commandes dédiées et quelques fichiers de configuration qui sont documentés via une vingtaine de pages de manuel. Un certain nombre de pages liées à l’utilisation de systemd sont également modifiées.

Définition d’un service

Lorsque l’on regarde les scripts d’initialisation d’un service contenu dans le dossier « /etc/init.d », on constate que la plupart des scripts se ressemblent. L’information utile (programme à lancer, description du service, dépendances, niveaux de lancement) est complètement perdue dans le bruit généré par le code de gestion (création d’un fichier PID, s’assurer que le programme ne tourne pas déjà, gestion des cas d’utilisation…).

Systemd intègre toutes ces notions redondantes et permet à l’administrateur de définir un service de façon simple.

[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

Ici, est défini le service ABRT. Comme on le voit, la définition est assez simple :

  • le service doit être lancé après syslog ;
  • il s’agit d’un service D-Bus répondant au nom « com.redhat.abrt » ;
  • la commande à exécuter est : « /usr/sbin/abrtd -d -s » ;
  • le service doit être exécuté lorsqu’on choisi un environnement cible multi‐utilisateur (le mode de fonctionnement habituel).

Regardons un autre exemple ressemblant un peu plus à ce que l’on a l’habitude de voir avec les scripts SysVinit :

[Unit] Description=Command Scheduler After=syslog.target [Service] EnvironmentFile=/etc/sysconfig/crond ExecStart=/usr/sbin/crond -n $CRONDARGS [Install] WantedBy=multi-user.target

Il est toujours possible de définir un fichier contenant les paramètres à passer au service :

$ cat /etc/sysconfig/crond ## Settings for the CRON daemon. ## CRONDARGS= : any extra command-line startup arguments for crond CRONDARGS=

Les fichiers de définition peuvent être trouvés dans les dossiers « /etc/systemd/ » et « /lib/systemd/ ». Le premier correspond à la configuration faite par l’administrateur, et le second aux fichiers livrés par la distribution.

Gestion des services

Avec systemd, la gestion des services se fait maintenant par l’utilitaire systemctl.

Grâce à ce dernier, il est possible de lister toutes les unités présentes sur le système. Les commandes suivantes sont exécutées sur une distribution Fedora 15 :

$ systemctl list-units NIT LOAD ACTIVE SUB JOB DESCRIPTION
dev-hugepages.automount loaded active waiting Huge Pages File System Automount Point
dev-mqueue.automount loaded active waiting POSIX Message Queue File System Automount Point
proc-sys...misc.automount loaded active running Arbitrary Executable File Formats File System Automount Point
sys-kern...ebug.automount loaded active waiting Debug File System Automount Point
sys-kern...rity.automount loaded active waiting Security File System Automount Point
sys-devi...d-card0.device loaded active plugged N10/ICH 7 Family High Definition Audio Controller
sys-devi...et-eth0.device loaded active plugged BCM4313 802.11b/g LP-PHY
sys-devi...net-em2.device loaded active plugged RTL8101E/RTL8102E PCI Express Fast Ethernet controller
sys-devi...da-sda1.device loaded active plugged Hitachi_HTS725025A9A364
-.mount loaded active mounted /
boot.mount loaded active mounted /boot
home-spack-.gvfs.mount loaded active mounted /home/spack/.gvfs
home.mount loaded active mounted /home
media.mount loaded active mounted Media Directory
mcelog.service loaded active running Machine Check Exception Logging Daemon
mdmonitor.service loaded active running LSB: Start and stop the MD software RAID monitor
netfs.service loaded active exited LSB: Mount and unmount network filesystems.
NetworkManager.service loaded active running Network Manager
nfslock.service loaded active running LSB: Start up the NFS file locking sevice
ntpd.service loaded active running Network Time Service ntpdate.service loaded failed failed Set time via NTP
udev.service loaded active running udev Kernel Device Manager
dbus.socket loaded active running D-Bus System Message Bus Socket syslog.socket loaded active running Syslog Socket systemd-initctl.socket loaded active listening /dev/initctl Compatibility Named Pipe
systemd-logger.socket loaded active running Stdio Syslog Bridge Socket
systemd-shutdownd.socket loaded active listening Delayed Shutdown Socket
udev.socket loaded active running udev Kernel Device Manager Socket
sockets.target loaded active active Sockets sound.target loaded active active Sound Card
swap.target loaded active active Swap sysinit.target loaded active active System Initialization
syslog.target loaded active active Syslog
time-sync.target loaded active active System Time Synchronized
systemd-...ead-done.timer loaded active elapsed Stop Read-Ahead Data Collection 10s After Completed Startup
systemd-...es-clean.timer loaded active waiting Daily Cleanup of Temporary Directories

L’affichage a été réduit mais présente les différents éléments pris en compte par systemd. On retrouve donc les différentes unités précédemment listées.

Vous constaterez que le service « ntpdate.service » a échoué. Pour en savoir plus, nous pouvons utiliser la commande systemctl suivie de l’argument « status » et du nom de notre service :

$ systemctl status ntpdate.service ntpdate.service - Set time via NTP Loaded: loaded (/lib/systemd/system/ntpdate.service) Active: failed since Mon, 01 Aug 2011 16:01:49 +0200; 1h 58min ago Process: 741 ExecStart=/etc/init.d/ntpdate start (code=exited, status=1/FAILURE) CGroup: name=systemd:/system/ntpdate.service

On voit alors la commande exécutée et le code de retour renvoyé par le service : 1. Un coup d’œil au fichier « /var/log/messages » nous en dira plus :

# grep ntpdate /var/log/messages Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 0.fedora.pool.ntp.org: Name or service not known (-2) Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 1.fedora.pool.ntp.org: Name or service not known (-2) Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 2.fedora.pool.ntp.org: Name or service not known (-2) Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 3.fedora.pool.ntp.org: Name or service not known (-2) Aug 1 16:01:49 spiro ntpdate[1654]: no servers can be used, exiting Aug 1 16:01:49 spiro systemd[1]: ntpdate.service: main process exited, code=exited, status=1 Aug 1 16:01:49 spiro systemd[1]: Unit ntpdate.service entered failed state.

À l’aide de systemctl, il est bien sûr possible de démarrer, stopper et redémarrer les services. Voici, par exemple, comment l’on redémarre le service de prise en charge du Bluetooth :

# systemctl restart bluetooth.service

cgroups

Nous avons vu que systemd place chaque service dans un groupe distinct. Il serait donc intéressant d’identifier les différents groupes et les processus qu’ils contiennent. Pour cela, deux possibilités. Utiliser l’utilitaire ps ou l’outil maison de systemd : « systemd-cgls ».

$ ps waxf -eo 'user,pid,cgroup,command' USER PID CGROUP COMMAND root 1 name=systemd:/system /sbin/init root 367 cpu:/system/systemd-logger. /lib/systemd/systemd-logger root 390 cpu:/system/udev.service;na /sbin/udevd root 544 cpu:/system/udev.service;na _ /sbin/udevd root 1450 cpu:/system/udev.service;na _ /sbin/udevd root 685 cpu:/system/abrtd.service;n /usr/sbin/abrtd -d -s root 694 cpu:/system/crond.service;n /usr/sbin/crond -n root 717 cpu:/system/rsyslog.service /sbin/rsyslogd -n -c 5 root 1086 cpu:/system/autofs.service; automount --pid-file /var/run/autofs.pid root 1127 cpu:/system/prefdm.service; /usr/sbin/gdm-binary -nodaemon root 1135 cpu:/system/prefdm.service; _ /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 root 1139 cpu:/system/prefdm.service; _ /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-GWn7Er/database -n root 1352 name=systemd:/user/spack/1 _ pam: gdm-password spack 1374 name=systemd:/user/spack/1 _ gnome-session spack 1526 name=systemd:/user/spack/1 _ /usr/libexec/gnome-settings-daemon spack 1562 name=systemd:/user/spack/1 _ /usr/bin/gnome-shell spack 1786 name=systemd:/user/spack/1 | _ gnome-terminal spack 1791 name=systemd:/user/spack/1 | _ gnome-pty-helper spack 1792 name=systemd:/user/spack/1 | _ bash spack 1810 name=systemd:/user/spack/1 | _ bash spack 1823 name=systemd:/user/spack/1 | _ ps waxf -eo user,pid,cgroup,command spack 1569 name=systemd:/user/spack/1 _ gnome-power-manager spack 1574 name=systemd:/user/spack/1 _ nm-applet spack 1575 name=systemd:/user/spack/1 _ abrt-applet spack 1578 name=systemd:/user/spack/1 _ gnome-screensaver root 1151 cpu:/system/getty@.service/ /sbin/agetty tty2 38400 root 1152 cpu:/system/getty@.service/ /sbin/agetty tty6 38400 root 1155 cpu:/system/getty@.service/ /sbin/agetty tty4 38400 root 1159 cpu:/system/getty@.service/ /sbin/agetty tty3 38400 root 1160 cpu:/system/getty@.service/ /sbin/agetty tty5 38400
$ systemd-cgls ├ user │ └ spack │ └ 1 │ ├ 1352 pam: gdm-password │ ├ 1374 gnome-session │ ├ 1383 dbus-launch --sh-syntax --exit-with-session │ ├ 1384 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session │ ├ 1445 /usr/libexec/gvfsd │ ├ 1521 /usr/libexec/gconfd-2 │ ├ 1526 /usr/libexec/gnome-settings-daemon │ ├ 1529 /usr/bin/pulseaudio --start │ ├ 1562 /usr/bin/gnome-shell │ ├ 1569 gnome-power-manager │ ├ 1574 nm-applet │ ├ 1575 abrt-applet │ ├ 1578 gnome-screensaver │ ├ 1583 /usr/libexec/dconf-service │ ├ 1786 gnome-terminal │ ├ 1791 gnome-pty-helper │ ├ 1792 bash │ └ 1809 systemd-cgls └ system ├ 1 /sbin/init ├ getty@.service │ ├ tty5 │ │ └ 1160 /sbin/agetty tty5 38400 │ ├ tty3 │ │ └ 1159 /sbin/agetty tty3 38400 │ ├ tty4 │ │ └ 1155 /sbin/agetty tty4 38400 │ ├ tty6 │ │ └ 1152 /sbin/agetty tty6 38400 │ └ tty2 │ └ 1151 /sbin/agetty tty2 38400 ├ prefdm.service │ ├ 1127 /usr/sbin/gdm-binary -nodaemon │ ├ 1135 /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 │ ├ 1139 /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-GWn7Er/database -nolisten tcp vt1 │ └ 1363 /usr/bin/gnome-keyring-daemon --daemonize --login ├ autofs.service │ └ 1086 automount --pid-file /var/run/autofs.pid ├ dbus.service │ ├ 707 /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation │ ├ 720 /usr/libexec/polkit-1/polkitd │ ├ 750 /usr/sbin/modem-manager │ ├ 816 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplic... │ ├ 1037 /usr/libexec/colord │ ├ 1282 /usr/libexec/udisks-daemon │ ├ 1283 udisks-daemon: not polling any devices │ ├ 1288 /usr/libexec/upowerd │ └ 1559 /usr/libexec/packagekitd ├ crond.service │ └ 694 /usr/sbin/crond -n ├ rsyslog.service │ └ 717 /sbin/rsyslogd -n -c 5 ├ abrtd.service │ └ 685 /usr/sbin/abrtd -d -s ├ home.mount ├ boot.mount ├ fsck@.service ├ udev.service │ ├ 390 /sbin/udevd │ ├ 544 /sbin/udevd │ └ 1450 /sbin/udevd ├ systemd-logger.service │ └ 367 /lib/systemd/systemd-logger └ media.mount

Vous l’aurez constaté, systemd gère non seulement les ressources du système, mais aussi celles des utilisateurs.

Écrire un service

L’utilisation de systemd simplifie un certain nombre de choses dans l’écriture d’un service :

  • il n’est plus nécessaire de se relancer pour passer en arrière‐plan (fork), et encore moins de le faire deux fois (double‐fork) ;
  • il n’est plus nécessaire d’implémenter de mécanisme pour changer d’utilisateur, pour modifier les privilèges, pour faire un chroot ; tout cela étant pris en charge par systemd ;
  • avec les cgroups, il n’est plus nécessaire d’écrire un fichier PID permettant de retrouver le service via son identifiant de processus ;
  • pour les cas simples, il n’est plus nécessaire de mettre en place une interface vers le service de journalisation des événements, les sorties classiques étant gérées comme des journaux (cela ne permet toutefois pas de spécifier la sévérité) ;
  • il n’est plus nécessaire de configurer et de créer les sockets.

Ces recommandations sont très semblables à celles d’Apple pour utiliser launchd. Pour les services écrits de façon classique, systemd dispose de mécanismes de compatibilité afin de pouvoir les démarrer selon l’ancienne mode. Enfin, il est tout à fait possible — et recommandé — d’écrire des services qui fonctionnent dans tous les modes (autonomes ou systemd), évitant de produire une version spéciale ou de créer une branche.

systemd vient avec un kit d’intégration en C, qui facilite l’intégration dans un service. Lennart a publié deux notes de documentation et une page de manuel très complète :

Conclusion

Au travers de cet article vous a été présenté un sous‐ensemble des fonctionnalités offertes par systemd. Il repense totalement la gestion des processus des systèmes basés sur le noyau Linux, et utilise pleinement les technologies offertes par ce dernier.

Plusieurs distributions intègrent ou vont intégrer systemd. D’après l’article concernant systemd sur Wikipédia, il est utilisé depuis Fedora 15, et est optionnel dans Fedora 14. openSUSE prévoyait de l’intégrer dans la version 11.4, mais cela semble être repoussé à la 12.1. Mandriva 2011 est prévue avec. Arch Linux a un paquet expérimental. Debian a un paquet dans « unstable » et devrait permettre de choisir un système ou l’autre dans une prochaine version. Gentoo rend systemd accessible dans « testing ». Le grand absent est Ubuntu, qui a beaucoup investi dans Upstart et n’a pas manifesté, pour l’instant, de volonté de passer à systemd (bien qu’un début d’intégration existe).

Cet outil à tout faire, semble faire un pied de nez à la philosophie d’UNIX en combinant plusieurs fonctions en un seul programme. D’un autre côté, le système de démarrage actuel montre ses limites.

La gestion de l’initialisation est très importante pour le système. On peut donc se demander s’il est judicieux d’inclure tant de fonctionnalités dans un programme critique ? Cependant, avec les contraintes de performances et la complexité des systèmes actuels, n’est‐ce pas un mal nécessaire pour gérer efficacement les processus exécutés sur nos systèmes ?

Installation

L’installation de SystemD se fait en deux temps. Tout d’abord installez le paquet

apt-get install systemd

Les options suivantes du noyau doivent être positionnées comme ci-dessous

  • CONFIG_DEVTMPFS=y
  • CONFIG_CGROUPS=y
  • CONFIG_AUTOFS4_FS=[y|m]
  • CONFIG_IPV6=[y|m], optionnel mais fortement recommandé
  • CONFIG_FANOTIFY=y, optionnel mais requis pour le systemd readahead. (> 2.6.37 only)

Ensuite on va changer le démarrage afin de remplacer init par systemd, ouvrez le fichier /etc/default/grub et éditez la ligne suivante:

GRUB_CMDLINE_LINUX_DEFAULT="quiet init=/bin/systemd"

Tapez ensuite la commande de mise à jour de grub:

update-grub

Redémarrez maintenant le système.

On va maintenant remplacer sysvinit par celui de systemD. Faites d’abord une copie de init puis installez le paquet systemd-sysv à la place. Lors de la désinstallation il vous sera demandé d’inscrire que vous êtes conscient que vous faites une erreur, normal vu que vous allez remplacer le système de chargement du système.

cp -av /sbin/init /sbin/init.sysvinit
apt-get remove sysvinit
apt-get install systemd-sysv

Redémarrez de nouveau.

Voilà vous êtes désormais passé de init à systemD

Configuration

Nous allons maintenant changer quelques options intéressantes. Ouvrez le fichier /etc/systemd/system.conf et ajustez les valeurs suivantes:

CPUAffinity 1 2 3 4
MountAuto=yes
SwapAuto=yes

Il est intéressant de mettre le montage automatique des partitions et du swap, ainsi chacun ne sera monté qu’à la demande. En ce qui concerne l’option CPU affinity, elle indique à systemd sur quels processeurs (cores de processeurs) se greffer. Ici nous l’avons mis sur les 4 coeurs de processeur. Par défaut il n’en utilise au maximum que 2.