Wie erstelle ich einen virtuellen Systemdienst, um mehrere Instanzen zusammen zu stoppen / zu starten?

12

Ich plane, mehrere Instanzen derselben Web-App für Kunden zu hosten, die diese verwenden systemd. Ich möchte in der Lage sein, jede Kundeninstanz zu verwenden stopund die gesamte Sammlung von Kundeninstanzen als einen einzigen Dienst zu behandeln, der gemeinsam gestoppt und gestartet werden kann.startsystemd

systemdscheint die Bausteine ​​bereitzustellen, die ich verwenden muss PartOf, und Vorlageneinheitsdateien, aber ich habe den übergeordneten Dienst gestoppt, der untergeordnete Kundendienst wird nicht gestoppt. Wie kann ich das mit systemd machen? Folgendes habe ich bisher.

Die übergeordnete Einheitendatei app.service:

[Unit]
Description=App Web Service

[Service]
# Don't run as a deamon (because we've got nothing to do directly)
Type=oneshot
# Just print something, because ExecStart is required
ExecStart=/bin/echo "App Service exists only to collectively start and stop App instances"
# Keep running after Exit start finished, because we want the instances that depend on this to keep running
RemainAfterExit=yes
StandardOutput=journal

Eine Einheitsvorlagendatei mit dem Namen [email protected], mit der Kundeninstanzen erstellt werden:

[Unit]
Description=%I Instance of App Web Service

[Service]
PartOf=app.service
ExecStart=/home/mark/bin/app-poc.sh %i
StandardOutput=journal

Mein app-poc.shSkript (Proof of Concept, das nur in einer Schleife als Protokolldatei gedruckt wird):

#!/bin/bash
# Just a temporary code to fake a full daemon.
while :
do
  echo "The App PoC loop for $@"
  sleep 2;
done

Für den Proof of Concept habe ich die systemd-Einheitendateien in ~/.config/systemd/user.

Ich starte dann das übergeordnete Element und eine Instanz basierend auf der Vorlage (danach systemctl --user daemon-reload):

systemctl --user start app
systemctl --user start [email protected]

Durch die Verwendung journalctl -fkann ich sehen, dass beide gestartet wurden und die Kundeninstanz weiterhin ausgeführt wird. Jetzt erwarte ich, dass das Herunterfahren des Elternteils das Kind stoppen wird (weil ich es verwendet habe PartOf), aber das tut es nicht. Außerdem startet das Starten des Elternteils das Kind auch nicht wie erwartet.

systemctl --user stop app

Vielen Dank!

(Ich verwende Ubuntu 16.04 mit systemd 229).

Mark Stosberg
quelle
1
"PartOf = Konfiguriert Abhängigkeiten ähnlich wie Requires =, beschränkt sich jedoch auf das Stoppen und Neustarten von Einheiten." Wenn Sie anfangen möchten zu arbeiten, müssen Sie nicht Requires=stattdessen verwenden?
Sourcejedi

Antworten:

10

Sie müssen die Linie verschieben

PartOf=app.service

aus [Service]und in den [Unit]Abschnitt, und fügen Sie die [Unit]von app.serviceder Liste der Kunden zu beginnen, zB

[email protected] [email protected]

oder wie sourcejedi in den Kommentaren sagte, Requires=dasselbe. Sie können die PartOfvon Hand gestarteten To- Stop-Dienste behalten , die nicht in der obigen Liste aufgeführt sind, z systemctl --user start [email protected].

meuh
quelle
Ich habe bestätigt, dass Sie Recht hatten PartOf. Vielen Dank. Ich werde die "Wünsche" über einen Symlink bearbeiten, der die einzige Aktion ist, die ich ausführen muss, um einen neuen Kunden mit systemd zu aktivieren. Für meinen Testfall: `ln -s /home/mark/.config/systemd/user/[email protected] / home / mark / .config / systemd / user / app.service.wants / entity @ foo.service`
Mark Stosberg
13

Ich habe gelernt, dass dies das ist, wofür systemd "Target Units" sind. Durch die Verwendung einer Zieleinheit erhalte ich die gewünschten Vorteile, ohne den oben genannten gefälschten [Service]Abschnitt erstellen zu müssen . Eine funktionierende Beispieldatei "Zieleinheit" sieht folgendermaßen aus:

# named like app.target
[Unit]
Description=App Web Service

# This collection of apps should be started at boot time.
[Install]
WantedBy=multi-user.target

Dann sollte jede Kundeninstanz PartOfin den [Unit]Abschnitt aufgenommen werden (wie von @meuh hervorgehoben), und sollte auch einen [Install]Abschnitt haben, damit enableund disableauf dem spezifischen Dienst arbeiten:

# In a file name like [email protected]
[Unit]
Description=%I Instance of App Web Service
PartOf=app.target

[Service]
ExecStart=/home/mark/bin/app-poc.sh %i
Restart=on-failure
StandardOutput=journal

# When the service runs globally, make it run as a particular user for added security
#User=myapp
#Group=myapp

# When systemctl enable is used, make this start when the App service starts
[Install]
WantedBy=app.target

Dieser einmalige Aktivierungsbefehl wird verwendet, um die Kundeninstanz aufzurufen und beim Starten des Ziels zu starten:

 systemctl enable app

An diesem Punkt kann ich stopund startauf app@customerdem für eine bestimmte Instanz, oder kann ich verwenden start appund stop appgemeinsam alle Anwendungen zu stoppen.

Mark Stosberg
quelle
Wie wäre es mit Status? Ich kann keinen einfachen Weg finden, um einen Status aller von der App gewünschten Dienste zu erhalten. Ich weiß, wie ich das schreiben kann, aber ...
Tommi Kyntola
1
Ich meine, als würde man den Status von Apps in dieser Zielgruppe abrufen, ohne alle Teile davon aufzulisten, Wildcards oder nicht, vorzugsweise unter Verwendung dieses Gruppennamens und ohne sich darum zu kümmern, woraus sie bestehen.
Tommi Kyntola
2
Es ist nicht so einfach. Zu welchem ​​Paket würde dieses Skript gehören? Es müsste jedes Mal geändert werden, wenn eine neue Komponente hinzugefügt wird. Vergessen Sie das und die Bereitstellung / Wartung geht durcheinander. Ich möchte natürlich nur ein neues Paket mit der Einstellung partOf hinzufügen, das angibt, dass es in dieser Gruppe vorhanden ist, und nicht dann Skript zu ändern, das noch verweilt. Und dann funktioniert das Stoppen und Starten dieses Ziels wie zuvor. Dies funktioniert, aber der Status scheint nicht in diesen Bereich zu fallen. Ich kann nicht einmal einen Weg finden, eine Liste der Einheiten zu erhalten, deren Laufzeit in einem Ziel vorhanden ist. Dieser Anwendungsfall wird von systemd nicht abgedeckt.
Tommi Kyntola
2
@TommiKyntola Hier ist ein Bash-Einzeiler, den Sie nicht aktualisieren müssen, wenn sich die Zielabhängigkeiten ändern:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg
2
@TommiKyntola Ich stimme zu, dass systemddies die Benutzerfreundlichkeit hier verbessern könnte. Ich habe eine Funktionsanforderung geöffnet, um einen verbesserten Status für Ziele vorzuschlagen.
Mark Stosberg