Eu tenho um arquivo chamado botstart.service
com /etc/systemd/system
este conteúdo:
[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
Comecei com systemctl enable botstart
e dizia que estava habilitado. Reiniciei meu VPS, mas o script não foi executado. Eu fiz systemctl status botstart
e mostrou isso:
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)
Se eu executar o script manualmente, ele funcionará, mas de alguma forma não como um serviço, mas o serviço será executado e o script será executado (é o que o status do serviço está me dizendo). Alguém sabe por quê? Este é o código do script:
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"
Responder1
Na sua [Service]
seção, você não definiu explicitamente um arquivo Type=
. Isso significa que o padrão será Type=simple
.
A página de manual diz:
Se definido como simples, o gerenciador de serviço considerará a unidade iniciada imediatamente após o processo de serviço principal ter sido interrompido. Espera-se que o processo configurado com ExecStart= seja o processo principal do serviço....
Resumindo, seu script é o processo principal. Ele inicia vários processos e depois sai. systemd
vê a saída do seu processo principal e, em seguida, procede à limpeza dos processos filhos que não têm mais um pai.
Em vez disso você quer Type=forking
.
Se definido como forking, espera-se que o processo configurado com ExecStart= chame fork() como parte de sua inicialização. Espera-se que o processo pai seja encerrado quando a inicialização for concluída e todos os canais de comunicação estiverem configurados. O processo filho continua a ser executado como o processo de serviço principal e o gerente de serviço considerará a unidade iniciada quando o processo pai for encerrado. Este é o comportamento dos serviços UNIX tradicionais. Se esta configuração for usada, é recomendável usar também a opção PIDFile=, para que o systemd possa identificar com segurança o processo principal do serviço.
Nesse caso, seus processos filhos poderão continuar vivos, mesmo após o término do script. Se você adicionar Type=forking
, as coisas funcionarão melhor para você.
Observe que existem alguns outros problemas com seu design. Se a única alteração feita for Type=forking
, você ainda terá alguns problemas:
- Se um dos processos terminar, o systemd pode decidir ou não qual é o MainPID e considerar todo o seu serviço
inactive (dead)
. Escrever para aPIDFile=
pode ajudar com isso, mas suspeito que você não pretende que nenhum desses processos seja o MainPID. - Se um processo falhar,
systemd
não poderá reportar um estado de falha. Não está claro o que deve acontecer com os outros processos.
Recomendação 1: divida cada processo em seu próprio serviço. Então Type=simple
funcionará. Dessa forma, se um serviço falhar, você poderá detectá-lo e operá-lo de maneira confiável, sem afetar os outros bots.
Recomendação 2: Adicione Restart=on-failure
. Isso permitirá que um serviço com falha seja reiniciado automaticamente (sem necessidade de intervenção humana).
Recomendação 3: Não ligue para bash
quais chamadas screen
para quais chamadas python3
. Basta ligar diretamente para python. screen
é uma solução alternativa que não é necessária em um systemd
ambiente.
Recomendação 4: Para implementar isso sleep 3
, você pode usar umExecStartPre=
Seguindo essas recomendações, nyoko.service
ficaria assim:
[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
Recomendação 5: sleep
não é muito confiável. Num dia bom, perde 3 segundos. Num dia ruim, não é suficiente e seu serviço irá falhar. Considere adicionar itens After=lavalink.service
à sua [Unit]
seção para garantir lavalink.service
que seja iniciado antes de algo que dependa dela. Se lavalink
houver algum tipo de sinal para mostrar que ele foi iniciado (o arquivo foi criado ou o soquete foi aberto), você poderá acioná-lo.