プロセスが停止した場合にプロセスを再起動するbashスクリプトを作成するにはどうすればよいですか?


226

キューをチェックして各アイテムに対してアクションを実行するPythonスクリプトがあります。

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

実行されているかどうかを確認し、実行されていない場合は起動するbashスクリプトを作成するにはどうすればよいですか。おおよそ次の疑似コード(またはおそらくそれは何かのようなことをする必要がありps | grepます):

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

run checkqueue.py
write processid to processidfile

これをcrontabから呼び出します。

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

4
これを2017年に追加するだけです。supervisordを使用します。crontabは、この種のタスクを実行することを意味するものではありません。実際のエラーを出力するのにbashスクリプトはひどいです。 stackoverflow.com/questions/9301494/...
mootmoot

他の非システムソリューションの代わりにinittabとrespawnを使用するのはどうですか?superuser.com/a/507835/116705を
Lars Nordin

回答:


635

PIDファイル、cron、その他の子ではないプロセスを評価しようとするものは避けてください。

UNIXで子供を待つことしかできないのには、十分な理由があります。回避しようとするメソッド(ps解析、pgrep、PIDの保存など)には欠陥があり、ギャップホールがあります。ノーと言うだけ

代わりに、プロセスを監視するプロセスがプロセスの親になる必要があります。これは何を意味するのでしょうか?つまり、プロセスを開始するプロセスだけが、プロセスの終了を確実に待つことができます。bashでは、これは絶対に取るに足らないことです。

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

上記のbashコードはループで実行さmyserveruntilます。最初の行が開始されmyserver、終了するまで待機します。終了すると、until終了ステータスをチェックします。終了ステータスがの場合、0正常に終了したことを意味します(つまり、何らかの方法でシャットダウンするように要求したところ、正常にシャットダウンしました)。その場合は、再起動したくありません(シャットダウンするように要求しただけです!)。終了状態である場合にはない 0untilstderrにエラーメッセージを発するとループ(ライン1に戻る)を再起動ループ本体、実行され、1秒後に

なぜ私たちは一秒待つのですか?起動シーケンスに問題があり、myserverすぐにクラッシュする場合は、常に再起動してクラッシュするという非常に集中的なループが発生するためです。はそれsleep 1から緊張を取り除きます。

これで、必要なのはこのbashスクリプトを(おそらく非同期で)開始するだけでありmyserver、必要に応じて監視および再起動します。起動時にモニターを起動する(サーバーを「再起動」させる)場合は、@rebootルールを使用してユーザーのcron(1)でモニターをスケジュールできます。cronルールを開きますcrontab

crontab -e

次に、監視スクリプトを開始するルールを追加します。

@reboot /usr/local/bin/myservermonitor

あるいは; inittab(5)と/ etc / inittabを見てください。そこに行を追加myserverして、特定の初期化レベルで開始し、自動的にリスポーンさせることができます。


編集。

PIDファイルを使用しない理由についていくつか情報を追加しましょう。彼らは非常に人気がありますが、また、これらには非常に欠陥があり、正しい方法で実行しない理由はありません。

このことを考慮:

  1. PIDリサイクル(間違ったプロセスを強制終了):

    • /etc/init.d/foo start:開始してfoo、にfooPIDを書き込みます/var/run/foo.pid
    • しばらくして、fooどういうわけか死ぬ。
    • しばらくして:開始する(それを呼び出すbar)ランダムプロセスはランダムなPIDを受け取りfooます。古いPID を受け取ることを想像してください。
    • あなたはfooなくなっていることに気づきます:/etc/init.d/foo/restart読み取り/var/run/foo.pid、それがまだ生きているかどうかを確認し、見つけbar、それを考え、それfooを殺し、新しいものを開始しfooます。
  2. PIDファイルが古くなります。PIDファイルが古くなっているかどうかを確認するには、過度に複雑な(つまり、自明ではない)ロジックが必要であり、そのようなロジックはに対して脆弱1.です。

  3. 書き込みアクセス権がない場合や、読み取り専用環境にいる場合はどうなりますか?

  4. それは無意味な複雑化です。上記の私の例がいかに単純かを見てください。それを複雑にする必要はまったくありません。

