Provoquer l'exécution d'un script après le démarrage de la mise en réseau?

102

Je suis relativement nouveau chez systemd et j'apprends son architecture.

En ce moment, j'essaie de trouver un moyen d'exécuter un script shell personnalisé. Ce script doit être exécuté après le démarrage de la couche réseau.

J'utilise Arch, en utilisant systemd et netctl.

Pour tester, j'ai écrit un script simple qui s'exécute simplement ip addr list > /tmp/ip.txt. J'ai créé le fichier de service suivant pour ce script.

(/etc/systemd/system/test.service)
[Unit]
Description=test service

[Service]
ExecStart=/root/test.script

[Install]
WantedBy=multi-user.target

J'ai ensuite activé le script avec,

systemctl enable test

Au redémarrage, le script est effectivement exécuté, mais il est exécuté avant le démarrage du réseau. En d'autres termes, la sortie dans ip.txtn'affiche aucune adresse IPv4 attribuée à l'interface principale. Au moment de ma connexion, l'adresse IPv4 a bien été attribuée et la mise en réseau est terminée.

Je suppose que je pourrais modifier le point auquel le script est exécuté en modifiant le WantedByparamètre, mais je ne sais pas comment faire.

Quelqu'un peut-il m'indiquer la bonne direction?

Millions
la source

Réponses:

126

Dépendances de la configuration réseau de systemd

Il est très facile d’affecter la commande d’unités de systemd. D'autre part, vous devez faire attention à ce qu'une unité achevée garantit.

Configurez votre service

Sur les systèmes actuels, commander après network.targetgarantit simplement que le service réseau a été démarré, et non qu'il existe une configuration réelle. Vous devez commander après network-online.targetet le tirer pour y parvenir.

[Unit]
Wants=network-online.target
After=network-online.target

Pour assurer la compatibilité avec les systèmes plus anciens, vous devrez peut-être aussi commander après network.target.

[Unit]
Wants=network-online.target
After=network.target network-online.target

C'est pour le fichier d'unité de votre service et pour systemd.

Implémentation dans les versions actuelles du logiciel

