Dépannage efficace des pannes courantes des services systemd

Diagnostiquez les pannes courantes des services systemd avec systemctl, journalctl, les codes de sortie, les vérifications d'unités et les étapes de réparation pratiques.

Dépannage efficace des pannes courantes des services systemd

La plupart des pannes de services systemd ne sont pas mystérieuses une fois que vous séparez trois questions : systemd a-t-il lu le fichier d'unité, a-t-il réussi à exécuter la commande, et l'application est-elle restée saine après son démarrage ? Ce sont des points de défaillance différents, et ils laissent des indices différents.

L'erreur que je vois le plus souvent est de sauter directement dans l'édition du fichier d'unité. Lisez d'abord le statut et les journaux. Un service défaillant vous dit généralement s'il a rencontré un exécutable manquant, un mauvais utilisateur, un problème de permission, un problème d'ordre de dépendance ou un crash d'application. Le libellé exact est important.


La boîte à outils de diagnostic essentielle

Un dépannage efficace repose sur deux outils systemd principaux qui fournissent un retour immédiat sur l'état du service et les journaux d'exploitation.

1. Vérification du statut du service

La commande systemctl status fournit un instantané immédiat de l'état de l'unité, y compris son état actuel, les journaux récents et les métadonnées critiques comme l'ID de processus (PID) et le code de sortie.

$ systemctl status myapp.service

Informations clés à rechercher :

  • Load: Confirme que le fichier d'unité a été lu correctement. loaded est bon. S'il affiche not found, votre fichier de service est au mauvais endroit ou mal orthographié.
  • Active: C'est le statut principal. S'il indique failed, le service a tenté de démarrer et s'est arrêté de manière inattendue.
  • Exit Code: Ce code numérique, souvent affiché avec Active: failed, est vital. Il indique pourquoi le processus s'est terminé (par exemple, 0 pour une sortie propre, 1 ou 2 pour des erreurs générales d'application, 203 pour des erreurs de chemin d'exécution).
  • Journaux récents : Systemd inclut souvent les dernières lignes de sortie de journal du service, ce qui peut révéler instantanément l'erreur.

2. Plongée approfondie dans les journaux avec Journalctl

Alors que systemctl status donne un résumé, journalctl fournit le contexte complet de l'historique d'exécution du service, y compris les flux de sortie standard et d'erreur standard.

Utilisez la commande suivante pour afficher le journal spécifiquement pour votre service défaillant, en utilisant le drapeau -x pour l'explication et le drapeau -e pour sauter à la fin (entrées les plus récentes) :

$ journalctl -xeu myapp.service

Astuce : Si la panne s'est produite il y a des heures ou des jours, utilisez les options de filtrage temporel, comme journalctl -u myapp.service --since "2 hours ago".


Diagnostic étape par étape des pannes courantes

Les pannes systemd tombent généralement dans quelques catégories prévisibles. En examinant le statut et les journaux, vous pouvez rapidement catégoriser le problème et appliquer la solution appropriée.

Type de panne 1 : Erreurs d'exécution (Code de sortie 203)

Un code de sortie de 203/EXEC signifie que systemd n'a pas pu exécuter le fichier spécifié dans la directive ExecStart. C'est l'une des erreurs de configuration les plus courantes.