参照:PIDファイルは、「正しく」実行した場合でも欠陥がありますか?

ところで; PIDファイルよりもさらに悪いのは解析psです! これを行わないでください。

  1. ps非常に移植性がありません。ほとんどすべてのUNIXシステムで見つかりますが、非標準出力が必要な場合、その引数は大きく異なります。また、標準出力は人間が使用するためのものであり、スクリプトによる解析のためのものではありません。
  2. 解析psすると、多くの誤検知が発生します。テイクps aux | grep PID例を、そして今、あなたとあなたのデーモンを見つめていたPIDと同じであることを起こる引数として数値どこかでプロセスを開始する人を想像してみてください。2人のユーザーがXセッションを開始し、Xを求めて自分のセッションを殺すことを想像してください。それはすべての種類の悪いです。

プロセスを自分で管理したくない場合。プロセスのモニターとして機能する完全に優れたシステムがいくつかあります。たとえばrunit調べます。


1
@Chas。所有:それは必要ではないと思います。正当な理由もなく、実装が複雑になるだけです。シンプルさは常により重要です。頻繁に再起動する場合は、スリープによってシステムリソースに悪影響が及ばないようにします。とにかくすでにメッセージがあります。
lhunath 2009年

2
@orschiroプログラムが動作するときにリソースが消費されることはありません。起動直後に継続して存在する場合、継続的にスリープ1でのリソース消費はまったく無視できます。
lhunath 2013年

7
私はこの答えを見ているだけだと信じることができます。本当にありがとう!
getWeberForStackExchange 2013

2
@TomášZatoでは、プロセスの終了コードをテストせずに上記のループを実行できますが、プロセスwhile true; do myprocess; doneを停止する方法はありません。
lhunath 2014年

2
@ SergeyP.akaazure bashの出口で親に子を強制的に殺す唯一の方法は、子を仕事に変えてそれを知らせることです:trap 'kill $(jobs -p)' EXIT; until myserver & wait; do sleep 1; done
lhunath

33

monitを見てくださいhttp://mmonit.com/monit/)。スクリプトの開始、停止、再起動を処理し、必要に応じてヘルスチェックと再起動を実行できます。

または、簡単なスクリプトを実行します。

while true
do
/your/script
sleep 1
done

4
Monitはまさにあなたが探しているものです。
Sarke、2015

4
「while 1」は機能しません。「while [1]」または「while true」または「while:」が必要です。unix.stackexchange.com/questions/367108/what-does-while-mean
Curtis Yallop

8

それを行う最も簡単な方法は、ファイルにflockを使用することです。あなたはPythonスクリプトで

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()

シェルでは、実行中かどうかを実際にテストできます。

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

ただし、もちろん、テストする必要はありません。すでに実行されていて再起動した場合は、 'other instance already running'

プロセスが終了すると、そのすべてのファイル記述子が閉じられ、すべてのロックが自動的に削除されます。


これは、おそらくbashスクリプトを削除することで、少し単純化することができます。Pythonスクリプトがクラッシュするとどうなりますか?ファイルはロック解除されていますか?
トム

1
ファイルロックは、アプリケーションが停止するとすぐに、強制終了、自然、またはクラッシュのいずれかによって解放されます。
クリスチャンウィッツ

@Tom ...もう少し正確に言えば、ロックがかかっているファイルハンドルが閉じるとすぐにロックはアクティブではなくなります。Pythonスクリプトが意図的にファイルハンドルを決して閉じず、ガベージコレクションされているファイルオブジェクトを介して自動的に閉じられない場合は、おそらくスクリプトが終了したか、強制終了されたことを意味します。これは再起動などでも機能します。
Charles Duffy

1
使用する方法ははるかに優れていますflock。実際、manページにはその方法が明示的に示されています。exec {lock_fd}>/tmp/script.lock; flock -x "$lock_fd"Pythonと同等のbashであり、ロックを保持したままにします(そのため、プロセスを実行すると、そのプロセスが終了するまでロックは保持されたままになります)。
Charles Duffy、