Maintenant, vous devez vous assurer que cela network-online.targetfonctionne comme prévu (ou que vous pouvez au moins l'utiliser network.target).

La version actuelle de NetworkManager offre le NetworkManager-wait-online.servicequi est tiré par network-online.targetet donc par votre service. Ce service spécial garantit que votre service attendra que toutes les connexions configurées pour être démarrées réussissent, échouent ou dépassent automatiquement le délai imparti.

La version actuelle de systemd-networkd bloque votre service jusqu'à ce que tous les périphériques soient configurés comme demandé. Il est plus facile dans la mesure où il ne prend actuellement en charge que les configurations appliquées au démarrage (plus précisément au moment du démarrage de `systemd-networkd.service).

Par souci d'exhaustivité, le /etc/init.d/networkservice dans Fedora, tel qu'interprété par les versions actuelles de systemd, bloque network.targetet donc, indirectement, bloque network-online.targetet votre service. C'est un exemple d'implémentation basée sur un script.

Si votre implémentation, qu'elle soit basée sur un démon ou sur un script, se comporte comme l'un des services de gestion de réseau ci-dessus, elle retardera le démarrage de votre service jusqu'à ce que la configuration du réseau soit terminée avec succès, échoue pour une bonne raison ou expire après un délai raisonnable. cadre à compléter.

Vous voudrez peut-être vérifier si netctl fonctionne de la même manière et que ces informations constitueraient un ajout précieux à cette réponse.

Implémentations dans les anciennes versions du logiciel

Je ne pense pas que vous verrez une version suffisamment ancienne de systemd où cela ne fonctionnerait pas bien. Mais vous pouvez vérifier qu'il network-online.targetexiste au moins et qu'il est commandé après network.target.

Auparavant, NetworkManager garantissait uniquement qu’au moins une connexion serait appliquée. Et même pour que cela fonctionne, vous devez l'activer NetworkManager-wait-online.serviceexplicitement. Ce problème a été corrigé depuis longtemps dans Fedora, mais il n'a été appliqué que récemment en amont.

systemctl enable NetworkManager-wait-online.service

Notes sur les implémentations network.target et network-online.target

Vous ne devriez pas avoir besoin de faire de votre logiciel dépendra NetworkManager.serviceou NetworkManager-wait-online.serviceni aucun autre services. Au lieu de cela, tous les services de gestion de réseau doivent se commander avant network.targetet éventuellement network-online.target.

Un simple service de gestion de réseau basé sur un script doit terminer la configuration du réseau avant de quitter et doit se commander avant network.targetet donc indirectement avant network-online.target.

[Unit]
Before=network.target

[Service]
Type=oneshot
ExecStart=...
RemainAfterExit=yes

Un service de gestion de réseau basé sur un démon doit également se commander avant network.targetmême s’il n’est pas très utile.

[Unit]
Before=network.target

[Service]
Type=simple
ExecStart=...

Un service qui attend la fin du démon doit se commander lui-même après le service spécifique et avant network-online.target. Il doit utiliser Requisitele service démon pour qu’il échoue immédiatement si le service de gestion de réseau correspondant n’est pas utilisé.

[Unit]
Requisite=...
After=...
Before=network-online.target

[Service]
Type=oneshot
ExecStart=...
RemainAfterExit=yes

Le paquet doit installer un lien symbolique vers le service en attente dans le wantsrépertoire network-online.targetafin que celui-ci soit attiré par les services qui souhaitent attendre le réseau configuré.

ln -s /usr/lib/systemd/system/... /usr/lib/systemd/system/network-online.target.wants/

Documentation connexe

Notes finales

J'espère avoir non seulement aidé à répondre à votre question au moment où vous l'avez posée, mais également à améliorer la situation des distributions amont et Linux, afin que je puisse maintenant donner une meilleure réponse que celle qui était possible au moment de la rédaction de l'original. .

Pavel imeri
la source
Voulez-vous dire l'option de connexion automatique par "attendre que toutes les connexions configurées pour être démarrées réussissent automatiquement"? Puis-je tirer parti de cela lorsque je définis no-auto-default = * mais que j'ai autoconnect = yes sur l'une de mes connexions? Et dernière question - je ne comprends pas - l’option d’attente au démarrage de la page de manuel et de nm-online n’aide pas grand-chose. Merci pour cet article très apprécié!
Lzap
Autant que je sache, nm-online ne s'en soucie pas no-auto-defaultseulement auto. Avez-vous une question spécifique? À mon avis, la page de manuel nm-online indique clairement -squ’elle attend que toutes les connexions automatiques soient tentées, c’est-à-dire qu’elles soient connectées ou en échec.
Pavel Šimerda
Après avoir joué avec cette merde pendant une heure, j'ai trouvé la solution: apt-get install sysv-init. :-) La complexité que systemd ajoute en remplacement de quelques scripts shell est ahurissante.
Quelqu'un
@ Quelqu'un je crains que les initscripts ne soient pas une réponse dans ce cas. Si vous utilisez NetworkManager ou tout autre outil de configuration dynamique, les initscripts ne peuvent pas se commander eux-mêmes après un réseau entièrement configuré. Vous pouvez obtenir une configuration dynamique limitée en utilisant /etc/init.d/networkou similaire, mais cela ne fonctionne pas universellement.
Pavel Šimerda
@Pavel Šimerda Init s'exécute en série, et un script d'initialisation correct ne sera renvoyé que lorsque les scripts suivants devront être utilisés. Pour la mise en réseau, cela signifie que tous les adaptateurs applicables sont disponibles. À moins que le moment ne soit propice, NM se comporte bien dans ce contexte. Le problème réel est bien entendu que NM réinvente la gestion du réseau au lieu de s’appuyer sur des structures simples, éprouvées et éprouvées. Les ordinateurs de bureau ne semblent pas avoir la moindre idée des dangers de la complexité. ;-)
Quelqu'un
9

