Neustart des systemd-Dienstes bei einem Abhängigkeitsfehler


26

Was ist der richtige Ansatz, um den Neustart eines Dienstes zu handhaben, wenn eine seiner Abhängigkeiten beim Start fehlschlägt (dies gelingt jedoch nach einem erneuten Versuch)?

Hier ist ein erfundener Repro, um das Problem klarer zu machen.

a.service (simuliert Fehler beim ersten Versuch und Erfolg beim zweiten Versuch)

[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.service (gelingt trivial nach A-Starts)

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

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

Fangen wir an b:

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

Protokolle:

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 wurde erfolgreich gestartet, B bleibt jedoch in einem fehlerhaften Zustand und versucht es nicht erneut.

BEARBEITEN

Ich habe beiden Diensten Folgendes hinzugefügt und jetzt wird B erfolgreich gestartet, wenn A gestartet wird, aber ich kann nicht erklären, warum.

[Install]
WantedBy=multi-user.target

Warum würde dies die Beziehung zwischen A und B beeinflussen?

EDIT2

Die obige Option "Fix" funktioniert in systemd 220 nicht.

Systemd 219 Debug-Protokolle

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 ']'

Systemd 220 Debug-Protokolle

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 ']'

1
Es gibt ein vorgelagertes Problem mit dem
System

Antworten:


31

Ich werde versuchen, meine Erkenntnisse für dieses Problem zusammenzufassen, falls jemand darauf stößt, da Informationen zu diesem Thema knapp sind.

  • Restart=on-failure Gilt nur für Prozessfehler (gilt nicht für Fehler aufgrund von Abhängigkeitsfehlern)
  • Die Tatsache, dass abhängige fehlerhafte Einheiten unter bestimmten Bedingungen neu gestartet werden, wenn eine Abhängigkeit erfolgreich neu gestartet wurde, war ein Fehler in systemd <220: http://lists.freedesktop.org/archives/systemd-devel/2015-July/033513.html
  • Wenn es nur eine geringe Wahrscheinlichkeit gibt, dass eine Abhängigkeit beim Start fehlschlägt und Sie sich für die Ausfallsicherheit interessieren, verwenden Sie Before/ nicht Afterund führen Sie stattdessen eine Überprüfung auf ein Artefakt durch, das die Abhängigkeit erzeugt

z.B

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

Sie könnten sogar verwenden systemctl is-active <dependecy>.

Sehr abgedreht, aber ich habe keine besseren Optionen gefunden.

Meiner Meinung nach ist es ein Fehler in systemd, keine Möglichkeit zu haben, mit Abhängigkeitsfehlern umzugehen.


Ja, ganz zu schweigen davon, dass es keinen erneuten Versuch für Mount Points gibt, den Leonard Poetring nicht implementieren möchte: github.com/systemd/systemd/issues/4468
Hvisage,

0

Es scheint so, als könnte man ziemlich leicht ein Skript schreiben und einen Cronjob machen. Die Grundlogik würde ungefähr so ​​lauten

  1. Überprüfen Sie, ob sowohl Service a und b als auch Abhängigkeiten ausgeführt werden / in einem gültigen Zustand sind. Sie werden den besten Weg kennen, um zu überprüfen, ob alles richtig funktioniert
  2. Wenn alles richtig funktioniert, nichts tun oder protokollieren, dass alles funktioniert. Die Protokollierung bietet den Vorteil, dass Sie nach dem vorherigen Protokolleintrag suchen können.
  3. Wenn etwas kaputt geht, starten Sie die Dienste neu und springen Sie zum Anfang des Skripts zurück, an dem die Prüfung des Dienst- und Abhängigkeitsstatus erfolgt. Ein Sprung sollte nur erfolgen, wenn Sie sicher sind, dass die Dienste neu gestartet werden und Abhängigkeiten mit hoher Wahrscheinlichkeit funktionieren. Andernfalls besteht die Gefahr einer Schleife.
  4. Lassen Sie cron das Skript in Kürze erneut ausführen

Sobald das Skript festgelegt ist, ist cron ein guter Ort, um es zu testen. Wenn cron ineffizient ist, ist das Skript ein guter Ausgangspunkt für den Versuch, einen Systemdienst auf niedriger Ebene zu schreiben, der den Status einiger anderer Dienste überprüfen und bei Bedarf neu starten kann. Je nach Aufwand, den Sie investieren möchten, kann das Skript möglicherweise sogar so eingerichtet werden, dass Sie anhand der Ergebnisse eine E-Mail erhalten (es sei denn, die in Frage kommenden Dienste sind die Netzwerkdienste).


Dieser Cronjob von Sachen sollte eher im Prozess / Service-Manager gemacht werden, sonst werden Sie auf SVR4-Methoden zurückgreifen, die systemd versucht, nicht zu tun ...
Hvisage

0

Afterund Beforelegen Sie nur die Reihenfolge fest, in der Dienste gestartet werden. In den Dienstdateien wird angegeben, dass "Wenn A und B gestartet werden, muss A vor B gestartet werden".

Requires bedeutet, dass, wenn dieser Dienst gestartet werden soll, dieser Dienst zuerst gestartet werden muss, in Ihrem Beispiel "Wenn B gestartet ist und A nicht ausgeführt wird, starten Sie A".

Wenn Sie hinzufügen WantedBy=multi-user.target, teilen Sie dem System jetzt mit, dass die Dienste beim Initialisieren des Systems gestartet werden müssen. Bedeutet dies multi-user.targetwahrscheinlich, dass Sie nach dem Hinzufügen die Dienste vom System starten ließen, anstatt sie manuell zu starten?

Ich bin mir nicht sicher, warum dies in Version 220 nicht funktioniert. Es könnte sich lohnen, 222 auszuprobieren. Ich werde eine VM ausgraben und Ihre Dienste testen, wenn ich die Gelegenheit dazu bekomme.


1
Ich habe auf systemd-devel nachgefragt, die Tatsache, dass es 219 funktionierte, war ein Fehler. Das beabsichtigte Verhalten ist, dass fehlgeschlagene Abhängigkeiten NICHT neu gestartet werden.
Vadim

0

Ich habe Tage damit verbracht, zu versuchen, es auf die "systemd" Weise zum Laufen zu bringen, habe aber frustriert aufgegeben und ein Wrapper-Skript geschrieben, um Abhängigkeiten und Fehler zu verwalten. Jeder untergeordnete Dienst ist ein normaler Systemdienst ohne "Requires" oder "PartOf" oder Hooks zu anderen Diensten.

Meine Top-Level-Servicedatei sieht folgendermaßen aus:

[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

So weit, ist es gut. Die top.serviceDatei steuert foo.serviceund bar.service. Das Starten topstartet foound barund das Stoppen topstoppt foound bar. Die letzte Zutat ist mein top-service.shSkript, das die Dienste auf Fehler überwacht:

#!/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

REQUIRES="$@"ist von Natur aus fehlerhafter Code - Sie reduzieren ein Array in eine Zeichenfolge und verwerfen die ursprünglichen Grenzen zwischen Elementen, also das Argument, das von, dh. set -- "argument one" "argument two"wird identisch mit set -- "argument" "one" "argument" "two". requires=( "$@" )würde die ursprünglichen Daten behalten und wäre somit sicher erweiterbar als systemctl is-active "${requires[@]}".
Charles Duffy

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.