FNSP Web Page

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.target

O 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 coisas

Quem 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=60

Cinco 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.env

A 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 o After= 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: sem PIDFile=, 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/dados

Impedem 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.service

O ú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.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.