systemd: transformar um script num serviço que arranca sozinho
O programa funciona. Falta pô-lo a correr no servidor, e a primeira tentativa é quase sempre esta:
nohup node servidor.js &Aguenta-se até à primeira coisa a correr mal. Se o processo morrer, ninguém o levanta. Se a máquina reiniciar, ele não volta. A saída vai para um nohup.out que cresce sem limite, e a única forma de saber se está vivo é procurá-lo na lista de processos.
O systemd resolve as quatro coisas com um ficheiro de dez linhas.
1. A unidade mínima
# /etc/systemd/system/o-meu-servico.service
[Unit]
Description=O meu servidor
After=network.target
[Service]
ExecStart=/usr/bin/node /opt/app/servidor.js
WorkingDirectory=/opt/app
User=app
Restart=on-failure
[Install]
WantedBy=multi-user.targetO caminho do executável tem de ser absoluto — o systemd não tem o PATH de ninguém, e é a causa da primeira falha de metade das pessoas.
2. enable não é start
São coisas diferentes, e a confusão entre as duas é a que dá mais chatices.
sudo systemctl daemon-reload # ler o ficheiro novo
sudo systemctl start o-meu-servico # arrancar agora
sudo systemctl enable o-meu-servico # arrancar no próximo arranque
sudo systemctl enable --now o-meu-servico # as duas coisasQuem faz só start tem um serviço a correr que desaparece no reinício seguinte — e a descoberta costuma acontecer semanas depois, com o servidor em baixo.
E o daemon-reload não é opcional: sem ele, o systemd continua a usar a versão anterior do ficheiro, e fica-se a editar sem que nada mude.
3. O reinício, e a armadilha dele
O Restart=on-failure volta a levantar o processo quando ele sai com erro. O Restart=always levanta-o mesmo quando ele sai bem — e é aí que se arma a ratoeira.
Um script que termine imediatamente com always entra num ciclo: arranca, sai, arranca. O systemd tem um travão para isso, e vale a pena conhecê-lo antes de o encontrar:
Restart=always
RestartSec=5
StartLimitBurst=5
StartLimitIntervalSec=60Cinco tentativas em sessenta segundos, e depois desiste. O serviço fica em failed — e é melhor assim do que uma máquina a arrancar o mesmo processo mil vezes por minuto. Para o voltar a tentar depois de corrigido: systemctl reset-failed.
4. Os registos vêm de graça
Não é preciso escrever para ficheiro nenhum. O que o programa mandar para a saída padrão vai para o journal:
journalctl -u o-meu-servico -f # a acompanhar, como um tail
journalctl -u o-meu-servico -n 50 # as últimas 50 linhas
journalctl -u o-meu-servico -p err # só os errosÉ uma das melhores razões para usar isto: acaba a pasta de registos, acaba a rotação para configurar, e o que o serviço diz fica ao lado do resto do que aconteceu na máquina, com as mesmas datas.
5. Quem corre, e com o quê
O User= é a linha que impede o serviço de correr como root, que é o que acontece quando não se escreve nada. E os segredos entram por ficheiro, não escritos ali:
EnvironmentFile=/etc/o-meu-servico.envA tentação é usar Environment= e pôr os valores na unidade. Não convém: o conteúdo de uma unidade é legível por qualquer utilizador da máquina através do systemctl show. Num ficheiro à parte, as permissões protegem-no como qualquer outro segredo.
6. O Type=, em três linhas
simple— o que vem por omissão. O systemd considera o serviço arrancado assim que lança o processo, sem esperar por nada.notify— o serviço avisa quando está mesmo pronto. É o melhor quando a aplicação o suporta, porque faz oAfter=dos outros serviços significar alguma coisa.forking— para programas à moda antiga, que se desdobram e deixam um filho a correr. É a origem de muito sofrimento: semPIDFile=, o systemd perde o rasto ao processo certo e passa a gerir o errado.
7. Três linhas que não custam nada
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/app/dadosImpedem a subida de privilégios, dão ao serviço um /tmp só dele, e tornam o sistema de ficheiros apenas de leitura, com exceção do que for declarado. Para quatro linhas, é muito mais do que a maioria dos serviços tem.
8. Quando não arranca
systemctl status o-meu-servico # o estado e as últimas linhas
journalctl -u o-meu-servico -n 50 # a razão, quase sempre
systemd-analyze verify /etc/systemd/system/o-meu-servico.serviceO último verifica o ficheiro sem o correr, e apanha erros de escrita e caminhos que não existem antes de se perder tempo a adivinhar.
O resumo
Uma unidade de dez linhas dá o que um nohup nunca dá: volta depois de um reinício, levanta-se sozinha quando morre, corre com um utilizador sem privilégios, e escreve para um sítio onde os registos são procuráveis e não enchem o disco. Para agendar tarefas em vez de manter um serviço de pé, o mecanismo é outro — está no artigo sobre cron e timers.