Назад | Перейти на главную страницу

systemd: служебная зависимость «Если A не запущен, B не должен запускаться»?

(systemd версия 229, fwiw)

У меня есть первичная служба A и вторичная служба B. Первичная служба A может работать сама по себе. Но служба B не может работать сама по себе: для нее необходимо, чтобы A работал (технически B может работать, но это то, что я хочу, чтобы systemd предотвратил). Моя цель: Если A не работает, B не должен работать. Учитывая, что A и B работают, когда A останавливается или умирает / выходит из строя, то B следует остановить.

Как мне этого добиться?

Я приближаюсь к добавлению элементов [Unit] в b.service, используя

Requisite=A.service
After=A.service

Результатом вышесказанного является то, что

1) B won't start unless A is running (good).
2) B is stopped when A is stopped (good).
3) However, if I kill A, service B continues to run (bad).

Как я могу исправить это последнее поведение №3? Я пробовал использовать BindsTo вместо Requisite, как это в служебном файле B:

BindsTo=A.service
After=A.service

и я получаю:

1) If A is not running and I start B, then A is also started
    (I want an error, I don't want A started)
2) B is stopped when A is stopped (good).
3) B is stopped when A is killed (good)

Итак, теперь №3 - это хорошо, но №1 - нежелательное поведение. Ни PartOf, ни BindsTo, похоже, не помогают, но, возможно, у меня нет правильного заклинания комбинаций опций? Из справочных страниц мне не ясно, какие варианты можно комбинировать.

С BindsTo я также попытался неудачно запустить B с помощью ExecStartPre, но, конечно, это не сработало, потому что начало B уже определило, что ему нужно A для запуска (и запустило его), до того, как он запустил B.

Есть ли способ добиться поведения между Requisite и BindsTo? 1-2-3, которые я перечислил выше?

Спасибо заранее!

Я тоже ищу более элегантное решение. Здесь есть открытый тикет systemd для запроса на улучшение: https://github.com/systemd/systemd/issues/5966

В настоящее время мой обходной путь выглядит следующим образом: B.service

Requisite=A.service
After=A.service

Услуга

OnFailure=Stop-B.service

Стоп-Б.сервис

[Service]
Type=oneshot
TimeoutSec=0
ExecStart=/bin/systemctl stop B.service

Мне нужно было решить похожую ситуацию. Подход, который я придумал, предполагает создание целевого отряда как барьер между двумя службами.

услуга:

[Unit]
Wants=a.target

цель:

[Unit]
Requisite=a.service
After=a.service
PartOf=a.service
RefuseManualStart=true
RefuseManualStop=true

б. сервис:

[Unit]
BindsTo=a.target
After=a.target

Эта настройка обеспечивает поведение, желаемое OP, поскольку a.target всегда активен с a.service (PartOf останавливает a.target, когда a.service остановлен, RefuseManual * избегает команд systemctl start / stop на a.target) и b. сервис не может запустить a.service через a.target.

Я использовал тот же подход, что и Крис, но без необходимости создавать отдельную службу для остановки B:

Б. сервис:

[Unit]
Requisite=A.service
After=A.service

Услуга:

[Service]
ExecStopPost=/bin/systemctl stop B.service

В Requisite строка в B.service гарантирует, что B.service не может быть запущен, если A.service еще не запущен, в то время как ExecStopPost строка в A.service гарантирует, что B.service будет остановлен, когда A.service остановлен или переходит в неактивное состояние.