У меня есть служба X, которая должна подключаться к прослушивающему сокету при запуске. Этот целевой сокет сам по себе открыт другой службой Y, запущенной systemd.
Есть ли способ указать в файле модуля (или иным образом), что служба X должна запускаться только после того, как служба Y успешно запущена и открыла прослушивающий сокет?
Обратите внимание, что я не могу изменить службу X для повторной попытки, если первоначальное соединение не удалось. Также фиксированные задержки тоже не работают, потому что службе Y требуется разное количество времени, прежде чем она откроет прослушивающий сокет.
решение1
systemd работает немного по-другому. Вы настраиваете systemd для создания и прослушивания сокета, и если кто-то вроде X попытается подключиться, systemd запускает Y для обработки соединения, передавая ему сокет. Поэтому X может запуститься теоретически до Y, но это не будет иметь значения. Более поздние соединения будут обрабатываться Y. (Вы также можете настроить его так, чтобы Y перезапускался для каждого соединения, но я предполагаю, что это не ваш случай).
Минимальные изменения в Y заключаются в том, чтобы он принимал заранее созданный сокет в качестве своего файлового дескриптора stdin/stdout вместо того, чтобы выполнять создание/привязку самостоятельно.
Вот тестовая настройка, которую вы можете попробовать, не как root. Вам нужно 3 файла юнитов.
~/.local/share/systemd/user/mysock.socket
сообщает systemd о необходимости создания сокета и о том, как его передать:
# create listening socket. on 1st connect run mysock.service
[Socket]
ListenStream=5555
Accept=false
~/.local/share/systemd/user/mysock.service
(имея то же имя mysock
) — это служба, которая будет запущена, если кто-то подключится к сокету. Здесь вы запускаете Y, который я заменил на какой-то python.
[Unit]
Description=started from mysock.socket
[Service]
ExecStart=/home/meuh/bin/python/mysock.py
StandardInput=socket
Наконец, ваш X-сервис имеет Unit, который говорит, что ему требуется сокет. Для X я использую socat, который записывает дату в сокет.
~/.local/share/systemd/user/mysockdepend.service
[Unit]
Description=dependent on socket listening
Requires=mysock.socket
[Service]
ExecStart=/usr/bin/socat -U tcp:localhost:5555 exec:/usr/bin/date
Python берет сокет на stdin, т. е. файловый дескриптор 0, и оборачивает его в подходящий объект сокета Python, выполняет операцию accept()
и может читать/писать в него:
~/bin/python/mysock.py
#!/usr/bin/python
# started from /home/meuh/.local/share/systemd/user/mysock.service
# with socket on stdin/stdout
import sys, time, socket, subprocess
def debug(msg):
# time.sleep(3)
subprocess.call(["/usr/bin/logger", msg])
debug("start\n")
s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
while True:
conn, addr = s.accept()
conn.send("hello\n")
while True:
try:
data = conn.recv(100)
debug("got "+data+"\n")
if len(data)<=0: break
except:
break
conn.close()
После этого systemctl --user daemon-reload
вы сможете запустить X с помощью
systemctl --user start mysockdepend
и посмотрите в логах, journalctl
что Y был запущен, и отладочный вывод с датой.
Прочитать оактивация сокетаи2-йот человека, который его изобрел.
решение2
systemd обрабатывает этот случай с помощьюфайлы сокетов.
some-socket.socket
Для представления сокета будет создан файл модуля systemd с именем .
Затем ваш Service X
файл службы может включать After=
директиву, ссылающуюся на сокет.
Официальная документация systemd по файлам сокетов должна оказаться полезной.