Как создать виртуальный сервис systemd для остановки / запуска нескольких экземпляров вместе?

12

Я планирую разместить несколько экземпляров одного и того же веб-приложения для клиентов systemd. Я хотел бы иметь возможность stopи startкаждый экземпляр клиента с использованием systemd, а также обрабатывать всю коллекцию экземпляров клиентов в качестве одной службы , которые могут быть остановлены и начали вместе.

systemdкажется, предоставляет необходимые мне строительные блоки PartOfи файлы модулей шаблона, но я остановил родительскую службу, дочерняя служба поддержки клиентов не остановилась. Как я могу заставить это работать с systemd? Вот что у меня так далеко.

Файл родительского блока app.service:

[Unit]
Description=App Web Service

[Service]
# Don't run as a deamon (because we've got nothing to do directly)
Type=oneshot
# Just print something, because ExecStart is required
ExecStart=/bin/echo "App Service exists only to collectively start and stop App instances"
# Keep running after Exit start finished, because we want the instances that depend on this to keep running
RemainAfterExit=yes
StandardOutput=journal

Файл шаблона модуля с именем app@.service, используемый для создания экземпляров клиента:

[Unit]
Description=%I Instance of App Web Service

[Service]
PartOf=app.service
ExecStart=/home/mark/bin/app-poc.sh %i
StandardOutput=journal

Мой app-poc.shсценарий (подтверждение концепции, которая просто печатает в файл журнала в цикле):

#!/bin/bash
# Just a temporary code to fake a full daemon.
while :
do
  echo "The App PoC loop for $@"
  sleep 2;
done

Для подтверждения концепции у меня есть файлы модулей systemd ~/.config/systemd/user.

Затем я запускаю родителя и экземпляр на основе шаблона (после systemctl --user daemon-reload):

systemctl --user start app
systemctl --user start app@customer.service

Из использования journalctl -fя вижу, что оба запустились и что экземпляр клиента продолжает работать. Теперь я ожидаю, что закрытие родителя остановит ребенка (потому что я использовал PartOf), но это не так. Кроме того, запуск родителя не запускает ребенка, как ожидалось.

systemctl --user stop app

Благодарность!

(Я использую Ubuntu 16.04 с systemd 229).

Марк Стосберг
источник
1
«PartOf = Настраивает зависимости, подобные« Требуется = », но ограничивается остановкой и перезапуском модулей». Если вы хотите начать работать, вам не нужно использовать Requires=вместо этого?
sourcejedi

Ответы:

10

Вам нужно переместить линию

PartOf=app.service

из [Service]и в [Unit]разделе, и добавить к [Unit]из app.serviceсписка клиентов , чтобы начать, например ,

Wants=app@customer1.service app@customer2.service

или, как сказал источник в комментариях, Requires=то же самое. Вы можете оставить PartOfслужбы, которые вы запускаете вручную, которых нет в списке выше, например systemctl --user start app@customer3.service.

meuh
источник
Я подтвердил, что ты был прав PartOf. Благодарю. Я собираюсь обработать «Wants» через символическую ссылку, которая станет единственным действием, которое мне нужно предпринять, чтобы активировать нового клиента с помощью systemd. Для моего теста: `ln -s /home/mark/.config/systemd/user/app@.service / home / mark / .config / systemd / user / app.service.wants / unity @ foo.service`
Марк Стосберг
13

Я узнал, что для этого предназначены системные целевые единицы. Используя Target Unit, я получаю преимущества, которые мне нужны, без необходимости создавать поддельный [Service]раздел, который я имел выше. Рабочий пример файла «Целевая единица» выглядит так:

# named like app.target
[Unit]
Description=App Web Service

# This collection of apps should be started at boot time.
[Install]
WantedBy=multi-user.target

Затем каждый экземпляр клиента должен быть включен PartOfв [Unit]раздел (как указано @meuh), а также должен иметь [Install]раздел, чтобы enableи disableработал над конкретной службой:

# In a file name like app@.service
[Unit]
Description=%I Instance of App Web Service
PartOf=app.target

[Service]
ExecStart=/home/mark/bin/app-poc.sh %i
Restart=on-failure
StandardOutput=journal

# When the service runs globally, make it run as a particular user for added security
#User=myapp
#Group=myapp

# When systemctl enable is used, make this start when the App service starts
[Install]
WantedBy=app.target

Чтобы вызвать экземпляр клиента и запустить его при запуске цели, используется эта одноразовая команда включения:

 systemctl enable app

Теперь в этот момент я могу использовать stopи startна app@customerк для экземпляра конкретного, или я могу использовать start appи stop appостановить все приложения вместе.

Марк Стосберг
источник
Как насчет статуса? Я не могу найти простой способ получить статус всех служб, которые хотел App. Я знаю, как я могу
написать
1
Я имею в виду получение статуса приложений в этой целевой группе без перечисления всего, что является его частью, подстановочных знаков или нет, предпочтительно с использованием названия этой группы и даже не заботясь о том, из чего она сделана.
Томми Кинтола
2
Это не так просто. К какому пакету принадлежит этот скрипт? Это должно быть изменено каждый раз, когда добавляется новый компонент. Забудьте об этом, и развертывание / обслуживание становится бесполезным. Очевидно, что я хотел бы просто добавить новый пакет с параметром partOf, указывающим на его присутствие в этой группе, а не изменять какой-то оставшийся скрипт. И затем остановка и запуск этой цели работает как раньше. Это работает, но статус, кажется, выпадает из этой области. Я даже не могу найти способ получить список единиц, которые присутствуют во время выполнения в цели. Этот вариант использования не охватывается systemd.
Томми Кинтола
2
@TommiKyntola Вот одна строчка bash, которую не нужно обновлять при изменении целевых зависимостей:systemctl status $(systemctl list-dependencies --plain otp.target)
Марк Стосберг
2
@ TommiKyntola Я согласен, что systemdможет улучшить удобство использования здесь. Я открыл запрос функции, чтобы предложить улучшенный статус для целей.
Марк Стосберг