Aller au contenu

systemd : dépendances, cibles et sockets

Comprendre comment systemd enchaîne les unités permet de démarrer les services dans le bon ordre. Les exemples utilisent des noms génériques.

Notion Directives Effet
Dépendance (quoi lancer) Wants=, Requires=, BindsTo=, PartOf= Si A dépend de B, démarrer A démarre aussi B
Ordre (dans quel ordre) After=, Before= A démarre après B (mais ne le lance pas)

Les deux sont indépendantes : Requires= seul ne garantit pas l’ordre. Pour « B doit être lancé et prêt avant A », il faut les deux.

[Unit]
Description=Application monapp
Requires=postgresql.service
After=postgresql.service
Directive Si la dépendance échoue ou s’arrête
Wants= L’unité démarre quand même (dépendance faible, recommandée)
Requires= L’unité n’est pas démarrée (dépendance forte) ; arrêtée si la dépendance s’arrête
Requisite= Comme Requires, mais échoue tout de suite si la dépendance n’est pas déjà active
BindsTo= Comme Requires, et s’arrête aussitôt si la dépendance disparaît
PartOf= Les arrêts et redémarrages de la dépendance sont propagés à l’unité
Conflicts= Les deux unités ne peuvent pas tourner ensemble

Pour un service qui a besoin de sa base de données mais qui doit survivre à son redémarrage : Wants= + After=.

After=network.target signifie seulement que la pile réseau est initialisée, pas que le réseau est en ligne (adresse obtenue). Pour un service qui contacte un serveur au démarrage :

[Unit]
After=network-online.target
Wants=network-online.target

network-online.target est atteint quand la configuration réseau est terminée, grâce à systemd-networkd-wait-online ou NetworkManager-wait-online, selon le gestionnaire réseau en place (Netplan avec networkd sur Ubuntu Server).

Une cible regroupe des unités pour représenter un état de la machine :

Cible Équivalent (anciens niveaux d’exécution)
poweroff.target, reboot.target Arrêt, redémarrage
rescue.target Mode de secours (mono-utilisateur)
multi-user.target Serveur sans interface graphique (niveau 3) : cible par défaut d’un serveur
graphical.target Avec interface graphique (niveau 5)
network-online.target Réseau configuré
timers.target, sockets.target Regroupent les timers, les sockets
Fenêtre de terminal
systemctl get-default
sudo systemctl set-default multi-user.target
sudo systemctl isolate rescue.target # bascule (arrête les services hors de cette cible)
systemctl list-dependencies multi-user.target --no-pager | head -20

WantedBy=multi-user.target dans la section [Install] d’un service signifie « démarrer avec la machine, en mode normal ».

Fenêtre de terminal
systemd-analyze # durée du démarrage
systemd-analyze blame --no-pager | head # unités les plus lentes
systemd-analyze critical-chain --no-pager # chaîne d'unités qui détermine la durée totale
systemd-analyze critical-chain monapp.service # chaîne d'un service
systemd-analyze plot > demarrage.svg # diagramme (ouvrir dans un navigateur)

Une unité .socket écoute un port et lance le service à la première connexion : économie de ressources et démarrage différé. (C’est le fonctionnement de ssh.socket sur Ubuntu : voir SSH : installation.)

/etc/systemd/system/echo.socket :

[Socket]
ListenStream=127.0.0.1:7777
Accept=yes
[Install]
WantedBy=sockets.target

/etc/systemd/system/echo@.service :

[Service]
ExecStart=/bin/cat
StandardInput=socket
Fenêtre de terminal
sudo systemctl daemon-reload
sudo systemctl enable --now echo.socket
echo "bonjour" | nc 127.0.0.1 7777 # renvoie « bonjour » : un service echo@ est lancé pour la connexion
systemctl list-sockets

Avec Accept=yes, systemd lance un service par connexion (nom d’unité avec @). Avec Accept=no (défaut), un seul service reçoit le socket et gère toutes les connexions.

/etc/systemd/system/traitement.path
[Path]
PathExists=/srv/depot/nouveau.txt
Unit=traitement.service
[Install]
WantedBy=multi-user.target

Autres déclencheurs : PathChanged= (fichier modifié), PathModified=, DirectoryNotEmpty= (un dossier reçoit un fichier).

Un service modèle s’instancie avec un paramètre :

# /etc/systemd/system/salut@.service
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo "salut %i"'
Fenêtre de terminal
sudo systemctl start salut@alice.service # %i vaut « alice »
sudo systemctl start salut@bob.service
journalctl -u 'salut@*' --no-pager | tail -4

Utile pour des instances multiples d’un même programme (un par port, par utilisateur, par site). %n est le nom complet de l’unité, %i l’instance.

# /etc/systemd/system/srv-donnees.mount (le nom correspond au chemin : /srv/donnees)
[Unit]
Description=Disque de données
[Mount]
What=/dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Where=/srv/donnees
Type=ext4
Options=defaults,noatime
[Install]
WantedBy=multi-user.target

Un service qui a besoin de ce disque : RequiresMountsFor=/srv/donnees. Les montages courants restent dans /etc/fstab (systemd en génère les unités).

Fenêtre de terminal
systemctl list-dependencies monapp --after # unités démarrées avant
systemctl list-dependencies monapp --before # unités démarrées après
systemctl list-jobs # tâches en cours (démarrage bloqué ?)
journalctl -b -u monapp --no-pager | head
Symptôme Piste
Le service démarre avant sa base de données Ajouter After= (en plus de Wants=/Requires=)
Dependency failed for … La dépendance (Requires=) a échoué : systemctl status de celle-ci
Un service démarre trop tôt, réseau indisponible After=network-online.target et Wants=network-online.target
Job … ordering cycle / Found ordering cycle Boucle de dépendances : journalctl -b | grep -i cycle, retirer un After=
Le démarrage est lent systemd-analyze blame et critical-chain
Le socket ne lance pas le service Nom du service différent (echo.socket ↔ echo@.service avec Accept=yes), Service= pour le désigner

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Les directives de cette page sont identiques sur systemd 255 et 259. Sur Ubuntu 26.04, la prise en charge des scripts System V est annoncée comme dernière version : les services sous /etc/init.d doivent être convertis (voir Bases et systemctl).