GNU/Linux >> Tutoriels Linux >  >> Linux

Gérer le démarrage à l'aide de systemd

Lors de la configuration récente d'un système Linux, je voulais savoir comment m'assurer que les dépendances pour les services et autres unités étaient opérationnelles avant le démarrage de ces services et unités dépendants. Plus précisément, j'avais besoin de plus de connaissances sur la façon dont systemd gère la séquence de démarrage, en particulier pour déterminer à quel moment les services de commande sont démarrés dans ce qui est essentiellement un système parallèle.

Vous savez peut-être que SystemV (le prédécesseur de systemd, comme je l'ai expliqué dans le premier article de cette série) ordonne la séquence de démarrage en nommant les scripts de démarrage avec un préfixe SXX, où XX est un nombre compris entre 00 et 99. SystemV utilise alors l'ordre de tri par nom et exécute chaque script de démarrage dans l'ordre pour le niveau d'exécution souhaité.

Mais systemd utilise des fichiers d'unité, qui peuvent être créés ou modifiés par un administrateur système, pour définir des sous-programmes non seulement pour l'initialisation, mais également pour le fonctionnement normal. Dans le troisième article de cette série, j'ai expliqué comment créer un fichier d'unité de montage. Dans ce cinquième article, je montre comment créer un autre type de fichier d'unité :un fichier d'unité de service qui exécute un programme au démarrage. Vous pouvez également modifier certains paramètres de configuration dans le fichier d'unité et utiliser le journal systemd pour afficher l'emplacement de vos modifications dans la séquence de démarrage.

Préparation

Assurez-vous d'avoir supprimé rhgb et quiet depuis le GRUB_CMDLINE_LINUX= ligne dans le /etc/default/grub fichier, comme je l'ai montré dans le deuxième article de cette série. Cela vous permet d'observer le flux de messages de démarrage de Linux, dont vous aurez besoin pour certaines des expériences de cet article.

Le programme

Dans ce tutoriel, vous allez créer un programme simple qui vous permet d'observer un message au démarrage sur la console et plus tard dans le journal systemd.

Créez le programme shell /usr/local/bin/hello.sh et ajoutez le contenu suivant. Vous voulez vous assurer que le résultat est visible au démarrage et que vous pouvez le trouver facilement en parcourant le journal systemd. Vous utiliserez une version du programme "Hello world" avec quelques barres autour, pour qu'il se démarque. Assurez-vous que le fichier est exécutable et qu'il appartient à l'utilisateur et au groupe root avec 700 autorisations pour la sécurité :

#!/usr/bin/bash
# Simple program to use for testing startup configurations
# with systemd.
# By David Both
# Licensed under GPL V2
#
echo "###############################"
echo "######### Hello World! ########"
echo "###############################"

Exécutez ce programme à partir de la ligne de commande pour vérifier qu'il fonctionne correctement :

[root@testvm1 ~]# hello.sh 
###############################
######### Hello World! ########
###############################
[root@testvm1 ~]#

Ce programme peut être créé dans n'importe quel langage de script ou compilé. Le hello.sh programme pourrait également être situé dans d'autres endroits basés sur la structure hiérarchique du système de fichiers Linux (FHS). Je le place dans le /usr/local/bin répertoire afin qu'il puisse être facilement exécuté à partir de la ligne de commande sans avoir à ajouter un chemin lorsque je tape la commande. Je trouve que de nombreux programmes shell que je crée doivent être exécutés à partir de la ligne de commande et par d'autres outils tels que systemd.

Le fichier de l'unité de service

Créez le fichier d'unité de service /etc/systemd/system/hello.service avec le contenu suivant. Ce fichier n'a pas besoin d'être exécutable, mais pour des raisons de sécurité, il doit être propriétaire de l'utilisateur et du groupe par root et des autorisations 644 ou 640 :

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh

[Install]
WantedBy=multi-user.target

Vérifiez que le fichier d'unité de service fonctionne comme prévu en affichant l'état du service. Toutes les erreurs de syntaxe s'afficheront ici :

[root@testvm1 ~]# systemctl status hello.service 
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 ~]#

Vous pouvez exécuter ce type de service "oneshot" plusieurs fois sans problème. Le type oneshot est destiné aux services où le programme lancé par le fichier d'unité de service est le processus principal et doit se terminer avant que systemd ne démarre tout processus dépendant.

