Reinicio del servicio systemd en caso de fallo de dependencia

26

¿Cuál es el enfoque correcto para manejar el reinicio de un servicio en caso de que una de sus dependencias falle al inicio (pero tenga éxito después de volver a intentarlo)?

Aquí hay una réplica artificial para aclarar el problema.

Un servicio (simula fallas en el primer intento y éxito en el segundo intento)

[Unit]
Description=A

[Service]
ExecStartPre=/bin/sh -x -c "[ -f /tmp/success ] || (touch /tmp/success && sleep 10)"
ExecStart=/bin/true
TimeoutStartSec=5
Restart=on-failure
RestartSec=5
RemainAfterExit=yes

B. servicio (trivialmente tiene éxito después de que A comienza)

[Unit]
Description=B
After=a.service
Requires=a.service

[Service]
ExecStart=/bin/true
RemainAfterExit=yes
Restart=on-failure
RestartSec=5

Comencemos b:

# systemctl start b
A dependency job for b.service failed. See 'journalctl -xe' for details.

Registros:

Jun 30 21:34:54 debug systemd[1]: Starting A...
Jun 30 21:34:54 debug sh[1308]: + '[' -f /tmp/success ']'
Jun 30 21:34:54 debug sh[1308]: + touch /tmp/success
Jun 30 21:34:54 debug sh[1308]: + sleep 10
Jun 30 21:34:59 debug systemd[1]: a.service start-pre operation timed out. Terminating.
Jun 30 21:34:59 debug systemd[1]: Failed to start A.
Jun 30 21:34:59 debug systemd[1]: Dependency failed for B.
Jun 30 21:34:59 debug systemd[1]: Job b.service/start failed with result 'dependency'.
Jun 30 21:34:59 debug systemd[1]: Unit a.service entered failed state.
Jun 30 21:34:59 debug systemd[1]: a.service failed.
Jun 30 21:35:04 debug systemd[1]: a.service holdoff time over, scheduling restart.
Jun 30 21:35:04 debug systemd[1]: Starting A...
Jun 30 21:35:04 debug systemd[1]: Started A.
Jun 30 21:35:04 debug sh[1314]: + '[' -f /tmp/success ']'

A se ha iniciado correctamente pero B se deja en un estado fallido y no volverá a intentarlo.

EDITAR

Agregué lo siguiente a ambos servicios y ahora B comienza con éxito cuando A comienza, pero no puedo explicar por qué.

[Install]
WantedBy=multi-user.target

¿Por qué esto afectaría la relación entre A y B?

EDIT2

El "arreglo" anterior no funciona en systemd 220.

registros de depuración systemd 219

