Configure el servicio buggy systemd para finalizar a través de SIGKILL

20

Fondo

Me pidieron que creara un systemdscript para un nuevo servicio, foo_daemonque a veces entra en un "mal estado" y no morirá SIGTERM(probablemente debido a un controlador de señal personalizado). Esto es problemático para los desarrolladores, ya que se les indica que inicien / detengan / reinicien el servicio a través de:

  • systemctl start foo_daemon.service
  • systemctl stop foo_daemon.service
  • systemctl restart foo_daemon.service

Problema

A veces, debido a foo_daemonun mal estado, tenemos que matarlo por la fuerza a través de:

  • systemctl kill -s KILL foo_daemon.service

Pregunta

¿Cómo puedo configurar mi systemdscript para foo_daemonque, cada vez que un usuario intente detener / reiniciar el servicio, systemdpueda:

  • Intenta un apagado elegante de foo_daemonvia SIGTERM.
  • Espere hasta 2 segundos para foo_daemonque se complete el apagado / finalización .
  • Intente un apagado forzado de foo_daemonvia SIGKILLsi el proceso todavía está vivo (por lo que no tenemos el riesgo de que el PID sea reciclado y los systemdproblemas SIGKILLcon el PID incorrecto). El dispositivo que estamos probando genera / bifurca numerosos procesos rápidamente, por lo que existe una preocupación rara pero muy real sobre el reciclaje de PID que causa un problema.
  • Si, en la práctica, estoy siendo paranoico sobre el reciclaje de PID, estoy de acuerdo con que el script solo emita SIGKILLcontra el proceso 'PID sin preocuparme por matar un PID reciclado.

Nube
fuente
2
Incluso si genera procesos lo suficientemente rápido como para rodar más de 4 millones de PID en dos segundos, systemd no se sienta en un bucle comprobando "¿este pid sigue vivo? ¿Este pid sigue vivo?" porque no necesita hacerlo; ya está informado sobre si sus procesos secundarios inmediatos siguen vivos o no (por medio de SIGCHLD ordinario y waitpid ()). Entonces, si ve que el proceso salió después de SIGTERM, simplemente marcará el servicio como 'inactivo' en ese punto, no se molestará en verificar, esperar y enviar el SIGKILL.
Grawity

Respuestas:

26

systemd ya admite esto de forma inmediata, y está habilitado de forma predeterminada .

Lo único que puede personalizar es el tiempo de espera, que puede hacer con TimeoutStopSec=. Por ejemplo:

[Service]
TimeoutStopSec=2

Ahora, systemd enviará un SIGTERM, esperará dos segundos para que salga el servicio, y si no lo hace, enviará un SIGKILL.

Si su servicio no es compatible con systemd, es posible que deba proporcionar la ruta a su archivo PID PIDFile=.

Finalmente, mencionaste que tu demonio genera muchos procesos. En este caso, es posible que desee configurar KillMode=control-groupy systemd enviará señales a todos los procesos en el cgroup.

Michael Hampton
fuente
Gracias. Una última pregunta: supongamos que el servicio no reconoce el sistema. ¿Qué podría agregar al script systemd para este servicio para que systemd cree / administre el archivo PID? Además, el servicio puede ser de varias instancias a través de unidades de plantilla, por lo que generalmente lo lanzamos a través de `systemctl start [email protected]", ¿eso afectaría la lógica del archivo PID en el script?
Nube
44
@DevNull systemd no crea ni administra archivos PID. No hay razón para que lo haga. Si su servicio no crea su propio archivo PID, si es posible configúrelo para que se ejecute en primer plano (en lugar de demonizar) y configúrelo Type=simpleen la unidad systemd.
Michael Hampton
1
Si el servicio tiene dependientes, Type=forkingtiene la ventaja de (si el servicio se escribió correctamente) informar a systemd cuando está completamente 'listo', lo que Type = simple no puede hacer. Daemonizing no es un problema, incluso sin un archivo PID: systemd rastreará el proceso principal de todos modos.
Grawity
1
@grawity Es cierto ... aunque ha sido mi experiencia que los servicios demonizan antes de que estén realmente listos para comenzar a servir. El uso de un servicio compatible con Type=notifysystemd es lo mejor para systemd, y muchos servicios comunes ya lo hacen. Pero probablemente no este servicio heredado. En el caso del OP, tiene un servicio que genera muchos procesos. Los documentos del sistema advierten sobre este caso .
Michael Hampton
1

Como nadie mencionó la necesidad Type=oneshot, aquí hay un ejemplo completo que sale debido a una falla de tiempo de espera.

[Unit]
Description=timeout test

[Service]
Type=oneshot
TimeoutStartSec=2
ExecStart=/bin/sleep 10
Evidlo
fuente