Il existe sept types de service, et vous pouvez trouver une explication de chacun (ainsi que des autres parties d'un fichier d'unité de service) dans la page de manuel systemd.service(5). (Vous pouvez également trouver plus d'informations dans les ressources à la fin de cet article.)

Aussi curieux que je sois, je voulais voir à quoi pouvait ressembler une erreur. J'ai donc supprimé le "o" du Type=oneshot ligne, donc ça ressemblait à Type=neshot , et réexécutez la commande :

[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 06 08:50:09 testvm1.both.org systemd[1]: /etc/systemd/system/hello.service:12: Failed to parse service type, ignoring: neshot
[root@testvm1 ~]#

Ces résultats m'ont indiqué précisément où se trouvait l'erreur et ont facilité la résolution du problème.

Plus de ressources Linux

  • Aide-mémoire des commandes Linux
  • Aide-mémoire des commandes Linux avancées
  • Cours en ligne gratuit :Présentation technique de RHEL
  • Aide-mémoire sur le réseau Linux
  • Aide-mémoire SELinux
  • Aide-mémoire sur les commandes courantes de Linux
  • Que sont les conteneurs Linux ?
  • Nos derniers articles Linux

Sachez simplement que même après avoir restauré le hello.service fichier dans sa forme d'origine, l'erreur persistera. Bien qu'un redémarrage efface l'erreur, vous ne devriez pas avoir à le faire, alors j'ai cherché une méthode pour effacer les erreurs persistantes comme celle-ci. J'ai rencontré des erreurs de service qui nécessitent la commande systemctl daemon-reload pour réinitialiser une condition d'erreur, mais cela n'a pas fonctionné dans ce cas. Les messages d'erreur qui peuvent être corrigés avec cette commande semblent toujours avoir une déclaration à cet effet, vous savez donc comment l'exécuter.

Il est cependant recommandé d'exécuter systemctl daemon-reload après avoir modifié un fichier unité ou en avoir créé un nouveau. Cela informe systemd que les modifications ont été apportées et peut empêcher certains types de problèmes liés à la gestion des services ou des unités modifiés. Allez-y et exécutez cette commande.

Après avoir corrigé la faute d'orthographe dans le fichier de l'unité de service, un simple systemctl restart hello.service effacé l'erreur. Expérimentez un peu en introduisant d'autres erreurs dans le hello.service fichier pour voir quels types de résultats vous obtenez.

Démarrer le service

Vous êtes maintenant prêt à démarrer le nouveau service et à vérifier l'état pour voir le résultat. Bien que vous ayez probablement effectué un redémarrage dans la section précédente, vous pouvez démarrer ou redémarrer un service oneshot autant de fois que vous le souhaitez puisqu'il s'exécute une fois puis se ferme.

Allez-y et démarrez le service (comme indiqué ci-dessous), puis vérifiez l'état. Selon le nombre d'erreurs que vous avez expérimentées, vos résultats peuvent différer des miens :

[root@testvm1 ~]# systemctl start hello.service 
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
     Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
     Active: inactive (dead)

May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Notez dans la sortie de la commande status que les messages systemd indiquent que le hello.sh script démarré et le service terminé. Vous pouvez également voir la sortie du script. Cet affichage est généré à partir des écritures comptables des derniers appels du service. Essayez de démarrer le service plusieurs fois, puis exécutez à nouveau la commande status pour voir ce que je veux dire.

Vous devez également consulter directement le contenu du journal; il existe plusieurs façons de le faire. Une façon consiste à spécifier l'identifiant du type d'enregistrement, dans ce cas, le nom du script shell. Cela affiche les entrées de journal pour les redémarrages précédents ainsi que la session en cours. Comme vous pouvez le voir, j'ai fait des recherches et des tests pour cet article depuis un certain temps maintenant :

[root@testvm1 ~]# journalctl -t hello.sh
<snip>
-- Reboot --
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
May 08 15:55:47 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
May 08 16:01:51 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

Pour localiser les enregistrements systemd pour le hello.service unit, vous pouvez effectuer une recherche sur systemd. Vous pouvez utiliser G+Entrée pour faire défiler jusqu'à la fin des entrées de journal, puis faites défiler vers l'arrière pour localiser celles qui vous intéressent. Utilisez le -b option pour afficher uniquement les entrées du démarrage le plus récent :

[root@testvm1 ~]# journalctl -b -t systemd
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:50 testvm1.both.org systemd[1]: Starting D-Bus System Message Bus...
May 10 10:37:50 testvm1.both.org systemd[1]: Started D-Bus System Message Bus.

J'ai copié quelques autres entrées de journal pour vous donner une idée de ce que vous pourriez trouver. Cette commande crache toutes les lignes de journal relatives à systemd - 109 183 lignes lorsque j'ai écrit ceci. Cela fait beaucoup de données à trier. Vous pouvez utiliser la fonction de recherche du téléavertisseur, qui est généralement less , ou vous pouvez utiliser le grep intégré fonctionnalité. Le -g (ou --grep= ) utilise des expressions régulières compatibles Perl :

[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:01:01 EDT. --
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#

Vous pouvez utiliser le standard GNU grep commande, mais cela n'afficherait pas les métadonnées du journal sur la première ligne.

Si vous ne souhaitez pas voir uniquement les entrées de journal relatives à votre hello service, vous pouvez affiner un peu les choses en spécifiant une plage de temps. Par exemple, je commencerai par l'heure de début de 10:54:00 sur ma machine virtuelle de test, qui était le début de la minute d'où proviennent les entrées ci-dessus. Notez que le --since= l'option doit être entourée de guillemets et que cette option peut également être exprimée sous la forme -S "<time specification>" .

La date et l'heure seront différentes sur votre hébergeur, alors assurez-vous d'utiliser les horodatages qui correspondent aux heures de vos journaux :

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:00"
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=54 op=LOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd"'
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/"'
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <error> [1589122560.0633] dhcp4 (enp0s3): error -113 dispatching events
May 10 10:56:00 testvm1.both.org NetworkManager[840]: <info>  [1589122560.0634] dhcp4 (enp0s3): state changed bound -> fail
<snip>

Le since La spécification ignore toutes les entrées avant cette heure, mais il y a encore beaucoup d'entrées après cette heure dont vous n'avez pas besoin. Vous pouvez également utiliser le until possibilité de supprimer les entrées qui arrivent un peu après l'heure qui vous intéresse. Je veux la minute entière où l'événement s'est produit et rien de plus :

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:04:59 EDT. --
May 10 10:54:35 testvm1.both.org systemd[1]: Reloading.
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=27 op=UNLOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=26 op=UNLOAD
<snip>
ay 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
lines 1-46/46 (END)

S'il y a eu beaucoup d'activité au cours de cette période, vous pouvez affiner davantage le flux de données résultant en utilisant une combinaison de ces options :

[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" -t "hello.sh"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:10:41 EDT. --
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#

Vos résultats devraient être similaires aux miens. Vous pouvez voir à partir de cette série d'expériences que le service s'est exécuté correctement.

Redémarrer—enfin

Jusqu'à présent, vous n'avez pas redémarré l'hôte sur lequel vous avez installé votre service. Alors faites-le maintenant car, après tout, ce guide consiste à exécuter un programme au démarrage. Tout d'abord, vous devez activer le lancement du service lors de la séquence de démarrage :

[root@testvm1 ~]# systemctl enable hello.service 
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
[root@testvm1 ~]#

Notez que le lien a été créé dans le /etc/systemd/system/multi-user.target.wants annuaire. En effet, le fichier d'unité de service spécifie que le service est "voulu" par le multi-user.target .

Redémarrez et assurez-vous de regarder le flux de données pendant la séquence de démarrage pour voir le message "Hello world". Attendez… vous ne l'avez pas vu ? Eh bien, moi non plus. Bien que cela soit passé très vite, j'ai vu le message de systemd indiquant qu'il démarrait le hello.service .

Regardez le journal depuis le dernier démarrage du système. Vous pouvez utiliser le less l'outil de recherche du téléavertisseur pour trouver "Hello" ou "hello". J'ai supprimé de nombreuses lignes de données, mais j'ai laissé certaines des entrées de journal environnantes, afin que vous puissiez avoir une idée de ce à quoi ressemblent les entrées relatives à votre service localement :

[root@testvm1 ~]# journalctl -b
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Listening on SSSD Kerberos Cache Manager responder socket.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Sockets.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Basic System.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Modem Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Network Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Avahi mDNS/DNS-SD Stack...
May 10 10:37:49 testvm1.both.org systemd[1]: Condition check resulted in Secure Boot DBX (blacklist) updater being skipped.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
May 10 10:37:49 testvm1.both.org systemd[1]: Started irqbalance daemon.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=irqbalance comm="systemd" exe="/usr/lib/sy>"'
May 10 10:37:49 testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Hardware Monitoring Sensors...
<snip>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting NTP client/server...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=livesys-late comm="systemd" exe="/usr/lib/>"'
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>"'
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
May 10 10:37:50 testvm1.both.org audit: BPF prog-id=28 op=LOAD
<snip>

Vous pouvez voir que systemd a démarré le hello.service unité, qui a exécuté le hello.sh script shell avec la sortie enregistrée dans le journal. Si vous pouviez l'attraper lors du démarrage, vous auriez également vu le message systemd indiquant qu'il démarrait le script et un autre message indiquant que le service avait réussi. En regardant le premier message systemd dans le flux de données ci-dessus, vous pouvez voir que systemd a démarré votre service très peu de temps après avoir atteint la cible système de base.

Mais j'aimerais aussi voir le message affiché au démarrage. Il existe un moyen d'y parvenir :ajoutez la ligne suivante au [Service] section du hello.service fichier :

StandardOutput=journal+console

Le hello.service le fichier ressemble maintenant à ceci :

# Simple service unit file to use for testing 
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=My hello shell script

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target

Après avoir ajouté cette ligne, redémarrez le système et observez le flux de données pendant qu'il défile sur l'écran pendant le processus de démarrage. Vous devriez voir le message dans sa petite boîte. Une fois la séquence de démarrage terminée, vous pouvez afficher le journal du démarrage le plus récent et localiser les entrées de votre nouveau service.

Modification de la séquence

Maintenant que votre service fonctionne, vous pouvez regarder où il commence dans la séquence de démarrage et essayer de le modifier. Il est important de se rappeler que l'intention de systemd est de démarrer autant de services et d'autres types d'unités en parallèle dans chacune des cibles principales :basic.target , multi-user.target , et graphical.target . Vous devriez avoir vu les entrées de journal pour le démarrage le plus récent, qui devraient ressembler à mon journal dans la sortie ci-dessus.

Notez que systemd a démarré votre service de test peu de temps après avoir atteint le système de base cible. C'est ce que vous avez spécifié dans le fichier d'unité de service dans le WantedBy ligne, donc c'est correct. Avant de changer quoi que ce soit, répertoriez le contenu de /etc/systemd/system/multi-user.target.wants répertoire, et vous verrez un lien symbolique (logiciel) vers le fichier de l'unité de service. Le [Install] section du fichier d'unité de service spécifie quelle cible démarrera le service et exécutera le systemctl enable hello.service La commande crée le lien dans le répertoire "target want" approprié :

hello.service -> /etc/systemd/system/hello.service

Certains services doivent démarrer pendant le basic.target , et d'autres n'ont pas besoin de démarrer sauf si le système démarre le graphical.target . Le service de cette expérience ne démarrera pas dans basic.target - supposons que vous n'en ayez pas besoin pour démarrer jusqu'à ce que le graphical.target . Alors changez le WantedBy ligne :

WantedBy=graphical.target

Assurez-vous de désactiver le hello.service et réactivez-le pour supprimer l'ancien lien et ajouter le nouveau dans le graphical.targets.wants annuaire. J'ai remarqué que si j'oublie de désactiver le service avant de changer la cible qui le souhaite, je peux exécuter le systemctl disable commande, et les liens seront supprimés des deux répertoires "cible veut". Ensuite, il me suffit de réactiver le service et de redémarrer.

Un souci avec le démarrage des services dans le graphical.target est que si l'hôte démarre sur multi-user.target , ce service ne démarrera pas automatiquement. C'est peut-être ce que vous voulez si le service nécessite une interface de bureau graphique, mais ce n'est peut-être pas non plus ce que vous voulez.

Regardez les entrées de journal pour le graphical.target et le multi-user.target en utilisant le -o short-monotonic option qui affiche les secondes après le démarrage du noyau avec une précision à la microseconde :

[root@testvm1 ~]# journalctl -b -o short-monotonic

Quelques résultats pour multi-user.target :

[   17.264730] testvm1.both.org systemd[1]: Starting My hello shell script...
[   17.265561] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.478468] testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
[   19.507359] testvm1.both.org iptables.init[844]: iptables: Applying firewall rules: [  OK  ]
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
[   19.507835] testvm1.both.org hello.sh[843]: ######### Hello World! ########
[   19.507835] testvm1.both.org hello.sh[843]: ###############################
<SNIP>
[   21.482481] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   21.482550] testvm1.both.org smartd[856]: Opened configuration file /etc/smartmontools/smartd.conf
[   21.482605] testvm1.both.org systemd[1]: Finished My hello shell script.

Et quelques résultats pour graphical.target :

[   19.436815] testvm1.both.org systemd[1]: Starting My hello shell script...
[   19.437070] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<SNIP>
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.612614] testvm1.both.org hello.sh[841]: ######### Hello World! ########
[   19.612614] testvm1.both.org hello.sh[841]: ###############################
[   19.629455] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629569] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   19.629682] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   19.629782] testvm1.both.org systemd[1]: Finished My hello shell script.

