Как может флаг службы systemd быть готовым, чтобы другие службы могли дождаться его готовности, прежде чем начать?

8

У меня есть несколько служб (скажем C0, C1C9), которые должны запускаться только после того, как служба Sзавершит свою инициализацию и будет полностью запущена и готова для других служб. Как мне это организовать с помощью systemd?

При заказе сервисов с активацией пути и целью в systemd предполагается, что сервис Sимеет механизм для записи какого-либо файла флага. Здесь, напротив, предположим, что у меня есть полный контроль над программой, которую Sзапускает служба , и я могу добавить в нее механизмы systemd, если потребуется.

JdeBP
источник

Ответы:

7

Это не обязательно нужно.

Если Cсервисам нужно дождаться Sготовности, чтобы они могли открыть сокет-соединение с ним, то вообще не обязательно делать это. Скорее, можно воспользоваться преимуществами раннего открытия сокетов прослушивания менеджерами сервисов.

Некоторые системы, в том числе s6 Лорана Берко , мой набор инструментов nosh и systemd, имеют способы, с помощью которых сокет прослушивания можно открыть на ранней стадии, что является самым первым шагом при настройке службы. Все они включают в себя нечто иное, чем служебная программа, открывающая сокет (ы) прослушивания, и служебная программа, когда она вызывается, получая сокеты (ы) прослушивания в качестве уже открытых дескрипторов файла.

В частности, с помощью systemd создается модуль сокета, который определяет сокет прослушивания. systemd открывает модуль сокета и устанавливает его так, чтобы сетевая подсистема ядра прослушивала соединения; и передает его фактической службе в качестве дескриптора открытого файла, когда дело доходит до порождения процесса (ов), которые обрабатывают соединения с сокетом. (Он может сделать это двумя способами, точно так же, как inetdмог бы, но обсуждение деталей Accept=trueпротив Accept=falseуслуг выходит за рамки этого ответа.)

Важным моментом является то, что не обязательно нужно больше упорядочивать, чем это. Ядро объединяет клиентские соединения в очереди до тех пор, пока сервисная программа не будет инициализирована и не готова принять их и общаться с клиентами.

Когда это происходит, протоколы готовности - это самое главное.

У systemd есть набор протоколов готовности, которые он понимает, указав сервис по сервису с Type=настройкой в ​​сервисном блоке. Особый интересующий notifyпротокол готовности - это протокол готовности. С его помощью systemd сообщается, что он ожидает сообщения от службы, а когда служба готова, она отправляет сообщение, указывающее на готовность. systemd задерживает активацию других сервисов, пока не будет помечена готовность.

Использование этого включает в себя две вещи:

  • Модифицируя код Sтак, чтобы он вызывал что-то вроде функции Пьера-Ива Ричарда notify_systemd()или функции Кэмерона Т Нормана notify_socket().
  • Настройка сервисного блока для сервиса с помощью Type=notifyи NotifyAccess=main.

NotifyAccess=mainОграничение (это значение по умолчанию), потому что Systemd нужно знать , чтобы игнорировать сообщения от злонамеренных (или просто неисправных) программ, потому что любой процесс в системе может отправлять сообщения в сокет уведомления Systemd'S.

Один из них использует код Пьера-Ива Ритшара или Камерона Т Нормана для предпочтения, поскольку он не исключает возможности использования этого механизма в UbuntuBSD, Debian FreeBSD, реальных FreeBSD, TrueOS, OpenBSD и т. Д .; который исключает код, предоставленный авторами systemd.

Одна из ловушек, которую следует избегать, - это systemd-notifyпрограмма. У него есть несколько основных проблем, не последняя из которых заключается в том, что отправленные с ним сообщения могут в конечном итоге быть отброшенными необработанными systemd. Самая главная проблема в этом случае заключается в том, что он не запускается как «основной» процесс службы, поэтому необходимо открыть уведомления о готовности службы Sдля каждого процесса в системе NotifyAccess=all.

Еще одна ловушка, которую следует избегать, - думать, что forkingпротокол проще. Не то. Чтобы сделать это правильно, нужно не разветвляться и не выходить из родительского процесса, пока (с одной стороны) не будут запущены все рабочие потоки программы. Это не соответствует тому, как подавляющее большинство демонов, которые разветвляются, на самом деле разветвляются.

дальнейшее чтение

JdeBP
источник
1
По словам человека systemd.service(5), NotifyAccess=allбудут приниматься сообщения от всех членов контрольной группы службы , что не подразумевает просто какого-либо мошеннического процесса в системе. Это достаточно безопасно для большинства случаев использования. Кроме того, ваше беспокойство по поводу переносимости в другие операционные системы не имеет отношения к OP, так как мы уже обсуждаем Systemd здесь.
Амир
1

Обращаясь к странице справочника systemd.service(5), а именно к разделу о Type = , каждый тип сервиса по-разному определяет, готов ли Systemd предлагать функциональность другим сервисам:

  • Если Type=simpleего каналы связи должны быть установлены до запуска демона (например, сокеты, установленные systemd, через активацию сокетов).

  • Если Type=forkingожидается, что родительский процесс завершится после завершения запуска и настройки всех каналов связи.

  • Если Type=dbusожидается, что демон получит имя на шине D-Bus, после чего systemd продолжит запуск последующих устройств.

  • Если Type=notify, ожидается, что демон отправит уведомление через sd_notify(3)или эквивалентный вызов, когда он завершит запуск. systemd продолжит запуск последующих модулей после отправки этого уведомления.

Для последнего варианта (отправка сообщения через sd_notify), вы можете использовать systemd-notifyутилиту, и не забудьте предоставить ей доступ с помощью NotifyAccess=all.

Учитывая, что у вас есть контроль над сервисом S, вы можете выбрать лучший вариант для вашего варианта использования или просто тот, который проще всего реализовать.

эмир
источник
1

нравится:

S.service

[Unit]
Description=My main Service

[Service]
Type=notify
ExecStart=/usr/bin/myBinary

C0.service

[Unit]
Description=Dependent service number 0
PartOf=S.service

C1.service

[Unit]
Description=Dependent service number 1
PartOf=S.service

C9.service

[Unit]
Description=Dependent service number 9
PartOf=S.service

Где / usr / bin / myBinary делает вызов sd_notify READY = 1, когда его инициализация завершена.

В зависимости от того, как вы хотите, чтобы зависимость зависела, вы можете использовать PartOf, Требуется, или BindsTo, или другие .

code_monk
источник