systemd219 systemd[1]: Trying to enqueue job b.service/start/replace
systemd219 systemd[1]: Installed new job b.service/start as 3454
systemd219 systemd[1]: Installed new job a.service/start as 3455
systemd219 systemd[1]: Enqueued job b.service/start as 3454
systemd219 systemd[1]: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch oldcoreos
systemd219 systemd[1]: Forked /bin/sh as 1502
systemd219 systemd[1]: a.service changed dead -> start-pre
systemd219 systemd[1]: Starting A...
systemd219 systemd[1502]: Executing: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmpoldcoreos
systemd219 sh[1502]: + '[' -f /tmp/success ']'
systemd219 sh[1502]: + touch /tmp/success
systemd219 sh[1502]: + sleep 10
systemd219 systemd[1]: a.service start-pre operation timed out. Terminating.
systemd219 systemd[1]: a.service changed start-pre -> final-sigterm
systemd219 systemd[1]: Child 1502 belongs to a.service
systemd219 systemd[1]: a.service: control process exited, code=killed status=15
systemd219 systemd[1]: a.service got final SIGCHLD for state final-sigterm
systemd219 systemd[1]: a.service changed final-sigterm -> failed
systemd219 systemd[1]: Job a.service/start finished, result=failed
systemd219 systemd[1]: Failed to start A.
systemd219 systemd[1]: Job b.service/start finished, result=dependency
systemd219 systemd[1]: Dependency failed for B.
systemd219 systemd[1]: Job b.service/start failed with result 'dependency'.
systemd219 systemd[1]: Unit a.service entered failed state.
systemd219 systemd[1]: a.service failed.
systemd219 systemd[1]: a.service changed failed -> auto-restart
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: a.service holdoff time over, scheduling restart.
systemd219 systemd[1]: Trying to enqueue job a.service/restart/fail
systemd219 systemd[1]: Installed new job a.service/restart as 3718
systemd219 systemd[1]: Installed new job b.service/restart as 3803
systemd219 systemd[1]: Enqueued job a.service/restart as 3718
systemd219 systemd[1]: a.service scheduled restart job.
systemd219 systemd[1]: Job b.service/restart finished, result=done
systemd219 systemd[1]: Converting job b.service/restart -> b.service/start
systemd219 systemd[1]: a.service changed auto-restart -> dead
systemd219 systemd[1]: Job a.service/restart finished, result=done
systemd219 systemd[1]: Converting job a.service/restart -> a.service/start
systemd219 systemd[1]: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch oldcoreos
systemd219 systemd[1]: Forked /bin/sh as 1558
systemd219 systemd[1]: a.service changed dead -> start-pre
systemd219 systemd[1]: Starting A...
systemd219 systemd[1]: Child 1558 belongs to a.service
systemd219 systemd[1]: a.service: control process exited, code=exited status=0
systemd219 systemd[1]: a.service got final SIGCHLD for state start-pre
systemd219 systemd[1]: About to execute: /bin/true
systemd219 systemd[1]: Forked /bin/true as 1561
systemd219 systemd[1]: a.service changed start-pre -> running
systemd219 systemd[1]: Job a.service/start finished, result=done
systemd219 systemd[1]: Started A.
systemd219 systemd[1]: Child 1561 belongs to a.service
systemd219 systemd[1]: a.service: main process exited, code=exited, status=0/SUCCESS
systemd219 systemd[1]: a.service changed running -> exited
systemd219 systemd[1]: a.service: cgroup is empty
systemd219 systemd[1]: About to execute: /bin/true
systemd219 systemd[1]: Forked /bin/true as 1563
systemd219 systemd[1]: b.service changed dead -> running
systemd219 systemd[1]: Job b.service/start finished, result=done
systemd219 systemd[1]: Started B.
systemd219 systemd[1]: Starting B...
systemd219 systemd[1]: Child 1563 belongs to b.service
systemd219 systemd[1]: b.service: main process exited, code=exited, status=0/SUCCESS
systemd219 systemd[1]: b.service changed running -> exited
systemd219 systemd[1]: b.service: cgroup is empty
systemd219 sh[1558]: + '[' -f /tmp/success ']'

registros de depuración systemd 220

