318 Stimmen

Wie schreibe ich ein Bash-Skript, um einen Prozess neu zu starten, wenn er stirbt?

Ich habe ein Python-Skript, das eine Warteschlange überprüft und eine Aktion auf jedem Element durchführen wird:

# checkqueue.py
while True:
  check_queue()
  do_something()

Wie schreibe ich ein Bash-Skript, das überprüft, ob es läuft, und wenn nicht, es startet. Ungefähr der folgende Pseudocode (oder vielleicht sollte es etwas tun wie ps | grep ?):

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

Ich rufe das über eine crontab auf:

# crontab
*/5 * * * * /path/to/keepalivescript.sh

774voto

lhunath Punkte 111281

Vermeiden Sie PID-Dateien, Crons oder alles andere, was versucht, Prozesse zu bewerten, die nicht ihre Kinder sind.

Es gibt einen sehr guten Grund, warum man in UNIX NUR auf seine Kinder warten kann. Jede Methode (ps parsing, pgrep, Speichern einer PID, ...), die versucht, dies zu umgehen, ist fehlerhaft und hat klaffende Löcher. Sagen Sie einfach no .

Stattdessen müssen Sie den Prozess, der Ihren Prozess überwacht, als Elternteil des Prozesses festlegen. Was bedeutet das? Es bedeutet, dass nur der Prozess, der startet Ihr Prozess kann zuverlässig auf sein Ende warten. In der Bash ist dies absolut trivial.

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

Das obige Stück Bash-Code läuft myserver in einem until Schleife. Die erste Zeile beginnt myserver und wartet darauf, dass es zu Ende geht. Wenn es endet, until prüft seinen Exit-Status. Wenn der Exit-Status 0 bedeutet dies, dass das Programm ordnungsgemäß beendet wurde (was bedeutet, dass Sie es gebeten haben, sich irgendwie herunterzufahren, und dass es dies erfolgreich getan hat). In diesem Fall wollen wir ihn nicht neu starten (wir haben ihn ja gerade gebeten, sich zu beenden!). Wenn der Exit-Status no 0 , until führt den Schleifenkörper aus, der eine Fehlermeldung auf STDERR ausgibt und die Schleife neu startet (zurück zu Zeile 1) nach 1 Sekunde .

Warum warten wir eine Sekunde? Weil, wenn etwas mit der Startsequenz von myserver und es stürzt sofort ab, dann haben Sie eine sehr intensive Schleife von ständigen Neustarts und Abstürzen vor sich. Die sleep 1 nimmt dem Ganzen den Schrecken.

Jetzt brauchen Sie nur noch dieses Bash-Skript zu starten (wahrscheinlich asynchron), und es wird Folgendes überwachen myserver und starten Sie ihn bei Bedarf neu. Wenn Sie den Monitor beim Booten starten wollen (damit der Server einen Neustart "überlebt"), können Sie ihn im cron(1) Ihres Benutzers mit einem @reboot Regel. Öffnen Sie Ihre Cron-Regeln mit crontab :

crontab -e

Fügen Sie dann eine Regel hinzu, um Ihr Monitor-Skript zu starten:

@reboot /usr/local/bin/myservermonitor

Alternativ können Sie auch in inittab(5) und /etc/inittab nachsehen. Sie können dort eine Zeile einfügen, in der myserver auf einem bestimmten Init-Level starten und automatisch respawned werden.


Bearbeiten.

Lassen Sie mich einige Informationen hinzufügen, warum no um PID-Dateien zu verwenden. Sie sind zwar sehr beliebt, aber auch sehr fehlerhaft, und es gibt keinen Grund, warum man es nicht einfach auf die richtige Art und Weise machen sollte.