Malgré le graphical.target "want" dans le fichier unité, le hello.service l'unité fonctionne environ 19,5 ou 19,6 secondes après le démarrage. Mais hello.service commence à environ 17,24 secondes dans le multi-user.target et 19,43 secondes dans la cible graphique.

Qu'est-ce que ça veut dire? Regardez le /etc/systemd/system/default.target lien. Le contenu de ce fichier montre que systemd démarre d'abord la cible par défaut, graphical.target , qui extrait ensuite le multi-user.target :

[root@testvm1 system]# cat default.target
#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
[root@testvm1 system]#

S'il démarre le service avec le graphical.target ou le multi-user.target , le hello.service l'unité fonctionne à environ 19,5 ou 19,6 secondes après le démarrage. Sur la base de cela et des résultats du journal (en particulier ceux utilisant la sortie monotone), vous savez que ces deux cibles démarrent en parallèle. Regardez encore une chose dans la sortie du journal :

[   28.397330] testvm1.both.org systemd[1]: Reached target Multi-User System.
[   28.397431] testvm1.both.org systemd[1]: Reached target Graphical Interface.

Les deux cibles finissent presque en même temps. Ceci est cohérent car le graphical.target récupère le multi-user.target et ne peut pas se terminer avant la multi.user target est atteint, c'est-à-dire terminé. Mais  hello.service se termine beaucoup plus tôt que cela.