Vous pouvez utiliser Afterdans la [Unit]section pour définir un service devant être démarré avant le démarrage de votre service. Par exemple, si vous utilisez NetworkManager, vous pouvez faire démarrer votre service après le démarrage de NetworkManager.

[Unit]
Description=test service
After=NetworkManager.service
phoops
la source
BindsTon’est pas aussi approprié ici car le service est un événement ponctuel et non un service persistant (à moins qu’il inclue également une ExecStopfonction à déclencher lorsque le réseau tombe en panne).
goldilocks
enlevéBindsTo
phoops
Vous pouvez toutefois ajouter quelque chose à remplacer BindsTo, par exemple, Requiressi vous souhaitez que le service ne soit exécuté que si NetworkManager le fait. Afterne fait pas réellement cela - cela signifie simplement que si NM est également en cours d'exécution, exécutez ceci par la suite. Si NM ne doit pas être exécuté, le service sera exécuté à un moment quelconque.
goldilocks
4
After = network.target est préférable à After = NetworkManager.service car il est plus générique.
Pavel Šimerda le
7
Notez que la spécification neAfter=foo fera pasfoo démarrer l' unité si elle n'est pas déjà démarrée, elle dira uniquement à systemd comment commander les unités si elles sont toutes les deux démarrées en même temps . Utilisant à la fois After=fooainsi que Wants=fooou Requires=fooaura pour effet de tirer dans foosi elle est pas démarré, et aussi faire pour systemd les unités correctement.
Emil Lundberg
8

Si votre service fournit un serveur pouvant attendre passivement que quelqu'un s'y connecte, utilisez ceci:

[Unit]
After=network.target

Votre service doit être lié sur l'interface générique. S'il utilise l'activation de socket (recommandé) ou s'il est uniquement local, vous pouvez ignorer complètement les cibles réseau.

Si votre service agit en tant que client, ou est peer to peer, ceci est plus approprié:

[Unit]
After=network-online.target
Requires=network-online.target

Avant la version 213 , network-online.target a besoin de la solution mentionnée par Pavel (vous devez activer manuellement un service qui attend que le réseau soit opérationnel). A partir de la version 213, cela est fait par défaut. systemd-networkd-wait-onlineattendra qu’au moins une adresse (routable ou link-local) soit configurée sur une interface sans bouclage.

La configuration de systemd-networkd, NetworkManager ou équivalente est une tâche indépendante. Les protocoles DHCP (pour IPv4) et NDP (pour IPv6) ont tendance à fonctionner immédiatement, mais vous devez les configurer de manière à ce que votre définition précise de «le réseau est opérationnel» déclenche network-online.target.

Documentation:

Tobu
la source
Juste curieux de savoir pourquoi une nouvelle réponse et pas seulement de petites améliorations à la réponse existante et (espérons-le) bien structurée.
Pavel Šimerda
Les deux premiers liens de documentation sont actuellement obsolètes.
Peter Hansen
Pourquoi utiliser requiert au lieu de vouloir?
Karl Morrison
4

J'imagine que je pourrais modifier le point d'exécution du script en jouant avec le paramètre WantedBy.

Cela aura l'effet inverse de ce que vous voulez. De man systemd.unit:

WantedBy =, RequiredBy =

[...] Un lien symbolique est créé dans le répertoire .wants / ou .requires / de chacune des unités répertoriées lorsque cette unité est installée par systemctl enable. Cela a pour effet qu'une dépendance de type Wants = ou Requise = est ajoutée à partir de l' unité listée à l' unité en cours .

Sur cette base, nous pouvons voir que l’unité appropriée est "veut" ou "nécessite"; sur la base de la description de ceux-ci, "Nécessite" est probablement correcte, avec l'ajout de "Après" pour garantir non seulement que le service de réseau soit exécuté, mais également avant.

