systemd: Перезапустите единицы, остановленные параметром Conflicts

Вопрос или проблема

У меня есть два сервиса 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 и обеспечит правильную работу ваших сервисов.

Оцените материал
Добавить комментарий

Капча загружается...