Maîtriser les fichiers de service Systemd : Un guide complet
Créez des fichiers de service systemd fiables avec les bonnes sections d'unité, le comportement de redémarrage, les journaux, la sécurité et les minuteries.
Maîtrise des Fichiers de Service Systemd : Guide Complet
Les fichiers de service systemd indiquent à Linux comment démarrer, arrêter, redémarrer et superviser votre application. Si votre service démarre manuellement mais échoue au démarrage, redémarre de manière trop agressive ou écrit les journaux au mauvais endroit, le fichier d'unité est généralement l'endroit où vous devez regarder.
Ce guide se concentre sur la création et la configuration de fichiers d'unité de service systemd à partir de zéro. Vous verrez les sections principales, un exemple de service Python fonctionnel, des commandes de dépannage courantes et quelques contrôles de sécurité et de ressources utiles en production.
Comprendre les Fichiers d'Unité Systemd
Systemd utilise des fichiers d'unité pour décrire diverses ressources système telles que les services, les sockets, les périphériques, les points de montage, etc. Un fichier d'unité de service, se terminant généralement par l'extension .service, définit comment systemd doit gérer un démon ou une application spécifique.
Ces fichiers sont organisés en sections, chaque section contenant des paires clé-valeur représentant des directives de configuration. Les sections principales sur lesquelles nous allons nous concentrer sont [Unit], [Service] et [Install].
Anatomie d'un Fichier de Service Systemd
Un fichier de service systemd typique a la structure suivante :
[Unit]
Description=Une brève description du service.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Détaillons chaque section et ses directives courantes :
La Section [Unit]
Cette section fournit des métadonnées sur l'unité et définit sa relation avec d'autres unités. Elle est utilisée pour les dépendances et l'ordonnancement.
Description=: Un nom lisible pour le service. C'est ce que vous verrez dans la sortie desystemctl status.Documentation=: URL ou chemins vers la documentation du service.Requires=: Définit des dépendances fortes. Si une unité listée ici échoue à démarrer, cette unité échouera également à démarrer.Wants=: Définit des dépendances faibles. Si une unité listée ici échoue à démarrer, cette unité tentera toujours de démarrer.Before=: Garantit que cette unité démarre avant les unités listées.After=: Contrôle uniquement l'ordonnancement. Par exemple,After=network.targetdémarre cette unité après la cible réseau de base, mais ne garantit pas la connectivité externe. Les services dépendants du réseau peuvent avoir besoin deAfter=network-online.targetplus le service wait-online de la distribution.Conflicts=: Si une unité listée ici est démarrée, cette unité sera arrêtée, et vice-versa.
La Section [Service]
Cette section configure le comportement du service lui-même. C'est là que vous définissez comment démarrer, arrêter et gérer le processus.
Type=: Spécifie le type de démarrage du processus. Les valeurs courantes incluent :simple(par défaut) : Le processus principal est celui spécifié dansExecStart=. Systemd suppose que le service est démarré immédiatement après la création du processusExecStart=.forking: Le processusExecStart=crée un processus fils, et le parent se termine. Systemd considère le service comme démarré lorsque le parent se termine. Vous devez souvent spécifierPIDFile=avec ce type.oneshot: Similaire àsimple, mais le processus est censé se terminer après avoir effectué son travail. Utile pour les scripts de configuration.notify: Le démon envoie un message de notification à systemd lorsqu'il a démarré avec succès. C'est le type préféré pour les démons modernes qui le supportent.dbus: Le service acquiert un nom D-Bus.
ExecStart=: La commande à exécuter pour démarrer le service. Pour la plupart des types de service, utilisez une seule commandeExecStart=. Plusieurs lignesExecStart=sont valides pourType=oneshot, où elles s'exécutent séquentiellement.ExecStop=: La commande à exécuter pour arrêter le service.ExecReload=: La commande à exécuter pour recharger la configuration du service sans redémarrer.Restart=: Définit quand le service doit être automatiquement redémarré. Valeurs courantes :no(par défaut) : Ne jamais redémarrer.on-success: Redémarrer uniquement si le service se termine proprement (code de sortie 0).on-failure: Redémarrer si le service se termine avec un code de sortie non nul, est terminé par un signal ou expire.on-abnormal: Redémarrer si terminé par un signal ou expiration.on-abort: Redémarrer uniquement si terminé de manière non propre par un signal.always: Toujours redémarrer, quel que soit le statut de sortie.
RestartSec=: Le temps d'attente avant de redémarrer le service (par défaut 100 ms).User=: L'utilisateur sous lequel exécuter le service.Group=: Le groupe sous lequel exécuter le service.WorkingDirectory=: Le répertoire dans lequel se placer avant d'exécuter les commandes.Environment=: Définit les variables d'environnement pour le service.EnvironmentFile=: Lit les variables d'environnement à partir d'un fichier.PIDFile=: Chemin vers le fichier PID (souvent utilisé avecType=forking).StandardOutput=/StandardError=: Contrôle où vont stdout et stderr, commejournal,nullouinherit. Sur les distributions courantes basées sur systemd, la sortie du service atterrit normalement dans le journal, sauf si les valeurs par défaut du gestionnaire ont été modifiées.
La Section [Install]
Cette section définit comment l'unité doit être activée ou désactivée, généralement en créant des liens symboliques.
WantedBy=: Spécifie la cible qui devrait "vouloir" ce service lorsqu'il est activé. Valeurs courantes :multi-user.target: Pour les services qui doivent démarrer lorsque le système atteint un état de ligne de commande multi-utilisateur.graphical.target: Pour les services qui doivent démarrer lorsque le système atteint un état de connexion graphique.
Créer Votre Premier Fichier de Service Systemd
Créons un fichier de service simple pour un script Python hypothétique nommé my_app.py situé dans /opt/my_app/my_app.py.
1. Créez le fichier de service :
Les fichiers de service pour les applications personnalisées sont généralement placés dans /etc/systemd/system/. Nommons notre fichier my_app.service.
# Créez le répertoire s'il n'existe pas
sudo mkdir -p /etc/systemd/system/
# Créez le fichier de service à l'aide d'un éditeur de texte
sudo nano /etc/systemd/system/my_app.service
2. Ajoutez le contenu suivant à my_app.service :
[Unit]
Description=Mon Application Python Personnalisée
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Explication de l'exemple :
Description: Identifie clairement notre application.After=network.target: Garantit que le réseau est disponible avant de démarrer.Type=simple: Suppose quemy_app.pyest le processus principal et ne crée pas de fork.User=appuser,Group=appgroup: Spécifie l'utilisateur et le groupe sous lesquels l'application doit s'exécuter. Assurez-vous que ces utilisateurs et groupes existent sur votre système et disposent des autorisations appropriées. Vous devrez peut-être les créer :sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: Définit le contexte pour le script.ExecStart: La commande pour exécuter le script Python. Assurez-vous que/usr/bin/python3est le chemin correct vers votre interpréteur Python et que le script est exécutable.Restart=on-failure: Si le script plante, systemd tentera de le redémarrer.WantedBy=multi-user.target: Ce service sera démarré automatiquement lorsque le système démarrera dans un environnement multi-utilisateur.
3. Rechargez la configuration du gestionnaire systemd :
Après avoir créé ou modifié un fichier de service, vous devez dire à systemd de recharger sa configuration.
sudo systemctl daemon-reload
4. Activez et Démarrez le Service :
- Activer : Cela permet au service de démarrer automatiquement au démarrage.
sudo systemctl enable my_app.service - Démarrer : Cela démarre le service immédiatement.
sudo systemctl start my_app.service
5. Vérifiez l'État du Service :
Pour vérifier si votre service fonctionne et voir les éventuelles erreurs :
sudo systemctl status my_app.service
S'il y a des problèmes, la commande status affichera souvent des messages d'erreur ou des journaux de journald.
6. Consultation des Journaux :
Systemd s'intègre à journald pour la journalisation. Vous pouvez consulter les journaux de votre service en utilisant :
sudo journalctl -u my_app.service
Vous pouvez également suivre les journaux en temps réel :
sudo journalctl -f -u my_app.service
Autres Commandes Utiles
- Arrêter le service :
sudo systemctl stop my_app.service - Redémarrer le service :
sudo systemctl restart my_app.service - Recharger la configuration (si supporté par l'application) :
sudo systemctl reload my_app.service - Désactiver le démarrage automatique :
sudo systemctl disable my_app.service
Configuration Avancée et Bonnes Pratiques
Considérations de Sécurité
- Exécutez les services en tant qu'utilisateurs non root : Spécifiez toujours
User=etGroup=sauf en cas d'absolue nécessité. Cela suit le principe du moindre privilège. - Isolez les services : Envisagez des fonctionnalités de sandboxing comme
PrivateTmp=true,ProtectSystem=strict,ProtectHome=trueetNoNewPrivileges=true. Testez-les avec votre application car elles peuvent bloquer les écritures légitimes de fichiers.PrivateTmp=true: Donne au service ses propres répertoires/tmpet/var/tmpprivés.ProtectSystem=strict: Rend la majeure partie du système de fichiers en lecture seule pour le service. UtilisezReadWritePaths=pour les répertoires dans lesquels le service doit écrire.NoNewPrivileges=true: Empêche le service d'acquérir de nouveaux privilèges.
Gestion des Démarrages Complexes
Type=forkingavecPIDFile=: Pour les applications plus anciennes qui créent des forks, assurez-vous quePIDFile=pointe vers le fichier correct.Type=notify: Si votre application le supporte, c'est la manière la plus robuste pour systemd de savoir quand elle est vraiment prête.ExecStartPre=etExecStartPost=: Commandes à exécuter avant et aprèsExecStart=. Utiles pour les tâches de configuration ou de nettoyage.
Contrôle des Ressources
Systemd vous permet de limiter l'utilisation des ressources :
CPUWeight=: Poids CPU relatif pour le service.MemoryMax=: Mémoire maximale que le service peut utiliser.IOWeight=: Poids E/S relatif là où il est supporté par le noyau et la configuration cgroup.
Exemple :
[Service]
# ... autres directives ...
MemoryMax=512M
CPUWeight=50
Minuteries vs. Cron
Les minuteries systemd offrent une alternative moderne aux tâches cron traditionnelles. Elles sont plus flexibles et s'intègrent mieux avec la gestion des journaux et des dépendances de systemd.
- Cron : Tâches planifiées définies dans les fichiers
crontab. - Minuteries Systemd (unités
.timer) : Ces unités planifient des unités.service. Vous définissez un fichier.timerqui spécifie quand un fichier.servicecorrespondant doit s'exécuter.
Exemple :
Pour exécuter un script quotidiennement à 3h du matin :
my_script.service: Le service à exécuter.[Unit] Description=Mon script quotidien [Service] Type=oneshot ExecStart=/opt/my_scripts/run_daily.sh User=scriptusermy_script.timer: La minuterie qui planifie le service.[Unit] Description=Exécuter mon script quotidien une fois par jour [Timer] # Exécuter à 03:00 tous les jours OnCalendar=*-*-* 03:00:00 # S'exécuter peu après le démarrage si l'heure planifiée a été manquée pendant que la machine était éteinte. Persistent=true [Install] WantedBy=timers.target
Pour utiliser ceci :
- Placez les deux fichiers dans
/etc/systemd/system/. - Exécutez
sudo systemctl daemon-reload. - Activez et démarrez la minuterie :
sudo systemctl enable my_script.timeretsudo systemctl start my_script.timer.
Les minuteries offrent des avantages comme Persistent=true (exécute les tâches manquées au démarrage), les événements de calendrier (comme hourly, daily, weekly) et une meilleure intégration avec journalctl.
Dépannage des Problèmes Courants
- Le service ne démarre pas : Vérifiez
systemctl status <nom_du_service>etjournalctl -u <nom_du_service>. Recherchez les fautes de frappe, les chemins incorrects, les dépendances manquantes ou les erreurs de permission. Type=incorrect : Si un service échoue immédiatement ou se bloque, leType=est peut-être erroné. Essayezsimpleouforkinget assurez-vous quePIDFileest correct si vous utilisezforking.- Permission refusée : Assurez-vous que l'
User=et leGroup=spécifiés ont un accès en lecture/écriture aux fichiers et répertoires nécessaires. - Variables d'environnement : Si votre application dépend de variables d'environnement spécifiques, assurez-vous qu'elles sont correctement définies en utilisant
Environment=ouEnvironmentFile=. - Dépendances : Vérifiez que
After=,Wants=etRequires=correspondent à ce que vous voulez.After=ordonne le démarrage ; il ne tire pas une autre unité par lui-même.
Avant d'activer une nouvelle unité sur un hôte de production, exécutez :
sudo systemd-analyze verify /etc/systemd/system/my_app.service
Cela détecte de nombreuses erreurs de syntaxe et de directive avant que vous ne comptiez sur le service au démarrage.
Point Clé à Retenir
Écrivez le plus petit fichier de service qui décrit avec précision votre application, puis ajoutez délibérément la politique de redémarrage, la journalisation, les restrictions de sécurité et les limites de ressources. Après chaque modification, exécutez systemctl daemon-reload, vérifiez l'unité, et consultez systemctl status ainsi que journalctl -u avant de lui faire confiance en production.