Kann ein Skript ausgeführt werden, nachdem das Netzwerk gestartet wurde?

102

Ich bin relativ neu in systemd und lerne seine Architektur.

Im Moment versuche ich herauszufinden, wie ein benutzerdefiniertes Shell-Skript ausgeführt werden kann. Dieses Skript muss ausgeführt werden, nachdem die Netzwerkebene gestartet wurde.

Ich führe Arch aus und verwende sowohl systemd als auch netctl.

Zum Testen habe ich ein einfaches Skript geschrieben, das einfach ausgeführt wird ip addr list > /tmp/ip.txt. Ich habe die folgende Servicedatei für dieses Skript erstellt.

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

[Service]
ExecStart=/root/test.script

[Install]
WantedBy=multi-user.target

Ich habe dann das Skript mit aktiviert,

systemctl enable test

Nach dem Neustart wird das Skript zwar ausgeführt, jedoch vor dem Start des Netzwerks. Mit anderen Worten, die Ausgabe in ip.txtzeigt keine der primären Schnittstelle zugewiesene IPv4-Adresse an. Bei der Anmeldung wurde die IPv4-Adresse tatsächlich zugewiesen, und das Netzwerk ist aktiv.

Ich schätze, ich könnte den Punkt, an dem das Skript ausgeführt wird, ändern, indem WantedByich mit dem Parameter herumspiele , aber ich bin mir nicht sicher, wie ich das machen soll.

Könnte mich jemand in die richtige Richtung weisen?

fdmillion
quelle

Antworten:

126

Abhängigkeiten der System-Netzwerkkonfiguration

Es ist sehr einfach, die Einheitenbestellung von systemd zu beeinflussen. Andererseits müssen Sie vorsichtig sein, was eine fertige Einheit garantiert.

Konfigurieren Sie Ihren Dienst

Bei aktuellen Systemen network.targetgarantiert die Bestellung nach nur, dass der Netzwerkdienst gestartet wurde, nicht, dass eine tatsächliche Konfiguration vorliegt. Sie müssen danach bestellen network-online.targetund es hineinziehen, um das zu erreichen.

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

Für die Kompatibilität mit älteren Systemen müssen Sie möglicherweise auch nach network.target bestellen.

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

Das ist für die Unit-Datei Ihres Dienstes und für systemd.

Implementierung in aktuelle Softwareversionen

Jetzt müssen Sie sicherstellen, dass dies network-online.targetwie erwartet funktioniert (oder zumindest verwendet werden kann network.target).

Die aktuelle Version von NetworkManager bietet das, NetworkManager-wait-online.servicewas von network-online.targetund damit von Ihrem Dienst abgerufen wird. Dieser spezielle Dienst stellt sicher, dass Ihr Dienst wartet, bis alle Verbindungen, die für den automatischen Start konfiguriert sind, erfolgreich sind, fehlschlagen oder eine Zeitüberschreitung aufweisen.

