Skonfiguruj usystematyzowaną usługę systemową do zakończenia przez SIGKILL

20

tło

Poproszono mnie o stworzenie systemdskryptu dla nowej usługi, foo_daemonktóra czasami przechodzi w „zły stan” i nie umrze przez SIGTERM(prawdopodobnie z powodu niestandardowej procedury obsługi sygnałów). Jest to problematyczne dla programistów, ponieważ są oni instruowani, aby uruchomić / zatrzymać / zrestartować usługę poprzez:

  • systemctl start foo_daemon.service
  • systemctl stop foo_daemon.service
  • systemctl restart foo_daemon.service

Problem

Czasami, z powodu foo_daemonpopadnięcia w zły stan, musimy siłą go zabić poprzez:

  • systemctl kill -s KILL foo_daemon.service

Pytanie

Jak skonfigurować systemdskrypt, foo_daemonaby za każdym razem, gdy użytkownik spróbuje zatrzymać / ponownie uruchomić usługę, systemdbędzie:

  • Spróbuj z wdziękiem wyłączyć foo_daemonvia SIGTERM.
  • Daj do 2 sekund na zakończenie / zakończenie działania foo_daemon.
  • Próbować wymuszone wyłączenie foo_daemonpoprzez SIGKILLjeśli proces jest wciąż żywy (więc nie ma ryzyka PID zawraca i systemdproblemy SIGKILLprzed niewłaściwym PID). Testowane przez nas urządzenie odradza się / rozwidla szybko wiele procesów, więc istnieje rzadka, ale bardzo realna obawa o to, że recykling PID powoduje problem.
  • Jeśli w praktyce jestem po prostu paranoikiem odnośnie recyklingu PID, nie mam nic SIGKILLprzeciwko skryptowi, który wystawia się przeciwko procesowi PID, nie martwiąc się o zabicie przetworzonego PID.

Chmura
źródło
2
Nawet jeśli odradzasz procesy wystarczająco szybko, aby rzucić ponad 4 miliony PID w dwie sekundy, systemd nie siedzi w pętli, sprawdzając „czy ten pid wciąż żyje? Czy ten pid wciąż żyje?” ponieważ nie musi ; jest już informowany o tym, czy jego bezpośrednie procesy potomne nadal działają, czy nie (za pomocą zwykłego SIGCHLD i waitpid ()). Jeśli więc zobaczy, że proces zakończył się po SIGTERM, po prostu oznaczy usługę jako „nieaktywną” w tym momencie - nie będzie w ogóle przeszkadzał w sprawdzaniu, czekaniu i wysyłaniu SIGKILL.
grawity

Odpowiedzi:

26

systemd już to obsługuje i jest domyślnie włączony .

Jedyne, co możesz chcieć dostosować, to limit czasu, który możesz zrobić TimeoutStopSec=. Na przykład:

[Service]
TimeoutStopSec=2

Teraz systemd wyśle ​​SIGTERM, poczeka dwie sekundy na zakończenie usługi, a jeśli nie, wyśle ​​SIGKILL.

Jeśli Twoja usługa nie obsługuje systemu, może być konieczne podanie ścieżki do pliku PID PIDFile=.

Na koniec wspomniałeś, że twój demon spawnuje wiele procesów. W takim przypadku możesz chcieć ustawić, KillMode=control-groupa systemd wyśle ​​sygnały do ​​wszystkich procesów w grupie.

Michael Hampton
źródło
Dziękuję Ci. Ostatnie pytanie: załóżmy, że usługa nie obsługuje systemu. Co mogę dodać do skryptu systemd dla tej usługi, aby systemd tworzył plik PID / zarządzał nim? Dodatkowo, usługa może być wieloinstancyjna za pośrednictwem jednostek szablonu, więc zazwyczaj uruchamiamy ją za pomocą `systemctl start [email protected]", więc czy wpłynęłoby to na logikę pliku PID w skrypcie?
Cloud
4
@DevNull systemd nie tworzy ani nie zarządza plikami PID. Nie ma ku temu powodu. Jeśli twoja usługa nie tworzy własnego pliku PID, to jeśli to możliwe, skonfiguruj go do działania na pierwszym planie (zamiast demonizacji) i ustaw Type=simplew jednostce systemd.
Michael Hampton
1
Jeśli usługa ma na utrzymaniu, Type=forkingma tę zaletę, że (jeśli usługa została poprawnie napisana) informuje systemd, kiedy jest w pełni „gotowa”, czego Type = simple nie może zrobić. Demonizacja nie stanowi problemu, nawet bez pliku PID - systemd i tak wyśledzi główny proces.
grawity
1
@grawity Prawda, prawda ... choć z mojego doświadczenia wynika, że ​​usługi demonizują się, zanim faktycznie są gotowe do rozpoczęcia udostępniania. Usługa systemd-świadomość Type=notifyjest najlepsza dla systemd, a wiele popularnych usług już to robi. Ale prawdopodobnie nie ta starsza usługa. W przypadku PO ma on usługę, która odradza wiele procesów. Systematyczni doktorzy ostrzegają o tej sprawie .
Michael Hampton
1

Ponieważ nikt nie wspomniał o potrzebie Type=oneshot, oto kompletny przykład, który kończy się z powodu awarii limitu czasu.

[Unit]
Description=timeout test

[Service]
Type=oneshot
TimeoutStartSec=2
ExecStart=/bin/sleep 10
Evidlo
źródło