Estou usando o systemd 231 em um sistema embarcado e estou tentando criar um serviço que monitora um componente de hardware no sistema. Aqui está uma descrição aproximada do que estou tentando fazer:
- Quando o serviço,
foo.service
, é iniciado, ele inicia um aplicativo,foo_app
. foo_app
monitora o componente de hardware, rodando continuamente.- Se
foo_app
detectar uma falha de hardware, ele sai com um código de retorno de 1. Isso deve acionar uma reinicialização do sistema. - Se
foo_app
travar, o systemd deve reiniciarfoo_app
. - Se travar
foo_app
repetidamente , o systemd deve reiniciar o sistema.
Aqui está minha tentativa de implementar isso como um serviço:
[Unit]
Description=Foo Hardware Monitor
# If the application fails 3 times in 30 seconds, something has gone wrong,
# and the state of the hardware can't be guaranteed. Reboot the system here.
StartLimitBurst=3
StartLimitIntervalSec=30
StartLimitAction=reboot
# StartLimitAction=reboot will reboot the box if the app fails repeatedly,
# but if the app exits voluntarily, the reboot should trigger immediately
OnFailure=systemd-reboot.service
[Service]
ExecStart=/usr/bin/foo_app
# If the app fails from an abnormal condition (e.g. crash), try to
# restart it (within the limits of StartLimit*).
Restart=on-abnormal
A partir da documentação ( systemd.service e systemd.service ), eu esperaria que, se eu matar foo_app
de uma forma que Restart=on-abnormal
seja acionada (por exemplo killall -9 foo_app
), o systemd deveria dar prioridade a Restart=on-abnormal
over OnFailure=systemd-reboot.service
e não start systemd-reboot.service
.
No entanto, não é isso que estou vendo. Assim que eu mato foo_app
uma vez, o sistema reinicia imediatamente.
Aqui estão alguns trechos relevantes dos documentos:
OnFailure=
Uma lista separada por espaços de uma ou mais unidades que são ativadas quando esta unidade entra no estado "falha". Uma unidade de serviço usando Restart= entra no estado de falha somente depois que os limites de partida são atingidos.
Restart=
[recorte] Observe que a reinicialização do serviço está sujeita à limitação da taxa de inicialização da unidade configurada com StartLimitIntervalSec= e StartLimitBurst=, consulte systemd.unit(5) para obter detalhes. Um serviço reiniciado entra no estado de falha somente depois que os limites iniciais são atingidos.
A documentação parece bastante clara:
- Os serviços especificados em
OnFailure
só devem ser executados quando um serviço entrar nofailed
estado " " - Um serviço só deve entrar no
failed
estado " " apósStartLimitIntervalSec
eStartLimitBurst
estar satisfeito.
Não é isso que estou vendo.
Para confirmar isso, editei meu arquivo de serviço para o seguinte:
[Unit]
Description=Foo Hardware Monitor
StartLimitBurst=3
StartLimitIntervalSec=30
StartLimitAction=none
[Service]
ExecStart=/usr/bin/foo_app
Restart=on-abnormal
Ao remover OnFailure
e configurar StartLimitAction=none
, pude ver como o systemd está respondendo à foo_app
morte. Aqui está um teste em que mato repetidamente foo_app
com SIGKILL
.
[root@device ~]
# systemctl start foo.service
[root@device ~]
# journalctl -f -o cat -u foo.service &
[1] 2107
Started Foo Hardware Monitor.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
Started foo.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
Started foo.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
foo.service: Start request repeated too quickly
Failed to start foo.
foo.service: Unit entered failed state.
foo.service: Failed with result 'start-limit-hit'
Isso faz sentido ou a maior parte. Quando foo_app
é morto, o systemd o reinicia até StartLimitBurst
ser atingido e depois desiste. Isso é o que eu quero, exceto com StartLimitAction=reboot
.
O que é incomum é que systemd imprime foo.service: Unit entered failed state.
sempre que foo_app
é eliminado, mesmo que esteja prestes a ser reiniciado por meio de Restart=on-abnormal
. Isso parece contradizer diretamente essas linhas dos documentos citados acima:
Uma unidade de serviço usando Restart= entra no estado de falha somente depois que os limites de partida são atingidos.
Um serviço reiniciado entra no estado de falha somente depois que os limites iniciais são atingidos.
Tudo isso me deixou bastante confuso. Estou entendendo mal alguma dessas opções do systemd? Isso é um bug do systemd? Qualquer ajuda é apreciada.
Editar 2019/08/12: Pelo
therealjumbo
comentário de, a correção para isso foi mesclada e lançada comsystemd
v239, portanto, se você não está preso a uma versão devido à sua distribuição (olhando para você CentOS) , atualize e divirta-se!TL;DR - Problema de documentação conhecido, atualmente ainda um problema pendente para o
systemd
projetoAcontece que, desde que você fez essa pergunta, isso foi relatado e identificado como uma discrepância entre
systemd
a documentação e o comportamento real. No meu entendimento (e na minha leitura do problema do github), sua expectativa e a documentação correspondem, então você não está louco.Atualmente
systemd
define o estado como falha após cada tentativa de inicialização, independentemente de o limite inicial ter sido atingido. Na edição, o OP escreveu uma anedota divertida sobre aprender a andar de bicicleta que eu sugiro dar uma olhada.