systemd220 systemd[1]: b.service: Trying to enqueue job b.service/start/replace
systemd220 systemd[1]: a.service: Installed new job a.service/start as 4846
systemd220 systemd[1]: b.service: Installed new job b.service/start as 4761
systemd220 systemd[1]: b.service: Enqueued job b.service/start as 4761
systemd220 systemd[1]: a.service: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 systemd[1]: a.service: Forked /bin/sh as 2032
systemd220 systemd[1]: a.service: Changed dead -> start-pre
systemd220 systemd[1]: Starting A...
systemd220 systemd[2032]: a.service: Executing: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 sh[2032]: + '[' -f /tmp/success ']'
systemd220 sh[2032]: + touch /tmp/success
systemd220 sh[2032]: + sleep 10
systemd220 systemd[1]: a.service: Start-pre operation timed out. Terminating.
systemd220 systemd[1]: a.service: Changed start-pre -> final-sigterm
systemd220 systemd[1]: a.service: Child 2032 belongs to a.service
systemd220 systemd[1]: a.service: Control process exited, code=killed status=15
systemd220 systemd[1]: a.service: Got final SIGCHLD for state final-sigterm.
systemd220 systemd[1]: a.service: Changed final-sigterm -> failed
systemd220 systemd[1]: a.service: Job a.service/start finished, result=failed
systemd220 systemd[1]: Failed to start A.
systemd220 systemd[1]: b.service: Job b.service/start finished, result=dependency
systemd220 systemd[1]: Dependency failed for B.
systemd220 systemd[1]: b.service: Job b.service/start failed with result 'dependency'.
systemd220 systemd[1]: a.service: Unit entered failed state.
systemd220 systemd[1]: a.service: Failed with result 'timeout'.
systemd220 systemd[1]: a.service: Changed failed -> auto-restart
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: Failed to send unit change signal for a.service: Transport endpoint is not connected
systemd220 systemd[1]: a.service: Service hold-off time over, scheduling restart.
systemd220 systemd[1]: a.service: Trying to enqueue job a.service/restart/fail
systemd220 systemd[1]: a.service: Installed new job a.service/restart as 5190
systemd220 systemd[1]: a.service: Enqueued job a.service/restart as 5190
systemd220 systemd[1]: a.service: Scheduled restart job.
systemd220 systemd[1]: a.service: Changed auto-restart -> dead
systemd220 systemd[1]: a.service: Job a.service/restart finished, result=done
systemd220 systemd[1]: a.service: Converting job a.service/restart -> a.service/start
systemd220 systemd[1]: a.service: About to execute: /bin/sh -x -c '[ -f /tmp/success ] || (touch /tmp/success && sleep 10)'
systemd220 systemd[1]: a.service: Forked /bin/sh as 2132
systemd220 systemd[1]: a.service: Changed dead -> start-pre
systemd220 systemd[1]: Starting A...
systemd220 systemd[1]: a.service: Child 2132 belongs to a.service
systemd220 systemd[1]: a.service: Control process exited, code=exited status=0
systemd220 systemd[1]: a.service: Got final SIGCHLD for state start-pre.
systemd220 systemd[1]: a.service: About to execute: /bin/true
systemd220 systemd[1]: a.service: Forked /bin/true as 2136
systemd220 systemd[1]: a.service: Changed start-pre -> running
systemd220 systemd[1]: a.service: Job a.service/start finished, result=done
systemd220 systemd[1]: Started A.
systemd220 systemd[1]: a.service: Child 2136 belongs to a.service
systemd220 systemd[1]: a.service: Main process exited, code=exited, status=0/SUCCESS
systemd220 systemd[1]: a.service: Changed running -> exited
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 systemd[1]: a.service: cgroup is empty
systemd220 sh[2132]: + '[' -f /tmp/success ']'
Vadim
fuente
1
Hay un problema del sistema upstream que sigue esto: github.com/systemd/systemd/issues/1312
JKnight

Respuestas:

31

Intentaré resumir mis hallazgos para este problema en caso de que alguien se encuentre con esto ya que la información sobre este tema es escasa.

  • Restart=on-failure solo se aplica a fallas de proceso (no se aplica a fallas debido a fallas de dependencia)
  • El hecho de que las unidades fallidas dependientes se reinicien bajo ciertas condiciones cuando una dependencia se reinicia con éxito fue un error en systemd <220: http://lists.freedesktop.org/archives/systemd-devel/2015-July/033513.html
  • Si incluso hay una pequeña posibilidad de que una dependencia pueda fallar al inicio y usted se preocupa por la capacidad de recuperación, no use Before/ Aftery en su lugar realice una verificación de algún artefacto que la dependencia produce

p.ej

ExecStartPre=/usr/bin/test -f /some/thing
Restart=on-failure
RestartSec=5s

Incluso podrías usarlo systemctl is-active <dependecy>.

Muy hacky, pero no he encontrado mejores opciones.

En mi opinión, no tener una manera de manejar las fallas de dependencia es una falla en systemd.

Vadim
fuente
Sí, por no mencionar que no se ha intentado de nuevo para los puntos de montaje que Leonard poetring no quiere implementar: github.com/systemd/systemd/issues/4468
Hvisage
0

Parece el tipo de cosa que podría ser programada y puesta en un cronjob con bastante facilidad. La lógica básica sería algo como esto

  1. verifique si tanto el servicio ayb como las dependencias se están ejecutando / en un estado válido. Conocerá la mejor manera de verificar si todo funciona correctamente
  2. Si todo funciona correctamente, no haga nada o registre que todo está funcionando. El registro tiene la ventaja de permitirle buscar la entrada de registro anterior.
  3. Si algo se rompe, reinicie los servicios y vuelva al comienzo del script donde se realiza la verificación del estado del servicio y la dependencia. El salto solo debe ocurrir si está seguro de que el reinicio de los servicios y las dependencias tendrán una alta probabilidad de funcionar; de lo contrario, existe la posibilidad de un bucle.
  4. Deje que cron ejecute el script nuevamente en un momento

Una vez que el script está configurado, cron es un buen lugar para probarlo, si cron es ineficiente, el script sería un buen punto de partida para intentar escribir un servicio de sistema de bajo nivel que pueda verificar el estado de algunos otros servicios y reiniciarlos según sea necesario. Dependiendo de la cantidad de esfuerzo que desee invertir, el script probablemente podría configurarse para enviarle un correo electrónico en función de los resultados (a menos, por supuesto, que los servicios en cuestión sean los servicios de red).