Tout cela signifie que ces deux cibles démarrent à peu près en parallèle. Si vous explorez les entrées de journal, vous verrez différentes cibles et services de chacune de ces cibles principales commençant principalement en parallèle. Il est clair que le multi-user.target n'a pas besoin de se terminer avant le graphical.target départs. Par conséquent, utiliser simplement ces cibles principales pour séquencer le démarrage ne fonctionne pas très bien, même si cela peut être utile pour s'assurer que les unités ne sont démarrées que lorsqu'elles sont nécessaires pour le graphical.target .

Avant de continuer, rétablissez le hello.service fichier d'unité vers WantedBy=multi-user.target (si ce n'est déjà fait.)

S'assurer qu'un service démarre une fois que le réseau est en cours d'exécution

Un problème de séquence de démarrage courant consiste à s'assurer qu'une unité démarre une fois que le réseau est opérationnel. L'article de Freedesktop.org Exécuter les services après que le réseau est en place dit qu'il n'y a pas de véritable consensus sur le moment où un réseau est considéré comme "en place". Cependant, l'article propose trois options, et celle qui répond aux besoins d'un réseau pleinement opérationnel est network-online.target . Sachez simplement que network.target est utilisé lors de l'arrêt plutôt que du démarrage, il ne vous servira donc à rien lorsque vous essayez de séquencer le démarrage.

