Сконфигурируйте ошибочную службу systemd для завершения через SIGKILL

20

Задний план

Меня попросили создать systemdскрипт для нового сервиса, foo_daemonкоторый иногда попадает в «плохое состояние» и не погибает через него SIGTERM(вероятно, из-за пользовательского обработчика сигнала). Это проблематично для разработчиков, так как им поручено запустить / остановить / перезапустить сервис через:

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

проблема

Иногда, из- foo_daemonза плохого состояния, мы вынуждены его принудительно убивать:

  • systemctl kill -s KILL foo_daemon.service

Вопрос

Как я могу настроить свой systemdсценарий foo_daemonтак, чтобы всякий раз, когда пользователь пытается остановить / перезапустить службу,systemd он:

  • Попытка постепенного отключения foo_daemon через SIGTERM.
  • Дайте до 2 секунд для выключения / прекращения foo_daemon завершения.
  • Попытайтесь принудительно отключить функцию foo_daemonvia, SIGKILLесли процесс еще активен (поэтому у нас нет риска повторного использования PID и systemdпроблем SIGKILLс неправильным PID). Устройство, которое мы тестируем, быстро порождает / разветвляет многочисленные процессы, поэтому существует редкая, но очень реальная проблема, связанная с рециркуляцией ПИД, вызывающей проблему.
  • Если на практике я просто параноик по поводу рециркуляции PID, я в порядке со сценарием, который просто выдает SIGKILLпротив PID процесса, не заботясь об уничтожении переработанного PID.

облако
источник
2
Даже если вы запускаете процессы достаточно быстро, чтобы пролистать более 4 миллионов PID за две секунды, systemd не зацикливается на проверке: «Этот pid еще жив? Этот pid еще жив?» потому что это не нужно ; он уже информирован о том, живы ли его непосредственные дочерние процессы или нет (с помощью обычного SIGCHLD и waitpid ()). Поэтому, если он увидит, что процесс завершился после SIGTERM, он просто пометит службу как «неактивную» в этот момент - он вообще не будет беспокоиться о проверке, ожидании и отправке SIGKILL.
Гравитация

Ответы:

26

systemd уже поддерживает это "из коробки", и он включен по умолчанию .

Единственное, что вы можете настроить - это время ожидания, которое вы можете использовать TimeoutStopSec=. Например:

[Service]
TimeoutStopSec=2

Теперь systemd отправит SIGTERM, подождите две секунды, пока служба не выйдет, а если нет, отправит SIGKILL.

Если ваша служба не поддерживает systemd, вам может потребоваться указать путь к ее файлу PID PIDFile=.

Наконец, вы упомянули, что ваш демон порождает много процессов. В этом случае вы можете установить, KillMode=control-groupи systemd будет посылать сигналы всем процессам в cgroup.

Майкл Хэмптон
источник
Спасибо. Последний вопрос: давайте предположим, что сервис не поддерживает systemd. Что можно добавить в сценарий systemd для этой службы, чтобы systemd создавал / управлял файлом PID? Кроме того, служба может быть несколькими экземплярами с помощью шаблонных модулей, поэтому мы обычно запускаем ее через `systemctl start foo_dameon@1.service", так что это повлияет на логику файла PID в сценарии?
Облако,
4
@DevNull systemd не создает и не управляет файлами PID. Для этого нет никаких причин. Если ваша служба не создает свой собственный файл PID, то, если возможно, настройте его на запуск на переднем плане (вместо демонизации) и настройте его Type=simpleв модуле systemd.
Майкл Хэмптон
1
Если у службы есть иждивенцы, она Type=forkingимеет преимущество (если служба была написана правильно), информируя systemd, когда она полностью «готова», что Type = simple не может сделать. Демонизация не проблема, даже без файла PID - systemd все равно отследит основной процесс.
Гравитация
1
@ grawity Достаточно верно ... хотя мой опыт заключается в том, что сервисы демонизируются до того, как они действительно будут готовы начать обслуживание. Использование systemd-ориентированных сервисов Type=notifyлучше всего подходит для systemd, и многие обычные сервисы уже делают это. Но, вероятно, не этот устаревший сервис. В случае ОП у него есть служба, которая порождает много процессов. Документы systemd предупреждают об этом случае .
Майкл Хэмптон
1

Поскольку никто не упомянул о необходимости Type=oneshot, вот полный пример, который завершается из-за сбоя тайм-аута.

[Unit]
Description=timeout test

[Service]
Type=oneshot
TimeoutStartSec=2
ExecStart=/bin/sleep 10
Evidlo
источник