Bedenken Sie dies:

  1. PID-Recycling (Töten des falschen Prozesses):

    • /etc/init.d/foo start : Start foo schreiben foo PID auf /var/run/foo.pid
    • Kurze Zeit später: foo irgendwie stirbt.
    • Einige Zeit später: Jeder zufällige Prozess, der beginnt (nennen wir ihn bar ) eine zufällige PID nimmt, stellen Sie sich vor, sie nimmt foo die alte PID.
    • Sie bemerken foo ist weg: /etc/init.d/foo/restart liest /var/run/foo.pid prüft, ob es noch lebt, und findet bar denkt, es ist foo tötet sie, startet eine neue foo .
  2. PID-Dateien werden veraltet. Man braucht eine überkomplizierte (oder sollte ich sagen, nicht-triviale) Logik, um zu prüfen, ob die PID-Datei veraltet ist, und jede solche Logik ist wiederum anfällig für 1. .

  3. Was ist, wenn Sie nicht einmal Schreibzugriff haben oder sich in einer Nur-Lese-Umgebung befinden?

  4. Das ist eine sinnlose Überkomplizierung; sehen Sie, wie einfach mein obiges Beispiel ist. Es ist überhaupt nicht nötig, das zu komplizieren.

Siehe auch: Sind PID-Dateien immer noch fehlerhaft, wenn man es "richtig" macht?

Im Übrigen; Noch schlimmer als PID-Dateien ist das Parsen ps ! Tun Sie das niemals.

  1. ps ist sehr unhandlich. Man findet es zwar auf fast jedem UNIX-System, aber seine Argumente variieren stark, wenn man eine nicht standardisierte Ausgabe wünscht. Und die Standardausgabe ist NUR für den menschlichen Konsum gedacht, nicht für das Parsen durch Skripte!
  2. Parsing ps führt zu einer Fülle von Fehlalarmen. Nehmen Sie die ps aux | grep PID Beispiel, und jetzt stellen Sie sich vor, jemand startet einen Prozess mit einer Zahl irgendwo als Argument, die zufällig die gleiche ist wie die PID, mit der Sie Ihren Daemon gestartet haben! Stellen Sie sich vor, zwei Leute starten eine X-Sitzung und Sie greifen nach X, um Ihre zu beenden. Das ist einfach alles sehr schlimm.

Wenn Sie den Prozess nicht selbst verwalten wollen, gibt es einige sehr gute Systeme, die Ihre Prozesse überwachen können. Schauen Sie sich runit zum Beispiel.

54voto

Bernd Punkte 3310

Werfen Sie einen Blick auf monit ( http://mmonit.com/monit/ ). Es übernimmt das Starten, Stoppen und Neustarten Ihres Skripts und kann bei Bedarf Zustandsprüfungen und Neustarts durchführen.

Oder machen Sie ein einfaches Skript:

while true
do
/your/script
sleep 1
done

35voto

Benyamin Jafari Punkte 20585

In-line:

while true; do <your-bash-snippet> && break; done

z.B. #1

while true; do openconnect x.x.x.x:xxxx && break; done

z.B. #2

while true; do docker logs -f container-name; sleep 2; done

11voto

vartec Punkte 124396

Am einfachsten ist es, den Flock in der Datei zu verwenden. In einem Python-Skript würden Sie Folgendes tun

lf = open('/tmp/script.lock','w')
if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): 
   sys.exit('other instance already running')
lf.write('%d\n'%os.getpid())
lf.flush()

In der Shell können Sie tatsächlich testen, ob es läuft:

if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then 
   echo 'it's not running'
   restart.
else
   echo -n 'it's already running with PID '
   cat /tmp/script.lock
fi

Aber natürlich müssen Sie das nicht testen, denn wenn es bereits läuft und Sie es neu starten, wird es sich mit 'other instance already running'

Wenn ein Prozess stirbt, werden alle seine Dateideskriptoren geschlossen und alle Sperren automatisch entfernt.

7voto

Tom Punkte 4485
watch "yourcommand"

Er startet den Prozess neu, wenn er anhält (nach einer Verzögerung von 2 Sekunden).

watch -n 0.1 "yourcommand"

Zum Neustart nach 0,1 Sekunden statt der standardmäßigen 2 Sekunden

watch -e "yourcommand"

Um Neustarts zu stoppen, wenn das Programm mit einem Fehler beendet wird.

Vorteile:

  • eingebauter Befehl
  • eine Zeile
  • einfach zu benutzen und zu merken.

Nachteilig:

  • Zeigen Sie das Ergebnis des Befehls erst auf dem Bildschirm an, wenn er beendet ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X