⚙️ systemd – Init, Service Management & journald
Guide complet IDEO-Lab sur le système "init" (PID 1) de Linux : systemctl, journald, timers.
Concept : PID 1 (Init)
Le "System and Service Manager". (Remplace SysVinit).
systemd PID 1 Initvs. SysVinit / init.d
Scripts shell (séquentiels) vs Unités (parallèles).
SysVinit init.d ComparatifComposants (Suite)
systemctl (CLI), journald (Logs), timedatectl...
Concept : Fichiers "Unit"
Les "objets" (.service, .socket, .timer, .target).
Structure Fichier Unit
Sections ([Unit], [Service], [Install]).
Section [Unit] (Métadonnées)
Description, After (Ordre), Wants (Dépendance).
Section [Service] (Logique)
ExecStart, User, Group, WorkingDirectory.
Section [Service] (HA)
Restart=on-failure, RestartSec. (Keep Alive).
Section [Install] (Boot)
WantedBy=multi-user.target (Activer au boot).
systemctl (Runtime)
start, stop, restart, reload.
systemctl (Boot)
enable, disable, mask (Désactivation forte).
systemctl (Debug)
status (État), is-active, is-enabled.
Logs : journald
Le daemon (systemd-journald). Centralise (Syslog, stdout/stderr).
Logs : journalctl (CLI)
L'outil de requête (-u, -f, -S/-U, -p).
Logs : Persistance
/run/log/journal (Volatil) vs /var/log/journal (Persistant).
.timer (vs Cron)
Le "remplaçant" de Cron. Plus puissant, logging intégré.
Timers Cron.timer : Syntaxe (Temps)
OnBootSec, OnUnitActiveSec, OnCalendar.
.timer : Workflow
monjob.service (Le "Quoi") + monjob.timer (Le "Quand").
Concept : Targets (Cibles)
Remplacement des "Runlevels" (multi-user.target).
Outils (Écosystème)
hostnamectl, timedatectl, loginctl.
Cheat-sheet
Résumé systemctl & journalctl.
Qu'est-ce que systemd ?
systemd est le "System and Service Manager" (Gestionnaire de Système et de Services) pour Linux. C'est le système d'init moderne.
C'est le premier processus lancé par le Noyau (Kernel) au démarrage. Il a le PID 1 (Process ID 1).
Son Rôle (PID 1)
En tant que PID 1, systemd est le "père" (ou "ancêtre") de tous les autres processus (services) du système.
- Démarrage (Boot) : Il est responsable du démarrage (lancement) de tous les services nécessaires (Réseau, SSHD, Nginx, Cron...) au boot, en parallèle (rapide).
- Gestion (Runtime) : Il surveille (monitor) tous ces services. Si
nginxcrash, c'estsystemd(viaRestart=) qui le redémarre. - Arrêt (Shutdown) : Il gère l'arrêt propre de tous les services.
systemd (2010+) remplace le système SysVinit (init) (1980s).
SysVinit (init, /etc/init.d/)
Philosophie : Scripts Shell (Procédural).
Démarrage : Séquentiel. init exécute /etc/rc2.d/S10network, *attend* qu'il finisse, puis lance S20ssh, *attend*... (Très lent).
Logique : Des scripts Shell (/etc/init.d/apache2) avec des arguments (start, stop, status).
Gestion : service apache2 start, update-rc.d apache2 defaults (Debian) ou chkconfig httpd on (RHEL).
systemd
Philosophie : Fichiers "Unit" (Déclaratif).
Démarrage : Parallèle (basé sur les dépendances). systemd lit les dépendances (After=, Wants=) et lance tout ce qui est non-bloquant en parallèle. (Boot très rapide).
Logique : Fichiers .service (/lib/systemd/system/nginx.service) qui déclarent *comment* démarrer (ExecStart).
Gestion : systemctl start nginx, systemctl enable nginx.
systemd n'est pas un seul binaire, c'est une suite (stack) de daemons et d'outils (ce qui lui est reproché).
| Daemon (Service) | Outil CLI | Rôle |
|---|---|---|
systemd (PID 1) | systemctl | Le Service Manager (Start, Stop, Enable, Status). |
systemd-journald | journalctl | Le Logging Daemon (Centralise tous les logs). |
systemd-logind | loginctl | Gestion des sessions utilisateur (Logins, Sièges). |
systemd-timedated | timedatectl | Gestion du temps (Heure, Timezone, NTP). |
systemd-hostnamed | hostnamectl | Gestion du nom d'hôte (Hostname). |
systemd-resolved | resolvectl | Gestion de la résolution DNS (Stub Resolver). |
systemd-networkd | networkctl | (Optionnel) Gestion de la configuration Réseau (IP, DHCP...). |
Une Unit (Unité) est l'objet de base que systemd gère. C'est un simple fichier .ini (texte) qui décrit une ressource.
Types d'Unit (Suffixes)
| Type (Unité) | Fichier | Description |
|---|---|---|
| Service | nginx.service | Un service (daemon) à exécuter (ex: /usr/sbin/nginx). |
| Target | multi-user.target | Un "point de synchronisation" (groupe) d'unités (ex: un Runlevel). |
| Timer | backup.timer | Un "déclencheur" (timer) qui active un .service (remplace cron). |
| Socket | sshd.socket | Un socket (IP/Port) à écouter (Socket Activation). |
| Mount | mnt-data.mount | Un point de montage (/etc/fstab). |
| Slice | user.slice | Un groupe (cgroup) pour la gestion des ressources. |
Emplacements (Priorité)
/lib/systemd/system/: (Bas) Installé par les paquets (apt,dnf). (Ne pas modifier)./run/systemd/system/: (Moyen) Généré au Runtime./etc/systemd/system/: (Haut) Prioritaire. Là où l'admin crée ses propres units (.service) ou ses "overrides" (surcharges).
.service)Un fichier "Unit" (ex: myapp.service) est un fichier .ini structuré en 3 sections principales.
# Fichier: /etc/systemd/system/myapp.service # --- 1. Métadonnées & Dépendances --- [Unit] Description=Mon Application Web (Gunicorn) After=network.target mysql.service Wants=mysql.service # --- 2. Logique d'Exécution --- [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/srv/myapp ExecStart=/srv/myenv/bin/gunicorn --workers 4 myapp:app Restart=on-failure RestartSec=5 Environment="MODE=prod" # --- 3. Démarrage (Boot) --- [Install] WantedBy=multi-user.target
[Unit]: (Métadonnées) Description, et Dépendances (Ordre de démarrage).[Service]: (Le "Comment") La commande à exécuter, l'utilisateur, le redémarrage.[Install]: (Activation) À quel "Runlevel" (Target) ce service doit-il être attaché (viasystemctl enable).
[Unit] (Dépendances)La section [Unit] gère les métadonnées et, surtout, l'ordre de démarrage (remplace les S10, S20 de SysVinit).
| Directive | Description |
|---|---|
Description= | Texte (lisible) décrivant le service (vu dans status). |
After= | Ordre (Ordering). "Démarrer cette unité après network.target." (Ne crée pas de dépendance). |
Before= | Ordre (Inverse). "Démarrer cette unité avant nginx.service." |
Wants= | Dépendance Faible. "Démarrer mysql.service en même temps. Si mysql.service échoue, ne rien faire (continuer)." |
Requires= | Dépendance Forte. "Démarrer mysql.service en même temps. Si mysql.service échoue, arrêter (fail) cette unité aussi." |
Conflicts= | Si A.service (Conflicts=B) démarre, systemd arrêtera B.service. |
Exemple (Le plus courant)
[Unit] Description=Mon App # (Attend que le réseau (network.target) # et que la BDD (mysql.service) soient UP # avant de se lancer) After=network.target mysql.service # (Tente de démarrer mysql.service en parallèle, # mais ne plantera pas si mysql échoue) Wants=mysql.service
[Service] (Logique)La section [Service] définit comment exécuter le processus. C'est la plus importante pour un service.
| Directive | Description | Exemple |
|---|---|---|
ExecStart= | La Commande. Le chemin (absolu) et les arguments à exécuter. | /usr/sbin/nginx -g 'daemon off;' |
ExecStop= | (Optionnel) Commande pour arrêter (ex: /bin/kill -s HUP $MAINPID). | /usr/bin/php /srv/stop.php |
ExecReload= | (Optionnel) Commande pour systemctl reload (ex: SIGHUP). | /bin/kill -s HUP $MAINPID |
User= / Group= | (Sécurité) Exécuter le processus en tant que cet utilisateur/groupe (non-root). | User=www-data |
WorkingDirectory= | Définit le dossier de travail (CWD) avant ExecStart. | /var/www/myapp |
Environment= | Définit des variables d'environnement. | Environment="MODE=prod" |
Type= | Comment le processus se comporte. | (Voir onglet suivant) |
Type=simple (Défaut)
(90% des cas modernes). Le processus (ExecStart) est le processus principal et ne "fork" pas (ne se détache pas). (ex: nginx -g 'daemon off;').
systemd sait que tant que ce PID est vivant, le service est "running".
Type=oneshot
Le processus est un "one-shot" (un script qui s'exécute 1 fois et se termine). (ex: iptables-restore, backup.sh).
Type=forking (Legacy Daemons)
(Pour les "vieux" daemons, ex: apache2).
Problème : Le processus (ExecStart) forke (lance) un "enfant" (le vrai daemon) puis se termine (le parent meurt).
systemd (par défaut simple) verrait le parent mourir et penserait que le service a crashé.
Solution : Type=forking dit à systemd : "Attends-toi à ce que le 1er process meure. Le vrai service est l'enfant." (Utiliser PIDFile= pour aider systemd à trouver le bon PID).
[Service] (HA : Restart)Les directives Restart= sont le "keep-alive" (HA) de systemd. (Similaire à autorestart de Supervisor).
| Directive | Description |
|---|---|
Restart=no | (Défaut) Ne jamais redémarrer. |
Restart=on-failure | (Courant) Redémarre si le service se termine avec un code d'erreur (non-zéro). |
Restart=always | (Courant) Redémarre toujours (même si exit code 0). |
Restart=on-abnormal | Redémarre si tué par un signal (sauf SIGHUP/SIGINT/SIGTERM). |
Contrôle (Throttling)
Pour éviter une "boucle de crash" (le service crash, redémarre, recrash...), on utilise RestartSec.
[Service] ExecStart=/usr/bin/mon_script_fragile Restart=on-failure # (Si le script crash, attendre 5 secondes avant de le relancer) RestartSec=5s
[Install] (Activation au Boot)La section [Install] ne fait rien au runtime. Elle n'est utilisée que par la commande systemctl enable (et disable).
WantedBy= (Lien)
Cette directive (la seule importante ici) dit à systemd : "Si l'utilisateur 'enable' ce service, à quel Target (Runlevel) doit-il être rattaché ?"
[Install] # (Ce service doit être démarré quand le système # entre dans l'état "multi-user" (Runlevel 3/5)) WantedBy=multi-user.target
Flux (systemctl enable myapp.service)
systemdlit/etc/systemd/system/myapp.service.- Il voit
WantedBy=multi-user.target. - Il crée un lien symbolique (symlink) :
ln -s /etc/systemd/system/myapp.service /etc/systemd/system/multi-user.target.wants/myapp.service
(Au prochain boot, quand multi-user.target démarre, systemd démarre tout ce qui se trouve dans son dossier .wants/).
systemctl : Gestion (Runtime)Commandes systemctl pour gérer un service maintenant (session actuelle).
# Démarrer un service
$ sudo systemctl start nginx.service
# ('.service' est optionnel)
$ sudo systemctl start nginx
# Arrêter un service
$ sudo systemctl stop nginx
# Redémarrer un service (Stop puis Start)
$ sudo systemctl restart nginx
# Recharger la configuration (SIGHUP) (si 'ExecReload' est défini)
# (Ne tue pas le processus, ex: Nginx)
$ sudo systemctl reload nginx
# Tenter un 'reload', sinon 'restart'
$ sudo systemctl reload-or-restart nginxsystemctl : Gestion (Boot)Commandes systemctl pour gérer si un service doit se lancer au démarrage (boot).
enable & disable
# Activer (au prochain boot) # (Crée le symlink 'WantedBy' (voir 3.3)) $ sudo systemctl enable nginx # Activer ET Démarrer (maintenant) $ sudo systemctl enable --now nginx # Désactiver (au prochain boot) # (Supprime le symlink) $ sudo systemctl disable nginx # Désactiver ET Arrêter (maintenant) $ sudo systemctl disable --now nginx
mask & unmask (Désactivation Forte)
Problème : systemctl disable empêche le démarrage au boot. Mais si un *autre* service (ex: myapp.service) a une dépendance (Requires=nginx.service), systemd démarrera quand même nginx !
Solution : mask (Masquer). Crée un symlink de /etc/systemd/system/nginx.service vers /dev/null.
# Empêche le service de démarrer (manuellement OU par dépendance) $ sudo systemctl mask nginx # (Le service est maintenant "mort" jusqu'à l'unmask) # Autoriser à nouveau $ sudo systemctl unmask nginx
systemctl : Debug (status)status est l'outil de diagnostic n°1.
$ systemctl status nginx
● nginx.service - A high performance web server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; ...)
Active: active (running) since Mon 2025-11-10 10:00:00 CET
Main PID: 1234 (nginx)
Tasks: 4 (limit: 9400)
Memory: 50.0M
CPU: 1.5s
CGroup: /system.slice/nginx.service
├─1234 /usr/sbin/nginx -g 'daemon off;'
└─1235 /usr/sbin/nginx (worker process)
Nov 10 10:00:00 server systemd[1]: Starting A high performance web server...
Nov 10 10:00:01 server systemd[1]: Started A high performance web server.
(Affiche les 10 derniers logs (journald) du service)| État (Loaded) | Description |
|---|---|
enabled | Se lancera au boot (symlink WantedBy existe). |
disabled | Ne se lancera pas au boot. |
masked | Désactivé (lié à /dev/null). |
| État (Active) | Description |
|---|---|
active (running) | OK. Le service tourne (ex: Type=simple). |
active (exited) | OK. (Pour Type=oneshot) Le script s'est exécuté et terminé (exit 0). |
inactive (dead) | Stoppé. Le service est arrêté (normalement). |
failed | Panne. Le service a échoué (exit non-zéro, timeout...). |
journald (Le Daemon)systemd-journald est le service (daemon) de logging de systemd. Il vise à remplacer (ou centraliser) rsyslog.
Le "Journal"
journald capture et centralise tous les logs :
- Logs Kernel : (
dmesg) - Logs Syslog : (Écoute sur
/dev/log, remplacersyslog) - Logs
stdout/stderr: (Le plus important) Capture automatiquement toutes les sorties (echo,print) de tous les services gérés parsystemd.
Stockage (Binaire/Structuré)
Contrairement à Syslog (/var/log/syslog) qui est 100% texte, journald stocke les logs dans un format binaire, structuré (JSON-like) et indexé.
Avantage : Permet des requêtes (journalctl) extrêmement rapides et un filtrage puissant (ex: "Montre-moi les logs du service nginx, niveau error, de la session 5").
journalctl (Le Client)journalctl est l'outil (CLI) pour lire le journal binaire de journald.
Commandes de Filtrage (Les plus utiles)
# 1. Voir TOUT (le plus récent en bas) (Pipe vers 'less' auto) $ journalctl # 2. (Le plus utile) Suivre (tail -f) les logs $ journalctl -f # 3. Filtrer par Service (Unit) $ journalctl -u nginx.service # (Suivre les logs Nginx) $ journalctl -f -u nginx.service # 4. Filtrer par Temps $ journalctl -S "1 hour ago" # (Depuis 1h) $ journalctl -S today $ journalctl -S "2025-11-10 14:00:00" -U "2025-11-10 14:05:00" # 5. Filtrer par Priorité (Gravité) # (p=3 -> 'err') $ journalctl -p err # (p=4 -> 'warning') $ journalctl -p warning # 6. Combiner (Logs d'erreur Nginx de la dernière heure) $ journalctl -u nginx.service -p err -S "1 hour ago" # 7. (Dmesg) Voir les logs Kernel $ journalctl -k
journald.conf)Où sont stockés les logs ? (/etc/systemd/journald.conf).
Stockage : Volatil vs Persistant
Storage=auto (Défaut). systemd décide :
- Mode Volatil (Par défaut sur certaines distros "minimales") :
- Si
/var/log/journal/n'existe pas. - Les logs sont stockés dans
/run/log/journal/(en RAM / tmpfs). - Inconvénient : Tous les logs sont perdus à chaque reboot !
- Si
- Mode Persistant :
- Si
/var/log/journal/existe. - (Pour forcer :
sudo mkdir -p /var/log/journaletsystemctl restart systemd-journald).
- Si
Rotation (Gestion de la Taille)
Puisque les logs sont binaires, on ne peut pas utiliser logrotate. La rotation est gérée par journald.conf.
# Fichier: /etc/systemd/journald.conf [Journal] # (Storage=persistent) # (Taille max pour /var/log/journal) SystemMaxUse=4G # (Garder 10% de l'espace disque libre) SystemKeepFree=10% # (Taille max pour /run/log/journal (RAM)) RuntimeMaxUse=500M
systemd.timer (vs Cron)Les .timer sont l'alternative (le "remplaçant") moderne de cron (voir guide Cron). Ils sont plus puissants et mieux intégrés.
| Critère | cron (Legacy) | systemd.timer (Moderne) |
|---|---|---|
| Logging | Mauvais (Email (MAILTO) ou > /dev/null) | Excellent (Intégration journald native). |
| Dépendances | Non (sauf &&) | Oui (After=, Wants=). (ex: "Lancer le backup *après* mysql.service"). |
| Environnement | Mauvais ($PATH minimal) | Contrôle total (Environment=, User=). |
| Gestion | crontab -e | systemctl enable/start. (Clair). |
| Granularité | Minute | Seconde, Nanoseconde. |
| Complexité | Simple (1 ligne) | Complexe (2 fichiers). |
.timer : Syntaxe (Temps)La section [Timer] du fichier .timer définit le "Quand".
Types de Timers
- Monotoniques (Après le boot) :
OnBootSec=: (ex:OnBootSec=5min) (5 min *après* le boot).OnUnitActiveSec=: (ex:30s) (30 sec *après* l'activation de ce timer). (Pour les "timers" récurrents, type@hourly).
- Temps Réel (Calendrier) :
OnCalendar=: (Le plus puissant, remplace la syntaxe cron).
Syntaxe OnCalendar=
# (Similaire à cron, mais plus lisible) # (Format: JourSem YYYY-MM-DD HH:MM:SS) # Tous les jours à 2h00 OnCalendar=daily # (ou) OnCalendar=02:00:00 # Tous les 15 minutes (toute la journée) OnCalendar=*:0/15 # Toutes les heures, aux minutes 0 et 30 OnCalendar=*:0,30 # Tous les Lundis à 9h00 (heures de bureau) OnCalendar=Mon *-*-* 09:00:00 # Le 1er et le 15 du mois, à 4h00 OnCalendar=*-*-01,15 04:00:00
.timer : Workflow (2 Fichiers)Un "timer" systemd nécessite deux fichiers "Unit", qui doivent avoir le même nom (ex: backup.timer et backup.service).
1. Le .service (Le "Quoi")
Définit la commande à exécuter. (Souvent Type=oneshot).
# Fichier: /etc/systemd/system/backup.service [Unit] Description=Exécuter le script de backup [Service] Type=oneshot User=root ExecStart=/usr/local/bin/backup.sh
2. Le .timer (Le "Quand")
Définit la planification. Il active le .service du même nom.
# Fichier: /etc/systemd/system/backup.timer [Unit] Description=Timer pour le backup (2h du matin) [Timer] # (Exécuter tous les jours à 2h00) OnCalendar=daily # (ou OnCalendar=02:00:00) # (Si le PC était éteint, # exécute au boot si manqué) Persistent=true [Install] WantedBy=timers.target
3. Activation
On active et démarre uniquement le .timer (pas le .service).
# Activer le timer (au boot) $ sudo systemctl enable backup.timer # Démarrer le timer (maintenant) $ sudo systemctl start backup.timer # Lister tous les timers actifs $ systemctl list-timers
Les .target (Cibles) sont le remplacement (moderne) des "Runlevels" (Niveaux d'exécution) de SysVinit.
Une "Target" est un point de synchronisation (un "groupe" de services) qui définit l'état du système.
| Target (systemd) | Runlevel (SysVinit) | Description |
|---|---|---|
poweroff.target | Runlevel 0 | Arrêt. |
rescue.target | Runlevel 1 | Mode "Single User" (secours). |
multi-user.target | Runlevel 3 | Mode "Texte" (CLI), multi-utilisateurs, réseau. (Serveur). |
graphical.target | Runlevel 5 | Mode "Graphique" (GUI). (multi-user.target + GUI). (Desktop). |
reboot.target | Runlevel 6 | Redémarrage. |
# Voir la target (runlevel) par défaut $ systemctl get-default graphical.target # Changer (ex: passer en mode serveur CLI par défaut) $ sudo systemctl set-default multi-user.target # Changer (maintenant) $ sudo systemctl isolate multi-user.target
systemd fournit une suite d'outils CLI (Clients) pour gérer les daemons (Serveurs) correspondants.
hostnamectl
(Gère systemd-hostnamed)
# Voir le statut (hostname, OS, Kernel...) $ hostnamectl # Changer le nom d'hôte (hostname) $ sudo hostnamectl set-hostname "nouveau-serveur"
timedatectl
(Gère systemd-timedated)
# Voir le statut (Heure, Timezone, NTP) $ timedatectl Local time: Wed 2025-11-12 10:00:00 CET ... Time zone: Europe/Paris (CET, +0100) System clock synchronized: yes NTP service: active # Lister les Timezones $ timedatectl list-timezones # Changer la Timezone $ sudo timedatectl set-timezone "Europe/London" # Activer la synchro NTP $ sudo timedatectl set-ntp true
systemctl & journalctlsystemctl (Gestion des Services)
# --- Gestion (Runtime) --- systemctl start [service] systemctl stop [service] systemctl restart [service] systemctl reload [service] # --- Gestion (Boot) --- systemctl enable [service] systemctl disable [service] systemctl enable --now [service] # (Enable + Start) # --- Diagnostic (Status) --- systemctl status [service] systemctl is-active [service] systemctl is-enabled [service] systemctl list-units --type=service --state=running systemctl list-units --type=service --state=failed # --- Gestion (System) --- systemctl reboot systemctl poweroff systemctl daemon-reload # (Obligatoire si on modifie un .service) systemctl list-dependencies [service] # (Voir dépendances)
journalctl (Gestion des Logs)
# --- Visualisation --- journalctl # (Tout, paginé) journalctl -f # (Suivre les logs) journalctl -n 100 # (Les 100 dernières lignes) # --- Filtrage (Unité) --- journalctl -u nginx.service journalctl -u nginx -f journalctl -u sshd # --- Filtrage (Temps) --- journalctl -S today journalctl -S "1 hour ago" journalctl -S "2025-11-10 14:00" # --- Filtrage (Priorité) --- journalctl -p err # (3) journalctl -p warning # (4) # --- Kernel (dmesg) --- journalctl -k # --- Combiné (Erreurs Nginx d'hier) --- journalctl -u nginx -p err -S yesterday