Aucune des options de l'unité, autant que je sache, ne peut spécifier qu'un perquis démarré doit avoir été terminé ou atteint un certain point (la mise en réseau est probablement un service démon), mais uniquement qu'il doit être démarré en premier. Dans cet esprit, vous voudrez peut-être créer votre script Type=forkinget lui attribuer un délai normal (par exemple 30 secondes) ou une sorte de boucle de sortie avec succès, y compris un délai, afin de vous assurer que vous disposez en premier d'un bail DHCP.

boucle d'or
la source
1
Ni WantedBy ni RequiredBy n'affectent la commande.
Pavel Šimerda le
1
@ PavelŠimerda: Personne ici n'a prétendu l'avoir fait. La commande est la raison pour laquelle j’ai explicitement mentionné Afteren conjonction avec Requires«pour s’assurer non seulement que le service de réseau soit exécuté, mais qu’il soit exécuté avant cette unité».
goldilocks
1
Oui, Afterfonctionne avec Wantsou de Requirescette façon. D'autre part, les délais explicites sont une mauvaise habitude dans les outils basés sur la dépendance, en particulier lorsqu'il existe un moyen explicite d'attendre la configuration du réseau spécifiée par la documentation de systemd. Je dois donc insister sur le vote négatif.
Pavel Šimerda
3

Utilisez Afterdans la [Unit]section pour spécifier ce qui doit être démarré avant votre propre service. (Cette partie de la réponse précédente est correcte.)

Pour démarrer votre service une fois le réseau mis en place, utilisez la cible réseau, que vous utilisiez NetworkManager, le système conf.d / netctl dans Arch ou tout autre service connu par systemd.

[Unit]
#.....
After=network.target

Un bref aperçu confirmera que tous les autres services de votre système reposant sur la connectivité réseau contiennent cette directive.

Il est également portable pour toute distribution utilisant systemd. Votre fichier d’unités sera le même pour Arch, Fedora, RHEL 7, les futures versions de Debian ...


Les services qui démarrent une connexion réseau, tels que les scripts d'Arch ou les vôtres, doivent le spécifier dans leurs propres fichiers d'unité.

[Unit]
Wants=network.target
Before=network.target
Michael Hampton
la source
Je n'aime pas tout à fait la Wantspartie car elle a des effets secondaires sur d'autres paquets Regardez ma réponse, s'il vous plaît.
Pavel Šimerda le
Juste réalisé que Wantssur network.targetest une bonne idée ici.
Pavel Šimerda
vous voulez vraiment utiliser network-online.target. ref
Edward Torvalds
1

Je voulais ajouter un point à cet article. Actuellement (été 2015) dans RHEL7 / CentOS 7, network-online.target est défini de manière incorrecte avant la mise en réseau IPv6, de sorte que les démons ayant

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

dans leur définition de service, qui se lient aussi explicitement aux adresses IPv6, seront probablement lancées avant qu'IPv6 ne soit opérationnel et en cours d'exécution, ce qui entraînerait leur défaillance.

Colo Host
la source
Je suppose que cela n’est le cas que pour la configuration automatique IPv6 basée sur le noyau, qui est de toute façon défectueuse. Si vous souhaitez commander correctement après IPv6, vous devez absolument utiliser NetworkManager au lieu de /etc/init.d/network. Si vous rencontrez le même problème même avec NM, ce serait une bonne raison de déposer une demande de fonctionnalité. Je n'ai pas vérifié avec RHEL / CentOS, je peux vous aider avec les détails si cela vous intéresse.
Pavel Šimerda
0
[Unit]
After=systemd-networkd.service

travaille pour moi.

Zhenxiao Hao
la source
Je ne sais pas si cela fonctionne dans certains cas particuliers, mais c'est faux pour plusieurs raisons. L'un d'eux est celui qui networkdfournit son propre service / wait-online. Arriver et commander après network-online.targetest la bonne façon de procéder avec tout service qui le supporte.
Pavel Šimerda