Wie kann ein systemd-Dienstflag bereit sein, damit andere Dienste warten können, bis es bereit ist, bevor sie gestartet werden?

8

Ich habe eine Reihe von Dienstleistungen (sagen wir C0, C1... C9) , die nur gestartet werden soll , nachdem ein Dienst Sseine Initialisierung abgeschlossen ist und vollständig ausgeführt wird und bereit für die anderen Dienste. Wie arrangiere ich das mit systemd?

Bei der Bestellung von Diensten mit Pfadaktivierung und Ziel in systemd wird davon ausgegangen, dass der Dienst Süber einen Mechanismus zum Schreiben einer Art Flag-Datei verfügt. Nehmen wir hier im Gegensatz dazu an, dass ich die volle Kontrolle über das Programm habe, das der Dienst Sausführt, und bei Bedarf systemd-Mechanismen hinzufügen kann.

JdeBP
quelle

Antworten:

7

Das braucht man nicht unbedingt.

Wenn die CDienste warten müssen, Sbis sie bereit sind, damit sie eine Socket-Verbindung herstellen können, muss dies nicht unbedingt der Fall sein. Vielmehr kann man das frühzeitige Öffnen der Hörbuchse durch Service-Manager nutzen.

Einige Systeme, darunter Laurent Bercots s6 , mein Nosh-Toolset und systemd, bieten Möglichkeiten, wie ein Abhörsockel frühzeitig geöffnet werden kann. Dies ist das allererste, was beim Einrichten des Dienstes erforderlich ist . Sie alle umfassen etwas anderes als das Serviceprogramm, das die Listening-Sockets öffnet, und das Serviceprogramm, das beim Aufrufen die Listening-Sockets als bereits geöffnete Dateideskriptoren empfängt.

Insbesondere mit systemd erstellt man eine Socket-Einheit , die den Listening-Socket definiert. systemd öffnet die Socket-Einheit und richtet sie so ein, dass das Kernel-Netzwerksubsystem auf Verbindungen wartet. und übergibt es als offenen Dateideskriptor an den eigentlichen Dienst, wenn es darum geht, die Prozesse zu erzeugen, die Verbindungen zum Socket verarbeiten. (Es kann dies auf zwei Arten tun, genau wie es inetdkönnte, aber eine Diskussion der Details von Accept=trueversus Accept=falseDiensten geht über den Rahmen dieser Antwort hinaus.)

Der wichtige Punkt ist, dass man nicht unbedingt mehr Bestellung braucht. Der Kernel stapelt Clientverbindungen in einer Warteschlange, bis das Serviceprogramm initialisiert ist, und ist bereit, sie zu akzeptieren und mit Clients zu sprechen.

Wenn man das tut, sind Bereitschaftsprotokolle das Richtige.

systemd verfügt über eine Reihe von Bereitschaftsprotokollen , die von Dienst zu Dienst mit der Type=Einstellung in der Serviceeinheit angegeben werden. Das hier interessierende besondere Bereitschaftsprotokoll ist das notifyBereitschaftsprotokoll. Damit wird systemd angewiesen, Nachrichten vom Dienst zu erwarten, und wenn der Dienst bereit ist, sendet es eine Nachricht, die die Bereitschaft kennzeichnet. systemd verzögert die Aktivierung der anderen Dienste, bis die Bereitschaft markiert ist.

Dies zu nutzen beinhaltet zwei Dinge:

  • Ändern Sie den Code von Sso, dass er so etwas wie die notify_systemd()Funktion von Pierre-Yves Ritschard oder die Funktion von Cameron T Norman aufruft notify_socket().
  • Einrichten der Serviceeinheit für den Service mit Type=notifyund NotifyAccess=main.

Die NotifyAccess=mainEinschränkung (dies ist die Standardeinstellung) besteht darin, dass systemd wissen muss, dass Nachrichten von schelmischen (oder einfach nur fehlerhaften) Programmen ignoriert werden, da jeder Prozess im System Nachrichten an den Benachrichtigungssocket von systemd senden kann.

