Jak utworzyć wirtualną usługę systemową, aby zatrzymać / uruchomić kilka wystąpień razem?

12

Planuję hostować kilka wystąpień tej samej aplikacji internetowej dla klientów korzystających z niej systemd. Chciałbym być w stanie stopi startkażda instancja klient korzystający systemd, jak również traktując całą kolekcję instancji klienta jako pojedyncze usługi, które mogą być zatrzymywany i uruchamiany razem.

systemdwydaje się, że zawiera elementy, których potrzebuję PartOf, i pliki jednostek szablonów, ale poszedłem zatrzymać usługę nadrzędną, podrzędna obsługa klienta nie została zatrzymana. Jak mogę to zrobić z systememd? Oto co mam do tej pory.

Plik jednostki nadrzędnej 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

Plik szablonu jednostki o nazwie [email protected], używany do tworzenia instancji klienta:

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

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

Mój app-poc.shskrypt (dowód koncepcji, który po prostu drukuje do pliku dziennika w pętli):

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

Dla potwierdzenia koncepcji mam systemowe pliki jednostek ~/.config/systemd/user.

Następnie uruchamiam element nadrzędny i instancję na podstawie szablonu (po systemctl --user daemon-reload):

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

Po użyciu journalctl -fwidzę, że oba zostały uruchomione i że instancja klienta nadal działa. Teraz spodziewam się, że wyłączenie rodzica zatrzyma dziecko (bo użyłem PartOf), ale tak nie jest. Również uruchomienie rodzica nie oznacza uruchomienia dziecka zgodnie z oczekiwaniami.

systemctl --user stop app

Dzięki!

(Używam Ubuntu 16.04 z systememd 229).

Mark Stosberg
źródło
1
„PartOf = Konfiguruje zależności podobne do Wymaga =, ale ogranicza się do zatrzymywania i restartowania jednostek.” Jeśli chcesz zacząć pracę, nie musisz Requires=zamiast tego używać ?
sourcejedi

Odpowiedzi:

10

Musisz przesunąć linię

PartOf=app.service

z [Service]i do [Unit]sekcji, a następnie dodaj do [Unit]z app.servicelisty klientów, aby rozpocząć, np

[email protected] [email protected]

lub jak powiedział sourcejedi w komentarzach, Requires=to samo. Możesz PartOfzatrzymać usługi uruchamiane ręcznie, których nie ma na powyższej liście, np systemctl --user start [email protected].

meuh
źródło
Potwierdziłem, że masz rację PartOf. Dzięki. Zajmę się „Wants” poprzez dowiązanie symboliczne, które staje się pojedynczym działaniem, które muszę podjąć, aby aktywować nowego klienta za pomocą systemd. W moim przypadku testowym: `ln -s /home/mark/.config/systemd/user/[email protected] / home / mark / .config / systemd / user / app.service.wants / unity @ foo.service`
Mark Stosberg
14

Dowiedziałem się, że po to są systemowe „jednostki docelowe”. Korzystając z jednostki docelowej, uzyskuję pożądane korzyści bez potrzeby tworzenia fałszywej [Service]sekcji, którą miałem powyżej. Działający przykładowy plik „Jednostka docelowa” wygląda następująco:

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

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

Następnie każda instancja klienta powinna zostać uwzględniona PartOfw [Unit]sekcji (jak wskazał @meuh), a także powinna mieć [Install]sekcję, aby enablei disabledziałała na określonej usłudze:

# 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

Aby przywołać instancję klienta i uruchomić ją po uruchomieniu celu, używana jest ta komenda jednorazowego włączenia:

 systemctl enable app

Teraz w tym momencie mogę używać stopi startwłączać app@customerdla konkretnej instancji lub mogę używać start appi stop appzatrzymać wszystkie aplikacje razem.

Mark Stosberg
źródło
Co powiesz na status? Nie mogę znaleźć prostego sposobu na uzyskanie statusu wszystkich usług, których potrzebowała aplikacja. Wiem, jak mogę to
napisać
1
Mam na myśli uzyskanie statusu aplikacji w tej grupie docelowej bez wyszczególnienia wszystkich jej elementów, symboli wieloznacznych lub nie, najlepiej używając nazwy grupy, a nawet nie dbając o to, z czego jest wykonana.
Tommi Kyntola,
2
To nie takie proste. Do którego pakietu należy ten skrypt? Musiałby być modyfikowany za każdym razem, gdy dodawany jest nowy komponent. Zapomnij o tym, a wdrażanie / konserwacja szaleje. Oczywiście chciałbym po prostu dodać nowy pakiet z ustawieniem partOf wskazującym, że jest on obecny w tej grupie, a następnie nie modyfikować jakiegoś skryptu. A potem zatrzymanie i rozpoczęcie tego celu działa jak wcześniej. To działa, ale status wydaje się wykraczać poza ten zakres. Nie mogę nawet znaleźć sposobu, aby uzyskać listę jednostek, które są uruchomione w celu. Ten przypadek użycia nie jest objęty systememd.
Tommi Kyntola,
2
@TommiKyntola Oto bash jednoliniowy, którego nie trzeba aktualizować, gdy zmieniają się zależności zależne od celu:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg
2
@TommiKyntola Zgadzam się, że systemdmoże to poprawić użyteczność tutaj. Mam otwarte żądania funkcji , które sugerowałyby, poprawę stanu dla celów.
Mark Stosberg,