Mate
fuente
Este cronjob de cosas debería hacerse en el administrador de procesos / servicios, de lo contrario volverá a los métodos SVR4, que systemd intenta no hacer ...
Hvisage
0

Aftery Beforesolo establece el orden en que se iniciarán los servicios, sus archivos de servicio dicen "Si A y B se iniciarán, entonces A debe iniciarse antes que B".

Requires significa que si se va a iniciar este servicio, ese servicio debe iniciarse primero, en su ejemplo "Si B se inicia y A no se está ejecutando, inicie A"

Cuando agregas el WantedBy=multi-user.target, ahora le estás diciendo al sistema que los servicios deben iniciarse cuando el sistema se está inicializando multi-user.target, ¿presumiblemente esto significa que una vez que lo agregaste estabas dejando que el sistema iniciara los servicios en lugar de iniciarlos manualmente?

No estoy seguro de por qué esto no funciona en la versión 220, podría valer la pena probar 222. Excavaré una máquina virtual y probaré sus servicios cuando tenga la oportunidad.

Michael Shaw
fuente
1
Pregunté en systemd-devel, el hecho de que funcionara en 219 era un error. El comportamiento previsto es que las dependencias fallidas NO se reinician.
Vadim
0

Pasé días en esto, tratando de hacer que funcionara de la manera "systemd", pero me di por vencido y escribí un script de contenedor para administrar las dependencias y las fallas. Cada servicio secundario es un servicio systemd normal, sin "Requiere" o "PartOf" o ningún enlace a otros servicios.

Mi archivo de servicio de nivel superior se ve así:

[Service]
Type=simple
Environment=REQUIRES=foo.service bar.service
ExecStartPre=/usr/bin/systemctl start $REQUIRES
ExecStart=@PREFIX@/bin/top-service.sh $REQUIRES
ExecStop=/usr/bin/systemctl      stop $REQUIRES

Hasta aquí todo bien. El top.servicearchivo controla foo.servicey bar.service. El inicio topcomienza fooy bar, y la parada se topdetiene fooy bar. El ingrediente final es mi top-service.shscript que monitorea los servicios por falla:

#!/bin/bash

# This monitors REQUIRES services. If any service stops, all of the services are stopped and this script ends.

REQUIRES="$@"

if [ "$REQUIRES" == "" ]
then
  echo "ERROR: no services listed"
  exit 1
fi

echo "INFO: watching services: ${REQUIRES}"

end=0
while [[ $end == 0 ]]
do
  s=$(systemctl is-active ${REQUIRES} )
  if echo $s | egrep '^(active ?)+$' > /dev/null
  then
    # $s has embedded newlines, but echo $s seems to get rid of them, while echo "$s" keeps them.
    # echo INFO: All active, $s
    end=0
  else
    echo "WARN: ${REQUIRES}"
    echo WARN: $s
  fi

  if [[ $s == *"failed"* ]] || [[ $s == *"unknown"* ]]
  then
    echo "WARN: At least one service is failed or unknown, ending service"
    end=1
  else
    sleep 1
  fi
done

echo "INFO: done watching services, stopping: ${REQUIRES}"
systemctl stop ${REQUIRES}
echo "INFO: stopped: ${REQUIRES}"
exit 1
Mark Lakata
fuente
REQUIRES="$@"es un código con errores innatos: está contrayendo una matriz en una cadena, descartando los límites originales entre los elementos, por lo que el argumento creado por, es decir. set -- "argument one" "argument two"se vuelve idéntico a set -- "argument" "one" "argument" "two". requires=( "$@" )mantendría los datos originales, expandiéndose de forma segura como systemctl is-active "${requires[@]}".
Charles Duffy
-1

No respondo a esto. Pero alguien podría necesitar esto (porque como aparece esta página en la búsqueda):

debiera ser

[Service]
 Restart=always
 RestartSec=3

https://jonarcher.info/2015/08/ensure-systemd-services-restart-on-failure/

Shimon Doodkin
fuente
Por favor lea la pregunta más cuidadosamente. No se trata de reiniciar un solo servicio poco saludable, sino de cómo se comporta systemd cuando falla un servicio demandado.
Vadim