botstart.service
У меня есть файл /etc/systemd/system
с таким содержимым:
[Unit]
Description=Start all discord bots (AntiSpam, AutoChat, Nyoko, Helper) and the lavalink server.
[Service]
ExecStart=/bin/bash /home/scripts/start.sh
[Install]
WantedBy=multi-user.target
Я запустил его, systemctl enable botstart
и он сказал, что он включен. Я перезагрузил свой VPS, но скрипт не был выполнен. Я перезагрузил, systemctl status botstart
и он показал это:
root@Hetzner-01:~# systemctl status botstart
● botstart.service - Start all discord bots (AntiSpam, AutoChat, Nyoko, Helper)
Loaded: loaded (/etc/systemd/system/botstart.service; enabled; vendor preset:
Active: inactive (dead) since Wed 2021-10-27 01:37:10 CEST; 50s ago
Process: 481 ExecStart=/bin/bash /home/scripts/start.sh (code=eited, status=0
Main PID: 481 (code=existed, status=0/SUCCESS)
Oct 27 01:37:07 Hetzner-01 systemd[1]: STarted Start all discord bots (antiSpam,
Oct 27 01:37:10 Hetzner-01 bash[481]: Started all bots
Oct 27 01:37:10 Hetzner-01 systemd[1]: botstart.service: Succeeded.
lines 1-9/9 (END)...skipping...
● botstart.service - Start all discord bots (AntiSpam, AutoChat, Nyoko, Helper) and the lavalink server.
Loaded: loaded (/etc/systemd/system/botstart.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Wed 2021-10-27 01:37:10 CEST; 50s ago
Process: 481 ExecStart=/bin/bash /home/scripts/start.sh (code=eited, status=0/SUCCESS)
Main PID: 481 (code=existed, status=0/SUCCESS)
Если я запускаю скрипт вручную, он работает, но как-то не как служба, но служба запускается и скрипт запускается (именно об этом мне говорит статус службы). Кто-нибудь знает почему? Вот код скрипта:
screen -dmS antispam bash -c "cd /home/AntiSpam; python3.8 main.py"
screen -dmS autochat bash -c "cd /home/AutoChat; python3.8 main.py"
screen -dmS helper bash -c "cd /home/Helper; python3.8 main.py"
screen -dmS lavalink bash -c "cd /home/Lavalink; python3.8 main.py"
sleep 3
screen -dmS nyoko bash -c "cd /home/Nyoko; python3.8 main.py"
echo "Started all bots"
решение1
В вашем [Service]
разделе вы явно не определили Type=
. Это значит, что по умолчанию будет Type=simple
.
На странице руководства написано:
Если установлено значение simple, менеджер сервиса будет считать, что блок запущен сразу после того, как основной процесс сервиса был разветвлен. Ожидается, что процесс, настроенный с помощью ExecStart=, является основным процессом сервиса....
Короче говоря, ваш скрипт — это основной процесс. Он запускает кучу процессов, затем завершает работу. systemd
видит, что ваш основной процесс завершается, затем приступает к очистке дочерних процессов, у которых больше нет родителя.
Вместо этого вы хотите Type=forking
.
Если установлено значение forking, ожидается, что процесс, настроенный с помощью ExecStart=, вызовет fork() как часть своего запуска. Ожидается, что родительский процесс завершит работу, когда запуск будет завершен и все каналы связи будут настроены. Дочерний процесс продолжит работу как основной процесс службы, а менеджер служб будет считать модуль запущенным, когда родительский процесс завершит работу. Это поведение традиционных служб UNIX. Если используется эта настройка, рекомендуется также использовать опцию PIDFile=, чтобы systemd мог надежно идентифицировать основной процесс службы.
В этом случае ваши дочерние процессы будут жить, даже после того, как ваш скрипт завершится. Если вы добавите Type=forking
, все должно работать лучше для вас.
Обратите внимание, что есть и другие проблемы с вашим дизайном. Если единственное изменение, которое вы вносите, это Type=forking
, то у вас все еще есть несколько проблем:
- Если один из процессов завершается, systemd может решить, что это MainPID, а может и нет, и рассмотреть всю вашу службу
inactive (dead)
. Запись вPIDFile=
может помочь в этом, но я подозреваю, что вы не собираетесь, чтобы какой-либо из этих процессов был MainPID. - Если процесс дает сбой,
systemd
может не сообщать о состоянии сбоя. Неясно, что должно произойти с другими процессами.
Рекомендация 1: разделите каждый процесс на отдельную службу. Тогда Type=simple
это сработает. Таким образом, если одна служба выйдет из строя, вы сможете обнаружить это и надежно работать с ней, не влияя на других ботов.
Рекомендация 2: Добавить Restart=on-failure
. Это позволит неисправной службе автоматически перезапуститься (вмешательство человека не требуется).
Рекомендация 3: Не вызывайте bash
which calls screen
which calls python3
. Просто вызывайте python напрямую. screen
— это обходной путь, который не нужен в systemd
среде.
Рекомендация 4: Чтобы реализовать это sleep 3
, вы можете использоватьExecStartPre=
Следуя этим рекомендациям, nyoko.service
это будет выглядеть так:
[Unit]
Description=Nyoko discord bot
[Service]
WorkingDirectory=/home/Nyoko
ExecStartPre=/usr/bin/sleep 3
ExecStart=/usr/bin/python3 /home/Nyoko/main.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Рекомендация 5: sleep
не слишком надежен. В хороший день он тратит 3 секунды. В плохой день этого недостаточно, и ваша служба даст сбой. Рассмотрите возможность добавления After=lavalink.service
в свой [Unit]
раздел, чтобы гарантировать lavalink.service
запуск до того, что что-то от него зависит. Если lavalink
есть какой-то сигнал, показывающий, что он запущен (создан файл или открыт сокет), то вы можете вместо этого запустить его.