Avant d'apporter d'autres modifications, assurez-vous d'examiner le journal et de vérifier que le hello.service l'unité démarre bien avant le réseau. Vous pouvez rechercher le network-online.target dans le journal pour vérifier.

Votre service ne nécessite pas vraiment le service réseau, mais vous pouvez l'utiliser comme avatar pour celui qui en a besoin.

Parce que définir WantedBy=graphical.target ne garantit pas que le service sera démarré une fois le réseau opérationnel, vous avez besoin d'un autre moyen pour vous en assurer. Heureusement, il existe un moyen simple de le faire. Ajoutez les deux lignes suivantes au [Unit] section du hello.service fichier unité :

After=network-online.target                                                                             
Wants=network-online.target

Both of these entries are required to make this work. Reboot the host and look for the location of entries for your service in the journals:

[   26.083121] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0293] device (enp0s3): Activation: successful, device activated.
[   26.083349] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0301] manager: NetworkManager state is now CONNECTED_GLOBAL
[   26.085818] testvm1.both.org NetworkManager[842]: <info>  [1589227764.0331] manager: startup complete
[   26.089911] testvm1.both.org systemd[1]: Finished Network Manager Wait Online.
[   26.090254] testvm1.both.org systemd[1]: Reached target Network is Online.
[   26.090399] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=NetworkManager-wait-online comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? termina>"'
[   26.091991] testvm1.both.org systemd[1]: Starting My hello shell script...
[   26.095864] testvm1.both.org sssd[be[implicit_files]][1007]: Starting up
[   26.290539] testvm1.both.org systemd[1]: Condition check resulted in Login and scanning of iSCSI devices being skipped.
[   26.291075] testvm1.both.org systemd[1]: Reached target Remote File Systems (Pre).
[   26.291154] testvm1.both.org systemd[1]: Reached target Remote File Systems.
[   26.292671] testvm1.both.org systemd[1]: Starting Notify NFS peers of a restart...
[   26.294897] testvm1.both.org systemd[1]: iscsi.service: Unit cannot be reloaded because it is inactive.
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.304682] testvm1.both.org hello.sh[1010]: ######### Hello World! ########
[   26.304682] testvm1.both.org hello.sh[1010]: ###############################
[   26.306569] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306669] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[   26.306772] testvm1.both.org systemd[1]: hello.service: Succeeded.
[   26.306862] testvm1.both.org systemd[1]: Finished My hello shell script.
[   26.584966] testvm1.both.org sm-notify[1011]: Version 2.4.3 starting