Causes et solutions :

  1. Chemin incorrect : Le chemin vers l'exécutable est incorrect ou n'est pas absolu.

    • Solution : Utilisez toujours le chemin absolu complet dans ExecStart. Assurez-vous que l'exécutable existe à cet emplacement exact.
    # INCORRECT
    ExecStart=myapp
    
    # CORRECT
    ExecStart=/usr/local/bin/myapp
    
  2. Permissions manquantes : Le fichier ne dispose pas de la permission d'exécution pour l'utilisateur exécutant le service.

    • Solution : Vérifiez et appliquez les permissions d'exécution : chmod +x /chemin/vers/exécutable.
  3. Interpréteur manquant (Shebang) : Si ExecStart pointe vers un script (par exemple, Python ou Bash), la ligne shebang (#!/usr/bin/env python) peut être manquante ou incorrecte, empêchant l'exécution.

    • Solution : Vérifiez que le script a une ligne shebang valide.

Type de panne 2 : Crashs d'application (Code de sortie 1 ou 2)

Si le service démarre avec succès (systemd trouve l'exécutable) mais entre immédiatement dans l'état failed avec un code d'erreur d'application générique (généralement 1 ou 2), le problème réside dans la logique ou l'environnement de l'application.

Causes et solutions :

  1. Erreurs de fichier de configuration : L'application n'a pas pu lire son fichier de configuration requis, ou le fichier contient une syntaxe invalide.

    • Solution : Examinez attentivement la sortie de journalctl. L'application imprime généralement un message d'erreur spécifique concernant le chemin ou la syntaxe du fichier de configuration. Utilisez la directive WorkingDirectory= si les fichiers de configuration sont relatifs.
  2. Conflit de ressources / Refus d'accès : L'application n'a pas réussi à ouvrir un port nécessaire, accéder à une base de données ou écrire dans un fichier journal en raison de restrictions de permissions.

    • Solution : Vérifiez la directive User= dans le fichier de service et assurez-vous que cet utilisateur a un accès en lecture/écriture à toutes les ressources et répertoires nécessaires.

Type de panne 3 : Pannes de dépendance

Le service peut échouer parce qu'il démarre avant qu'une dépendance requise ne soit prête, comme une base de données, une interface réseau ou un système de fichiers monté.

Causes et solutions :

  1. Réseau non prêt : Les services qui nécessitent une connectivité réseau (par exemple, serveurs web, proxys) échouent souvent s'ils démarrent avant que la pile réseau ne soit initialisée.

    • Solution : Si le service a besoin d'une adresse ou d'une route au démarrage, ajoutez l'ordonnancement network-online.target et assurez-vous que le service wait-online de votre distribution est activé pour votre gestionnaire de réseau :
    [Unit]
    Description=Mon service web
    After=network-online.target
    Wants=network-online.target
    
  2. Système de fichiers non monté : Le service tente d'accéder à des fichiers sur un volume qui n'a pas encore été monté (particulièrement critique pour le stockage secondaire ou les montages réseau).

    • Solution : Utilisez RequiresMountsFor= pour indiquer explicitement à systemd quel chemin doit être disponible avant le démarrage.
    [Unit]
    RequiresMountsFor=/mnt/data/storage
    

Type de panne 4 : Problèmes d'utilisateur et d'environnement (Code de sortie 217)

Le code de sortie 217/USER indique souvent une panne liée aux directives d'utilisateur ou de groupe, ou à des variables d'environnement indisponibles.

Causes et solutions :

  1. Utilisateur/Groupe invalide : L'utilisateur spécifié dans la directive User= ou Group= n'existe pas sur le système.

    • Solution : Vérifiez que le nom d'utilisateur existe via id <nom_utilisateur>.
  2. Variables d'environnement manquantes : Les services systemd s'exécutent dans un environnement propre, ce qui signifie que les variables shell (comme PATH ou les clés API personnalisées) ne sont pas héritées.

    • Solution : Définissez les variables nécessaires directement dans le fichier de service ou via un fichier d'environnement.
    [Service]
    # Définition directe
    Environment="API_KEY=ABCDEFG"
    
    # Utilisation d'un fichier externe (par exemple, /etc/sysconfig/myapp)
    EnvironmentFile=/etc/sysconfig/myapp
    

Flux de travail de dépannage et meilleures pratiques

Lors de la modification d'un fichier de service, suivez toujours ce cycle en trois étapes pour garantir que vos modifications sont prises en compte et testées correctement.

1. Valider la syntaxe de configuration

Utilisez systemd-analyze verify pour vérifier le fichier d'unité de service avant de tenter de le démarrer. Cela détecte les erreurs de syntaxe simples.

$ systemd-analyze verify /etc/systemd/system/myapp.service

2. Recharger le démon

Systemd met en cache les fichiers de configuration. Après toute modification d'un fichier d'unité, vous devez dire à systemd de recharger sa configuration.

$ systemctl daemon-reload

3. Redémarrer et vérifier le statut

Tentez de redémarrer le service et vérifiez immédiatement son statut et ses journaux.

$ systemctl restart myapp.service
$ systemctl status myapp.service

Gestion des redémarrages immédiats et des délais d'attente

Si votre service entre dans une boucle de restarting ou échoue immédiatement sans message de journal évident, envisagez d'ajuster ces directives dans la section [Service] :

Directive Objectif Meilleure pratique
Type= Comment systemd gère le processus (par exemple, simple, forking). Utilisez simple sauf si l'application se démonise explicitement.
TimeoutStartSec= Combien de temps systemd attend que le processus principal signale le succès. Augmentez cette valeur si l'application a un démarrage long (par exemple, initialisation d'une grande base de données).
Restart= Définit quand le service doit être redémarré automatiquement (par exemple, always, on-failure). Utilisez on-failure pour les applications de production afin d'éviter des boucles de redémarrage infinies en cas d'erreurs de configuration répétées.

Lire les états d'échec plus attentivement

failed n'est pas le seul mauvais état. Une unité peut être inactive (dead) après une sortie propre, ce qui est normal pour les tâches Type=oneshot mais suspect pour un démon que vous attendiez voir fonctionner en continu. Une unité peut être activating jusqu'à ce que TimeoutStartSec= expire. Une unité peut être active (exited) lorsque la commande s'est terminée et que systemd considère que c'est acceptable. Avant de modifier la politique de redémarrage, assurez-vous que le type de service correspond au programme.

Pour un processus normal de premier plan, commencez par :

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp

Pour un script qui s'exécute une fois et se termine :

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/rotate-reports

Pour les démons plus anciens qui se dupliquent en arrière-plan, Type=forking peut être nécessaire, mais ne l'utilisez pas par habitude. De nombreuses applications modernes restent déjà au premier plan lorsqu'elles sont exécutées sous systemd. Si vous dites à systemd de s'attendre à un fork et que le processus ne bifurque pas comme systemd l'attend, vous pouvez obtenir des échecs de démarrage trompeurs.

Une liste de contrôle de triage qui fonctionne sous pression

Lorsqu'un service est en panne et que les gens attendent, utilisez une séquence fixe :

systemctl status myapp.service --no-pager
journalctl -u myapp.service -b --no-pager
systemctl cat myapp.service
systemctl show myapp.service -p FragmentPath -p User -p Group -p WorkingDirectory -p ExecStart

Recherchez la première véritable erreur, pas la dernière ligne. La dernière entrée du journal peut seulement indiquer que systemd a marqué l'unité comme défaillante. La ligne utile est souvent au-dessus : Permission denied, No such file or directory, Address already in use, Failed at step USER, ou une exception spécifique à l'application.

Si le service a été récemment modifié, vérifiez la syntaxe et l'état de rechargement :

sudo systemd-analyze verify /etc/systemd/system/myapp.service
sudo systemctl daemon-reload

Si systemctl status indique que le fichier d'unité a changé sur le disque, systemd vous avertit que le gestionnaire n'a pas rechargé la nouvelle définition. Redémarrer le service avant daemon-reload peut continuer à utiliser des paramètres obsolètes.

Problèmes de permission qui ne ressemblent pas à des problèmes de permission

Un service peut fonctionner parfaitement depuis votre shell et échouer sous systemd parce qu'il ne s'exécute pas en tant que vous. Vérifiez User=, Group=, WorkingDirectory=, et toutes les options de durcissement telles que ProtectSystem=, ReadWritePaths=, PrivateTmp=, ou NoNewPrivileges=.

Par exemple :

[Service]
User=webapp
WorkingDirectory=/srv/webapp
ExecStart=/srv/webapp/bin/server
ReadWritePaths=/srv/webapp/var
ProtectSystem=strict

Avec ProtectSystem=strict, la majeure partie du système de fichiers est en lecture seule pour le service. C'est un bon paramètre de durcissement, mais cela signifie que l'application ne peut écrire que sur les chemins que vous autorisez explicitement. Si le journal indique que l'application ne peut pas créer un fichier PID, un fichier cache, une base de données SQLite ou un répertoire de téléchargement, le sandboxing de l'unité en est peut-être la raison.

Vérifiez également les permissions des répertoires parents. L'exécutable peut être en mode 755, mais si /srv/webapp n'est pas accessible par l'utilisateur du service, systemd échouera toujours à l'exécuter. Utilisez :

namei -l /srv/webapp/bin/server
sudo -u webapp /srv/webapp/bin/server --check-config

Exécuter une vérification de configuration sécurisée en tant qu'utilisateur du service détecte de nombreux problèmes sans démarrer le démon complet.

Boucles de redémarrage et limites de taux

Restart=on-failure est utile, mais il peut cacher l'erreur d'origine dans un flot de démarrages répétés. Systemd applique également une limitation du taux de démarrage. Lorsqu'un service échoue trop de fois dans une courte fenêtre, vous pouvez voir start-limit-hit.

Commandes utiles :

systemctl status myapp.service
systemctl reset-failed myapp.service
sudo systemctl start myapp.service

reset-failed ne corrige pas la cause. Il efface seulement l'état d'échec de systemd et la mémoire de limitation de taux afin que vous puissiez tester à nouveau après avoir apporté une modification. Si vous en avez constamment besoin, ralentissez et corrigez la première panne dans le journal.

Débogage des problèmes persistants

Si les journaux standard ne révèlent pas le problème, l'application redirige peut-être sa sortie.

  • Examinez StandardOutput et StandardError : Par défaut, ils sont dirigés vers le journal. S'ils sont définis sur /dev/null ou un fichier, vous devez vérifier ces emplacements directement pour les messages d'erreur.
  • Verbose temporaire : Si possible, configurez temporairement l'application (ou ses arguments de ligne de commande dans ExecStart) pour qu'elle s'exécute avec une verbosité maximale (par exemple, --debug ou -v) afin de générer une sortie de journal plus détaillée en cas d'échec.

Un point d'arrêt raisonnable

Une fois le service démarré, vérifiez encore une chose : s'il fait réellement du travail. systemctl status ne peut vous dire que l'état du processus du point de vue de systemd. Un service web peut être actif tout en renvoyant des 500. Un worker peut être actif tout en échouant à chaque tâche. Après avoir corrigé le problème au niveau de l'unité, exécutez la propre vérification de santé de l'application, consultez ses journaux d'application et confirmez que la dépendance avec laquelle il communique est accessible.

Pour la plupart des incidents, le chemin utile est court : systemctl status, puis journalctl -u, puis inspectez l'unité avec systemctl cat, puis testez la commande en tant qu'utilisateur de service configuré. Cela vous maintient près des preuves et loin des modifications aléatoires de fichiers d'unité.

Notez la cause finale dans le runbook du service ou les notes de déploiement pendant que c'est encore frais. "Systemd réparé" n'est pas utile plus tard. "Le service a échoué avec 203/EXEC parce que le déploiement a créé /opt/app/current/bin/server sans permission d'exécution" est utile. Le prochain incident rime généralement avec le précédent.