あなたのコードが間違っているので私はあなたに反対票を投じました。使用flockは正しい方法ですが、スクリプトが間違っています。あなたはcrontabファイルで設定する必要がある唯一のコマンドは次のとおりです。flock -n /tmp/script.lock -c '/path/to/my/script.py'
Rutrus

6

システム上のさまざまなものを監視し、それに応じて対応できる標準のUNIXツールであるmonitを使用する必要があります。

ドキュメントから:http : //mmonit.com/monit/documentation/monit.html#pid_testing

pidfile /var/run/checkqueue.pidでプロセスcheckqueue.pyを確認します
       pidを変更した場合は、「checkqueue_restart.sh」を実行します

再起動したときにメールで通知するようにmonitを設定することもできます。


2
Monitは優れたツールですが、POSIXまたはSUSVで指定されているという正式な意味での標準ではありません
Charles Duffy

5
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi

クール、それは私の擬似コードのいくつかをかなりうまく肉付けしている。2つのqns:1)PIDFILEをどのように生成しますか?2)psgrepとは何ですか?Ubuntuサーバーにはありません。
トム

ps grepはと同じことを行う小さなアプリですps ax|grep ...。インストールするか、そのための関数を作成することができます:function psgrep(){ps ax | grep -v grep | grep -q "$ 1"}
soulmerge 2009年

私があなたの最初の質問に答えていなかったことに気づきました。
soulmerge 2009年

7
本当に忙しいサーバーでは、チェックする前にPIDがリサイクルされる可能性があります。
vartec 2009年

2

オペレーティングシステム間での移植性はわかりませんが、システムに「run-one」コマンド、つまり「man run-one」が含まれているかどうかを確認できます。具体的には、このコマンドセットには「run-one-constantly」が含まれており、これはまさに必要なもののようです。

manページから:

run-one-constantly COMMAND [ARGS]

注:明らかにこれはスクリプト内から呼び出すことができますが、スクリプトを作成する必要がまったくなくなります。


これは受け入れられた答えよりも優れていますか?
tripleee 2018年

1
はい、システムコードベースの一部として維持する必要があるのと同じことを行うシェルスクリプトを作成するよりも、組み込みコマンドを使用する方が望ましいと思います。機能がシェルスクリプトの一部として必要な場合でも、上記のコマンドを使用してシェルスクリプトの質問に関連するようにすることもできます。
Daniel Bradley、

これは「組み込み」ではありません。一部のディストリビューションにデフォルトでインストールされている場合は、おそらくディストリビューションを指定する必要があります(理想的には、ディストリビューションが含まれていない場合は、ダウンロード先のポインターを含める必要があります)。
tripleee 2018年

Ubuntuのユーティリティのようです。Ubuntuでもオプションです。manpages.ubuntu.com/manpages/bionic/man1/run-one.1.html
tripleee 2018年

注目に値する:run-oneユーティリティは、その名前が示すとおりに機能します。run-one-nnnnnで実行されるコマンドのインスタンスは1つしか実行できません。ここでの他の答えは、より実行可能な不可知論です-コマンドの内容をまったく気にしません。
David Kohen

1

次のスクリプトを使用して、多数のサーバーで大きな成功を収めています。

pid=`jps -v | grep $INSTALLATION | awk '{print $1}'`
echo $INSTALLATION found at PID $pid 
while [ -e /proc/$pid ]; do sleep 0.1; done

ノート:

  • Javaプロセスを探しているので、jpsを使用できます。これは、psよりもディストリビューション全体ではるかに一貫しています。
  • $INSTALLATION 完全に明確なプロセスパスが十分に含まれている
  • プロセスが終了するのを待っている間はスリープを使用し、リソースの浪費を避けます:)

このスクリプトは実際には、コマンドラインでシャットダウン(および待機)したいtomcatの実行中のインスタンスをシャットダウンするために使用されるため、子プロセスとして起動することは、私にとっては選択肢になりません。


1
grep | awkまだアンチパターンです - awk "/$INSTALLATION/ { print \$1 }"役に立たないものgrepをAwkスクリプトに統合したいのですが、正規表現自体で行を見つけることができます。どうもありがとうございました。
tripleee 2015年

0

これをnpmプロセスに使用します

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.