This confirms that the hello.service unit started after the network-online.target . This is exactly what you want. You may also have seen the "Hello World" message as it passed by during startup. Notice also that the timestamp is about six seconds later in the startup than it was before.

The best way to define the start sequence

This article explored Linux startup with systemd and unit files and journals in greater detail and discovered what happens when errors are introduced into the service file. As a sysadmin, I find that this type of experimentation helps me understand the behaviors of a program or service when it breaks, and breaking things intentionally is a good way to learn in a safe environment.

As the experiments in this article proved, just adding a service unit to either the multi-user.target or the graphical.target does not define its place in the start sequence. It merely determines whether a unit starts as part of a graphical environment or not. The reality is that the startup targets multi-user.target and graphical.target —and all of their Wants and Requires—start up pretty much in parallel. The best way to ensure that a unit starts in a specific order is to determine the unit it is dependent on and configure the new unit to "Want" and "After" the unit upon which it is dependent.

Ressources

De nombreuses informations sur systemd sont disponibles sur Internet, mais la plupart sont concises, obtuses ou même trompeuses. En plus des ressources mentionnées dans cet article, les pages Web suivantes offrent des informations plus détaillées et fiables sur le démarrage de systemd.

  • Le projet Fedora propose un bon guide pratique de systemd. Il contient à peu près tout ce que vous devez savoir pour configurer, gérer et entretenir un ordinateur Fedora à l'aide de systemd.
  • Le projet Fedora a également une bonne feuille de triche qui renvoie les anciennes commandes SystemV à des commandes systemd comparables.
  • Pour des informations techniques détaillées sur systemd et les raisons de sa création, consultez la description de systemd par Freedesktop.org.
  • Linux.com's "More systemd fun" propose des informations et des conseils plus avancés sur systemd.

Il existe également une série d'articles profondément techniques pour les administrateurs système Linux par Lennart Poettering, le concepteur et développeur principal de systemd. Ces articles ont été écrits entre avril 2010 et septembre 2011, mais ils sont tout aussi pertinents aujourd'hui qu'ils l'étaient alors. Une grande partie de tout ce qui a été écrit sur systemd et son écosystème est basé sur ces articles.

  • Repenser le PID 1
  • systemd pour les administrateurs, partie I
  • systemd pour les administrateurs, partie II
  • systemd pour les administrateurs, partie III
  • systemd pour les administrateurs, partie IV
  • systemd pour les administrateurs, partie V
  • systemd pour les administrateurs, partie VI
  • systemd pour les administrateurs, partie VII
  • systemd pour les administrateurs, partie VIII
  • systemd pour les administrateurs, partie IX
  • systemd pour les administrateurs, partie X
  • systemd pour les administrateurs, partie XI

Linux
  1. Utilisation de Logrotate sous Linux pour gérer les fichiers journaux (avec exemples)

  2. Comment écrire un script de démarrage pour Systemd ?

  3. Comment utiliser Systemd pour redémarrer un service en panne ?

  4. Systemd :Utiliser à la fois After et Requires

  5. rediriger les journaux du service systemd vers un fichier

Commandes Systemctl pour gérer le service Systemd

Utiliser les fonctionnalités de systemd pour sécuriser les services

Comment configurer l'exécution automatique d'un script Python à l'aide de Systemd

Manière correcte d'utiliser Ubuntu systemctl pour contrôler Systemd

Utilisation de la variable dans le chemin de commande pour ExecStart dans le service systemd

Le fichier de service existe mais n'est pas trouvé par systemd