Die aktuelle Version von systemd-networkd blockiert Ihren Dienst, bis alle Geräte wie angefordert konfiguriert sind. Es ist einfacher, da derzeit nur Konfigurationen unterstützt werden, die beim Booten angewendet werden (genauer gesagt die Startzeit von `systemd-networkd.service).

Der Vollständigkeit halber /etc/init.d/networkblockiert der Dienst in Fedora, wie von den aktuellen Versionen von systemd interpretiert, network.targetund damit indirekt auch network-online.targetIhren Dienst. Es ist ein Beispiel für eine Skript-basierte Implementierung.

Wenn sich Ihre Implementierung, ob daemon- oder skriptbasiert, als einer der oben genannten Netzwerkverwaltungsdienste verhält, verzögert sie den Start Ihres Dienstes, bis die Netzwerkkonfiguration entweder erfolgreich abgeschlossen wurde, aus gutem Grund fehlgeschlagen ist oder nach einer angemessenen Zeit abgelaufen ist Rahmen zu vervollständigen.

Möglicherweise möchten Sie überprüfen, ob netctl auf die gleiche Weise funktioniert, und diese Informationen sind eine wertvolle Ergänzung zu dieser Antwort.

Implementierungen in älteren Softwareversionen

Ich glaube nicht, dass Sie eine ausreichend alte Version von systemd sehen werden, bei der dies nicht gut funktioniert. Aber Sie können prüfen, ob zumindest network-online.targetexistiert und dass es nach geordnet wird network.target.

Bisher garantierte NetworkManager nur, dass mindestens eine Verbindung angewendet wurde. Und selbst damit das funktioniert, müsste man das NetworkManager-wait-online.serviceexplizit aktivieren . Dies wurde in Fedora schon lange behoben, aber erst kürzlich im Upstream angewendet.

systemctl enable NetworkManager-wait-online.service

Hinweise zur Implementierung von network.target und network-online.target

Sie sollten nicht immer Ihre Software hängen von vornehmen müssen NetworkManager.serviceoder NetworkManager-wait-online.servicenoch andere spezifische Dienstleistungen. Stattdessen sollten sich alle Netzwerkverwaltungsdienste vor network.targetund optional selbst bestellen network-online.target.

Ein einfacher skriptbasierter Netzwerkverwaltungsdienst sollte die Netzwerkkonfiguration vor dem Beenden abschließen und sich vor network.targetund damit indirekt vor dem Beenden selbst anordnen network-online.target.

[Unit]
Before=network.target

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

Ein Daemon-basierter Netzwerkverwaltungsdienst sollte sich ebenfalls vorher network.targetselbst bestellen, auch wenn dies nicht sehr nützlich ist.

[Unit]
Before=network.target

[Service]
Type=simple
ExecStart=...

Ein Dienst, der für die Daemon wartet beenden sollte sich nach dem Dienst bestellen und vor network-online.target. Es sollte Requisiteauf dem Daemon-Dienst verwendet werden, damit es sofort fehlschlägt, wenn der entsprechende Netzwerkverwaltungsdienst nicht verwendet wird.

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

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

Das Paket sollte einen Symlink zum wartenden Dienst im wantsVerzeichnis für installieren network-online.target, damit er von Diensten abgerufen wird, die auf das konfigurierte Netzwerk warten möchten.

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

Dazugehörige Dokumentation

Schlussnoten

Ich hoffe, dass ich Ihnen nicht nur bei der Beantwortung Ihrer Frage geholfen habe, sondern auch dazu beigetragen habe, die Situation in Upstream- und Linux-Distributionen zu verbessern, sodass ich jetzt eine bessere Antwort geben kann, als dies zum Zeitpunkt des Erstellens der ursprünglichen möglich war .

Pavel Šimerda
quelle
Meinen Sie die Autoconnect-Option mit "Warten Sie, bis alle für den automatischen Start konfigurierten Verbindungen erfolgreich sind"? Kann ich dies nutzen, wenn ich no-auto-default = * setze, aber auf einer meiner Verbindungen autoconnect = yes habe? Und letzte Frage - ich verstehe nicht - Warten auf Startoption der nm-online- und man-Seite hilft nicht viel. Vielen Dank für dieses Schreiben, sehr geschätzt!
lzap
Soweit ich weiß, interessiert sich nm-online nicht no-auto-defaultnur dafür auto. Haben Sie eine konkrete Frage? Meiner Meinung nach heißt es in der Manpage von nm-online eindeutig, dass damit -sdarauf gewartet wird, dass alle automatischen Verbindungen versucht werden, dh verbunden oder fehlgeschlagen sind.
Pavel Šimerda
Nachdem ich eine Stunde lang mit diesem Mist rumgespielt hatte, fand ich die Lösung: apt-get install sysv-init. :-) Die Komplexität, die das System als Ersatz für ein paar Shellskripte hinzufügt, ist umwerfend.
Jemand
@Jemand fürchte ich, dass Initskripte in diesem Fall keine Antwort sind. Wenn Sie NetworkManager oder ein anderes dynamisches Konfigurationstool verwenden, können sich initscripts nach einem vollständig konfigurierten Netzwerk nicht selbst bestellen. Sie können eine begrenzte dynamische Konfiguration mit /etc/init.d/networkoder ähnlichem erhalten, aber das funktioniert nicht universell.
Pavel Šimerda
@Pavel Šimerda Init wird seriell ausgeführt, und ein ordnungsgemäßes Init-Skript wird erst dann zurückgegeben, wenn es das getan hat, worauf sich nachfolgende Skripts verlassen müssen. Für die Vernetzung bedeutet dies, alle geeigneten Adapter bereit zu haben. Sofern es nicht nur ein glücklicher Zeitpunkt war, verhält sich NM in diesem Kontext gut. Das eigentliche Problem ist natürlich, dass NM das Netzwerkhandling neu erfindet, anstatt auf bestehenden einfachen und bewährten Strukturen aufzubauen. Desktop-Benutzer scheinen keine Ahnung von den Gefahren der Komplexität zu haben. ;-)
Jemand
9

AfterIn [Unit]Abschnitt können Sie einen Dienst definieren, der vor dem Start Ihres Dienstes gestartet werden soll. Wenn Sie beispielsweise NetworkManager verwenden, können Sie den Dienst nach dem Start von NetworkManager starten.

[Unit]
Description=test service
After=NetworkManager.service
phoops
quelle
BindsToist hier nicht so angemessen, da es sich bei dem Dienst um ein einmaliges Ereignis und nicht um einen dauerhaften Dienst handelt (es sei denn, es enthält auch eine ExecStopFunktion, die ausgelöst wird, wenn das Netzwerk ausfällt).
Goldlöckchen
entferntBindsTo
phoops
Sie können jedoch etwas hinzufügen, das ersetzt werden BindsTosoll, z. B., Requireswenn der Dienst nur ausgeführt werden soll, wenn NetworkManager dies tut. Aftermacht das eigentlich nicht - es bedeutet nur, dass, wenn NM auch läuft, dies danach ausgeführt wird. Wenn NM nicht ausgeführt wird, wird der Dienst an einem beliebigen Punkt ausgeführt.
Goldlöckchen
4
After = network.target ist besser als After = NetworkManager.service, da es allgemeiner ist.
Pavel Šimerda
7
Beachten Sie, dass Spezifizierungs After=foowird nicht dazu führen , das fooGerät zu starten , wenn es nicht bereits gestartet wird, wird es nur systemd sagen , wie die Einheiten bestellen , wenn sie beide zur gleichen Zeit gestartet . Mit beiden After=foosowie Wants=foooder Requires=foodie Wirkung des Ziehens in haben , foowenn es nicht gestartet ist, und auch systemd um die Geräte richtig zu machen.
Emil Lundberg
8

Wenn Ihr Dienst einen Server bereitstellt, der passiv darauf warten kann, dass sich jemand mit ihm verbindet, verwenden Sie Folgendes:

[Unit]
After=network.target

Ihr Dienst sollte an die Platzhalter-Schnittstelle gebunden sein. Wenn die Socket-Aktivierung verwendet wird (empfohlen) oder wenn sie nur lokal ist, können Sie Netzwerkziele vollständig ignorieren.

Wenn Ihr Service als Client fungiert oder Peer-to-Peer ist, ist dies angemessener:

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

Vor systemd 213 benötigt network-online.target die erwähnte Problemumgehung Pavel (Sie müssen manuell einen Dienst aktivieren, der darauf wartet, dass das Netzwerk aktiv ist). Ab System 213 erfolgt dies standardmäßig. systemd-networkd-wait-onlineWartet darauf, dass mindestens eine Adresse (entweder routingfähig oder verbindungslokal) auf einer Nicht-Loopback-Schnittstelle konfiguriert wird.

Das Konfigurieren von systemd-networkd, NetworkManager oder einer entsprechenden Komponente ist eine eigenständige Aufgabe. DHCP (für IPv4) und NDP (für IPv6) funktionieren in der Regel sofort, Sie sollten sie jedoch so konfigurieren, dass Ihre genaue Definition von „Das Netzwerk ist aktiv“ die Auslöser sind network-online.target.

Dokumentation:

Tobu
quelle
Nur neugierig, warum eine neue Antwort und nicht nur kleine Verbesserungen an der bestehenden und (hoffentlich) gut strukturierten Antwort.
Pavel Šimerda
Die ersten beiden Dokumentationslinks sind derzeit ungültig.
Peter Hansen
Warum Requires anstelle von Want verwenden?
Karl Morrison
4

Ich schätze, ich könnte den Punkt ändern, an dem das Skript ausgeführt wird, indem ich mit dem WantedBy-Parameter experimentiere

Das hat den gegenteiligen Effekt von dem, was Sie wollen. Von man systemd.unit:

WantedBy =, RequiredBy =

[...] Eine symbolische Verknüpfung wird im Verzeichnis .wants / oder .requires / jeder der aufgelisteten Einheiten erstellt, wenn diese Einheit von systemctl enable installiert wird. Dies hat zur Folge, dass eine Abhängigkeit vom Typ Wants = oder Requires = von der aufgelisteten Einheit zur aktuellen Einheit hinzugefügt wird .

Basierend darauf können wir sehen, dass die richtige Einheitenoption "Wants" oder "Requires" ist. Basierend auf deren Beschreibung ist "Requires" wahrscheinlich richtig, mit dem Zusatz "After", um nicht nur sicherzustellen, dass der Netzwerkdienst ausgeführt wird, sondern auch, dass er vor diesem Gerät ausgeführt wird.

Keine der Einheitenoptionen, AFAIK, kann die Bedingung enthalten, dass eine gestartete Anforderung abgeschlossen sein muss oder einen bestimmten Punkt erreicht hat (Netzwerk ist wahrscheinlich ein Daemon-Dienst), nur dass sie zuerst gestartet wird . In diesem Sinne möchten Sie möglicherweise Ihr Skript Type=forkingerstellen und eine fehlerfreie Verzögerung (z. B. 30 Sekunden) oder eine Art Exit-On-Success-Schleife mit einer Verzögerung einleiten, um sicherzustellen, dass Sie zuerst eine DHCP-Lease haben.

Goldlöckchen
quelle
1
Weder WantedBy noch RequiredBy wirken sich auf die Bestellung aus.
Pavel Šimerda
1
@ PavelŠimerda: Niemand hier hat das behauptet. Aus diesem Grund habe ich die Bestellung Afterim Zusammenhang mit Requires"Sicherstellen, dass der Netzwerkdienst nicht nur ausgeführt wird, sondern auch vor diesem Gerät" ausdrücklich erwähnt .
Goldlöckchen
1
Ja, Afterarbeitet mit Wantsoder auf Requiresdiese Weise zusammen. Auf der anderen Seite sind explizite Verzögerungen eine schlechte Angewohnheit in abhängigkeitsbasierten Tools, insbesondere wenn es eine explizite Möglichkeit gibt, zu warten, bis das Netzwerk konfiguriert ist, wie in der Systemdokumentation angegeben. Ich muss also auf der Ablehnung bestehen.
Pavel Šimerda
3

Verwenden Sie Afterin diesem [Unit]Abschnitt, um anzugeben, was vor Ihrem eigenen Dienst gestartet werden soll. (So ​​viel von der vorherigen Antwort ist richtig.)

Verwenden Sie das Netzwerkziel, um Ihren Dienst nach dem Hochfahren des Netzwerks zu starten. Dies sollte unabhängig davon gelten, ob Sie NetworkManager, das conf.d / netctl-System in Arch oder einen anderen Dienst verwenden, der systemd bekannt ist.

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

Ein kurzer Blick bestätigt, dass jeder andere Dienst auf Ihrem System, der auf Netzwerkkonnektivität angewiesen ist, diese Anweisung enthält.

Es ist auch portierbar für alle Distributionen, die systemd verwenden. Ihre Unit-Datei wird dieselbe sein für Arch, Fedora, RHEL 7, zukünftige Versionen von Debian ...


Dienste , die starten eine Netzwerkverbindung, wie Arch Skripte oder eigenen, sollten angeben , damit ihre eigenen Unit - Dateien.

[Unit]
Wants=network.target
Before=network.target
Michael Hampton
quelle
Ich mag das WantsTeil nicht ganz, weil es Nebenwirkungen auf andere Pakete hat. Schau dir bitte meine Antwort an.
Pavel Šimerda
Ich habe gerade gemerkt, dass Wantson network.targethier eine gute Idee ist.
Pavel Šimerda
Sie möchten wirklich network-online.target verwenden. ref
Edward Torvalds
1

Ich wollte diesem Artikel einen Punkt hinzufügen. Derzeit (Sommer 2015) in RHEL7 / CentOS 7 ist network-online.target vor dem Hochfahren des IPv6-Netzwerks falsch eingestellt

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

In ihrer Dienstdefinition werden auch explizit an IPv6-Adressen gebundene Dienste wahrscheinlich gestartet, bevor IPv6 ausgeführt wird, was zu einem Fehler führt.

Colo Host
quelle
Ich vermute, dass dies nur bei der kernelbasierten automatischen IPv6-Konfiguration der Fall ist, die ohnehin fehlerhaft ist. Wenn Sie nach IPv6 ordnungsgemäß bestellen möchten, sollten Sie auf jeden Fall NetworkManager anstelle von verwenden /etc/init.d/network. Wenn Sie das gleiche Problem auch mit NM erhalten, ist dies ein guter Grund, eine Funktionsanforderung einzureichen. Ich habe nicht bei RHEL / CentOS nachgefragt, ich kann Ihnen bei Interesse mit den Details helfen.
Pavel Šimerda
0
[Unit]
After=systemd-networkd.service

funktioniert bei mir.

Zhenxiao Hao
quelle
Ich bin mir nicht sicher, ob es in bestimmten Fällen funktioniert, aber es ist aus mehreren Gründen falsch. Eine davon ist, dass es networkdeinen eigenen / wait-online / Service gibt. Das Einziehen und Nachbestellen network-online.targetist der richtige Weg, um mit jedem Dienst zu beginnen, der dies unterstützt.
Pavel Šimerda