Man verwendet den Code von Pierre-Yves Ritschard oder Cameron T Norman als Präferenz, da er die Möglichkeit nicht ausschließt, diesen Mechanismus unter UbuntuBSD, Debian FreeBSD, tatsächlichem FreeBSD, TrueOS, OpenBSD usw. zu haben. was der von den systemd-Autoren bereitgestellte Code ausschließt.

Eine zu vermeidende Falle ist das systemd-notifyProgramm. Es gibt mehrere Hauptprobleme, nicht zuletzt, dass mit ihm gesendete Nachrichten unbearbeitet von systemd weggeworfen werden können. Das größte Problem in diesem Fall ist, dass es nicht als "Haupt" -Prozess des Dienstes ausgeführt wird, sodass die Bereitschaftsbenachrichtigungen für den Dienst Sfür jeden Prozess auf dem System mit geöffnet werden müssen NotifyAccess=all.

Eine weitere zu vermeidende Falle ist die Annahme, dass das forkingProtokoll einfacher ist. Es ist nicht. Um es richtig zu machen , muss das übergeordnete Element erst dann gegabelt und beendet werden, wenn (zum einen) alle Arbeitsthreads des Programms ausgeführt werden. Dies entspricht nicht der Tatsache, dass die überwiegende Mehrheit der Dæmons, die sich gabeln, tatsächlich gabelt.

Weiterführende Literatur

JdeBP
quelle
Nach Menschen systemd.service(5), NotifyAccess=allakzeptieren Nachrichten von allen Mitgliedern der Kontrollgruppe Dienst , die sie nicht nur irgendeinen Schurken Prozess auf dem System bedeuten. Dies ist für die meisten Anwendungsfälle sicher genug. Auch Ihre Bedenken hinsichtlich der Portabilität auf andere Betriebssysteme sind für OP nicht relevant, da wir uns hier bereits mit dem Thema Systemd befassen.
Amir
1

Unter Bezugnahme auf die Handbuchseite systemd.service(5), insbesondere auf den Abschnitt über Type = , hat jeder Diensttyp eine andere Möglichkeit für Systemd, festzustellen, ob es bereit ist, anderen Diensten Funktionen anzubieten:

  • In diesem Type=simpleFall sollten die Kommunikationskanäle vor dem Start des Dämons installiert werden (z. B. von systemd eingerichtete Sockets über Socket-Aktivierung).

  • Wenn dies Type=forkingder Fall ist, wird erwartet, dass der übergeordnete Prozess beendet wird, wenn der Start abgeschlossen ist und alle Kommunikationskanäle eingerichtet sind.

  • Wenn Type=dbusdies der Fall ist, wird erwartet, dass der Dämon einen Namen auf dem D-Bus-Bus erhält. An diesem Punkt fährt systemd mit dem Starten der Folgeeinheiten fort.

  • Wenn Type=notifydies der Fall ist, wird erwartet, dass der Dämon nach sd_notify(3)Abschluss des Startvorgangs eine Benachrichtigungsnachricht über oder einen entsprechenden Anruf sendet . systemd fährt mit dem Starten der Folgeeinheiten fort, nachdem diese Benachrichtigung gesendet wurde.

Für die letzte Option (Senden einer Nachricht über sd_notify) können Sie das systemd-notifyDienstprogramm verwenden und daran denken, ihm Zugriff zu gewähren NotifyAccess=all.

Da Sie die Kontrolle über den Service haben S, können Sie die beste Option für Ihren Anwendungsfall oder einfach die am einfachsten zu implementierende auswählen.

Amir
quelle
1

so was:

S.service

[Unit]
Description=My main Service

[Service]
Type=notify
ExecStart=/usr/bin/myBinary

C0.service

[Unit]
Description=Dependent service number 0
PartOf=S.service

C1.service

[Unit]
Description=Dependent service number 1
PartOf=S.service

C9.service

[Unit]
Description=Dependent service number 9
PartOf=S.service

Wobei / usr / bin / myBinary einen sd_notify READY = 1- Aufruf ausführt , wenn die Initialisierung abgeschlossen ist.

Abhängig davon, wie sich die Abhängigkeit verhalten soll, können Sie PartOf, Requires oder BindsTo oder andere verwenden .

code_monk
quelle