Вопрос или проблема
У меня есть два сервиса A и B, которые работают непрерывно, для них я использую systemd
единицы сервиса. В определенные моменты сервис C должен запускаться, я могу сделать это, используя таймер и сервис с тем же именем. C завершается через короткое время. Когда C работает, A и B не должны работать. Чтобы гарантировать это, я использую в файле сервиса для C опцию Conflicts=
, которая объясняется на этой веб-странице: документация systemd. Но эта опция лишь останавливает A и B. Как я могу снова запустить A и B, когда C завершится? Могу ли я сделать это, используя systemd
?
Примечание: Вызов systemctl start A.service
и systemctl start B.service
не является тем решением, которое я ищу.
Я рекомендую использовать:
ExecStopPost=/usr/bin/systemctl start A.service B.service
Дополнительные команды, которые выполняются после остановки сервиса
Использование ExecStopPost
вместо ExecStop
важно, потому что, когда выполняется ExecStop
, сервис все еще считается работающим, так что конфликт все еще будет применяться.
У меня была та же проблема.
Мой сервис локальный (определен в /usr/local/lib/systemd/system/A.service
).
Я не хочу изменять файл сервиса системного пакета.
A
должен останавливаться и запускаться только в том случае, если он уже был активен.
Это решение использует два файла:
Переопределение для системного сервиса /usr/local/lib/systemd/system/C.service.d/pause-A.conf
:
[Service]
ExecStartPre=/usr/local/sbin/pause-A pause
ExecStopPost=/usr/local/sbin/pause-A unpause
(Файл .conf
может иметь любое описательное имя, которое вы хотите, но его родительская папка должна носить имя переопределяемого сервиса с суффиксом .d
.)
И его упоминаемый скрипт /usr/local/sbin/pause-A
, сделанный исполняемым:
#!/bin/sh
set -e
svc=A.service
runfile=/run/$svc-is-paused
case "${1:-is-paused}" in
pause)
! systemctl is-active --quiet "$svc" || {
touch "$runfile" && systemctl stop "$svc"
}
;;
unpause)
! [ -e "$runfile" ] || {
rm "$runfile" && systemctl start "$svc"
}
;;
is-paused)
# сообщить статус через код выхода
[ -e "$runfile" ]
;;
*)
exit 2
;;
esac
Я вообще не использую Conflicts=
, потому что это не делает явной записи о том, что C
остановил A
(если не копаться в journalctl
), поэтому, когда C
завершается, A
не может быть уверенным, следует ли ему возобновить работу или оставаться неактивным.
После создания ваших версий этих файлов вам нужно будет выполнить:
systemctl daemon-reload
Ответ или решение
Для решения проблемы с повторным запуском сервисов A и B после завершения сервиса C, который остановлен с помощью опции Conflicts=
в системе инициализации systemd, необходимо рассмотреть несколько подходов, которые позволят обеспечить желаемое поведение без использования команды systemctl start
.
Ваша задача заключается в том, чтобы сервисы A и B автоматически перезапустились после завершения сервиса C, который временно их останавливает. Поскольку опция Conflicts=
только прекращает выполнение сервисов A и B, нам необходимо использовать настройку, которая позволяет их автоматически запустить снова после выполнения C.
Решение с использованием ExecStopPost
Одним из простых и эффективных решений является добавление в конфигурацию сервиса C команды ExecStopPost
. Эта команда выполняется после остановки сервиса, и благодаря этому можно запустить A и B. Пример:
[Service]
ExecStopPost=/usr/bin/systemctl start A.service B.service
Важно помнить, что использование ExecStopPost
вместо ExecStop
критично, так как при выполнении ExecStop
сервис все еще считается работающим, и любые конфликты продолжат применяться. Таким образом, выполнение перезапуска возможно только после завершения работы сервиса C.
Альтернативный подход с помощью статики
Если вы хотите избежать конфигурирования команд в одном файле, существует вариант использования статического файла конфигурации (override file) для сервиса C. Это может быть реализовано через создание директории для переопределения, например, /usr/local/lib/systemd/system/C.service.d/
и файла pause-A.conf
с содержанием:
[Service]
ExecStartPre=/usr/local/sbin/pause-A pause
ExecStopPost=/usr/local/sbin/pause-A unpause
Здесь ExecStartPre
вызывает скрипт pause-A
, который временно останавливает сервис A перед выполнением C, и ExecStopPost
восстанавливает его после завершения.
Пример скрипта pause-A
:
#!/bin/sh
set -e
svc=A.service
runfile=/run/$svc-is-paused
case "${1:-is-paused}" in
pause)
! systemctl is-active --quiet "$svc" || {
touch "$runfile" && systemctl stop "$svc"
}
;;
unpause)
! [ -e "$runfile" ] || {
rm "$runfile" && systemctl start "$svc"
}
;;
is-paused)
[ -e "$runfile" ]
;;
*)
exit 2
;;
esac
Этот сценарий будет проверять, запущен ли сервис A, и останавливать его при необходимости. При запуске с параметром unpause
он восстановит сервис A.
Заключение
Таким образом, с помощью указанных методов можно добиться автоматического перезапуска сервисов A и B после завершения сервиса C без необходимости ручного вызова команд через systemctl
. Применение подхода с ExecStopPost
или использование локального скрипта pause-A
создаст эффективный контроль за состоянием сервисов, что повысит надежность и управляемость серверных приложений.
Не забудьте завершить настройки, выполнив команду:
systemctl daemon-reload
Это обновит конфигурацию systemd и обеспечит правильную работу ваших сервисов.