systemd : timers, sockets et drop-in
Tu sais planifier une tâche avec un timer, expliquer l'activation par socket, dire où vivent les fichiers d'unité et lequel l'emporte, modifier une unité proprement avec un drop-in, et passer des variables d'environnement à un service.
Mis à jour le
Pourquoi on voit ça
Tu sais écrire une unité et la piloter avec systemctl. Il reste quatre savoir-faire que tu rencontreras très vite en entreprise :
- lancer une tâche à heure fixe (sauvegarde, nettoyage) ;
- ne lancer un service que lorsqu’on en a besoin ;
- modifier l’unité d’un paquet sans la casser à la prochaine mise à jour ;
- donner à un service ses réglages (port, mot de passe) sans les écrire en dur.
Les timers : planifier une tâche
Un timer est une unité .timer qui déclenche un service à un moment donné. Il fonctionne toujours en duo :
- le
.servicedit quoi faire ; - le
.timerdit quand.
Par défaut, un .timer déclenche le .service du même nom : sauvegarde.timer lance sauvegarde.service.
Si les noms diffèrent, la directive Unit= de la section [Timer] désigne le service à lancer. Le timer remplace cron, le planificateur historique de Linux, avec trois avantages : les exécutions apparaissent dans le journal de systemd, la tâche peut avoir des dépendances, et une exécution manquée peut être rattrapée.
Le service, de type oneshot puisqu’il fait son travail puis se termine :
[Unit]
Description=Sauvegarde de la base
[Service]
Type=oneshot
ExecStart=/usr/local/bin/sauvegarde.sh
Le timer :
[Unit]
Description=Sauvegarde chaque nuit à 3 h
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
| Directive | Sens | Exemple |
|---|---|---|
OnCalendar= |
à une date ou une heure du calendrier | *-*-* 03:00:00 (tous les jours à 3 h), daily, weekly |
OnBootSec= |
un délai après le démarrage de la machine | OnBootSec=10min |
Persistent=true |
rattrape au démarrage une échéance manquée pendant que la machine était éteinte |
Le format de OnCalendar se lit année-mois-jour heure:minute:seconde, et * veut dire « tous ».
On peut faire précéder la date d’un jour de la semaine en anglais (Mon, Tue..Thu : .. donne un intervalle). L’heure s’écrit sur 24 h : 15:00, c’est 15 h, et 3:17 c’est 3 h 17 du matin.
Persistent=true note la dernière exécution sur le disque et rattrape celle qui a été manquée.C’est le timer qu’on active, pas le service :
systemctl enable --now sauvegarde.timer
Remarque l’extension : sans elle, systemctl comprendrait sauvegarde.service. Pour voir tous les timers, avec leur prochaine et leur dernière échéance :
systemctl list-timers
Pour voir ce que la tâche a fait, on lit le journal du service, pas celui du timer :
journalctl -u sauvegarde.service
L’activation par socket
Normalement, un service démarre, ouvre son port et attend, même si personne ne l’utilise de la journée. Avec l’activation par socket, les rôles changent :
- systemd écoute sur le port à la place du service ;
- le service n’est pas lancé ;
- à la première connexion, systemd démarre le service et lui passe la connexion.
C’est comme la sonnette d’une loge : le gardien (systemd) entend sonner, puis va chercher la personne concernée. Elle n’a pas besoin d’attendre toute la journée derrière la porte.
Là encore, c’est un duo par le nom : monapp.socket active monapp.service.
[Unit]
Description=Port d'écoute de monapp
[Socket]
ListenStream=8080
[Install]
WantedBy=sockets.target
Les avantages : un démarrage du serveur plus rapide, de la mémoire économisée pour les services rarement utilisés, et aucune connexion perdue pendant que le service se lance.
Comme pour le timer, c’est la socket qu’on active, pas le service :
systemctl enable --now monapp.socket
Tant que personne ne se connecte, ps aux ne montre aucun processus monapp, mais le port 8080 est déjà en écoute.
Où vivent les fichiers d’unité
| Dossier | Qui y écrit | Priorité |
|---|---|---|
/usr/lib/systemd/system (ou /lib/systemd/system) |
les paquets installés avec apt |
basse |
/run/systemd/system |
systemd lui-même, généré à chaque démarrage | entre les deux |
/etc/systemd/system |
l’administrateur : toi | haute |
Si une unité du même nom existe dans les deux dossiers, c’est celle de /etc/systemd/system qui gagne.
Tu retrouves la logique de la leçon sur le démarrage : ce que tu règles toi-même va dans /etc. Et tu ne modifies jamais un fichier de /usr/lib/systemd/system : la prochaine mise à jour du paquet écraserait ton changement. Tes propres unités (monapp.service, sauvegarde.timer) se rangent donc dans /etc/systemd/system.
Les drop-in : modifier sans tout recopier
Tu veux changer une seule ligne de l’unité de nginx, par exemple ajouter Restart=always. Recopier tout le fichier dans /etc fonctionnerait, mais tu ne profiterais plus des améliorations apportées par les mises à jour. La bonne méthode est le drop-in : un petit fichier qui ne contient que ce que tu changes.
systemctl edit nginx
Cette commande ouvre un éditeur. Tu y écris seulement :
[Service]
Restart=always
À l’enregistrement, systemd crée le fichier /etc/systemd/system/nginx.service.d/override.conf et relit sa configuration tout seul. Le nom du dossier, c’est le nom complet de l’unité suivi de .d. Il te reste à faire systemctl restart nginx pour que le service reparte avec le nouveau réglage.
/etc remplace celui du paquet. Un drop-in, lui, ne change que ce qu'il contient.Deux commandes vont avec :
| Commande | Effet |
|---|---|
systemctl cat nginx |
affiche l’unité telle que systemd la voit : le fichier d’origine, puis chaque drop-in, avec le chemin de chacun |
systemctl revert nginx |
supprime tes modifications et revient à la version du paquet |
systemctl cat est le réflexe quand un service ne se comporte pas comme son fichier le laisse croire : un drop-in oublié change peut-être la donne.
Remplacer une directive qui s’accumule
Certaines directives peuvent apparaître plusieurs fois et s’additionnent au lieu de se remplacer : c’est le cas de ExecStart= et de ListenStream=. Dans un drop-in, pour changer leur valeur, il faut d’abord les vider avec une ligne sans valeur :
[Service]
ExecStart=
ExecStart=/usr/local/bin/monapp --verbose
Sans la ligne vide, le drop-in ajoute une deuxième commande à la première, et systemd refuse de charger le service : plusieurs ExecStart= ne sont permis qu’avec Type=oneshot. Pour une socket, c’est plus sournois : sans ListenStream= vide avant ListenStream=1234, aucune erreur, mais la socket écoute à la fois sur l’ancien port et sur le nouveau.
Les variables d’environnement d’un service
Un service ne démarre pas dans ton terminal : il n’hérite pas des variables de ton shell. Un export PORT=8080 tapé avant systemctl start ne sert à rien. Les variables se déclarent dans la section [Service].
Directement dans l’unité :
[Service]
Environment=PORT=8080
Environment=MODE=production
ExecStart=/usr/bin/python3 /opt/monapp/app.py
Ou dans un fichier à part :
[Service]
EnvironmentFile=/etc/monapp/env
ExecStart=/usr/bin/python3 /opt/monapp/app.py
Le fichier /etc/monapp/env contient une variable par ligne :
PORT=8080
DB_PASSWORD=motdepasse
| Directive | Reçoit | Quand l’utiliser |
|---|---|---|
Environment= |
une variable NOM=valeur |
une ou deux valeurs sans secret |
EnvironmentFile= |
le chemin d’un fichier | beaucoup de variables, ou des secrets |
Avec un fichier à part, tu peux restreindre ses droits de lecture, et l’unité reste identique d’un serveur à l’autre : seul le fichier change.
À retenir
- Un
.timerdéclenche le.servicedu même nom.OnCalendar=: à une date ou une heure.OnBootSec=: un délai après le démarrage.Persistent=true: rattrape une échéance manquée. - On active le timer (
systemctl enable --now nom.timer) et on contrôle avecsystemctl list-timers. - Activation par socket : systemd écoute sur le port et ne lance le service qu’à la première connexion.
- Paquets :
/usr/lib/systemd/system(ou/lib/systemd/system). Administrateur :/etc/systemd/system, qui a la priorité. - Drop-in :
systemctl edit nomcrée/etc/systemd/system/nom.service.d/override.conf.systemctl cataffiche le tout,systemctl revertannule. - Variables d’un service :
Environment=NOM=valeurdans l’unité, ouEnvironmentFile=/cheminvers un fichier.
Entraînement
12 questions, dont 4 à compléter. Tu réponds, on passe à la suivante. La note arrive à la fin.
Ta note
Cartes · 8
Réviser les cartes ici
Pourquoi