web-dev-qa-db-ja.com

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

キューをチェックし、各アイテムに対してアクションを実行する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
204
Tom

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

UNIXでは、子供だけを待つことができる非常に良い理由があります。これを回避しようとするメソッド(ps解析、pgrep、PIDの保存など)には欠陥があり、それに穴が開いています。 noとだけ言ってください。

代わりに、プロセスを監視するプロセスがプロセスの親になる必要があります。これは何を意味するのでしょうか?プロセスが終了するのを確実に待つことができるstartsプロセスのみを意味します。 bashでは、これは非常に簡単です。

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

上記のbashコードは、myserverループでuntilを実行します。最初の行はmyserverで始まり、終了するのを待ちます。終了すると、untilは終了ステータスを確認します。終了ステータスが0の場合、正常に終了したことを意味します(つまり、何らかの方法でシャットダウンするように要求し、正常に終了したことを意味します)。その場合、再起動する必要はありません(シャットダウンするように要求しただけです!)。終了ステータスがnot0の場合、untilはループ本体を実行し、STDERRでエラーメッセージを発行し、ループを再開します(1行目に戻る)1秒後

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

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

crontab -e

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

@reboot /usr/local/bin/myservermonitor

または、 inittab(5)と/ etc/inittabを見てください。そこに行を追加して、myserverを特定の初期化レベルで開始し、自動的に再生成することができます。


編集。

PIDファイルを使用する理由notに関する情報を追加します。彼らは非常に人気がありますが;彼らはまた非常に欠陥があり、あなたがそれを正しい方法だけでしない理由はありません。

このことを考慮:

  1. PIDリサイクル(間違ったプロセスを殺す):

    • /etc/init.d/foo startfooを開始し、fooのPIDを/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 を調べます。

578
lhunath

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

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

while true
do
/your/script
sleep 1
done
23
Bernd

最も簡単な方法は、ファイルで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()

Shellでは、実際に実行されているかどうかをテストできます。

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'で終了するからです。

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

8
vartec

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

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

プロセスcheckqueue.pyをpidfile /var/run/checkqueue.pid
でチェックし、pidが変更された場合、「checkqueue_restart.sh」
を実行します。

再起動を行うときにメールを送信するようにmonitを構成することもできます。

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

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

Manページから:

run-one-constantlyコマンド[ARGS]

注:当然、これはスクリプト内から呼び出すことができますが、スクリプトを使用する必要もなくなります。

2
Daniel Bradley

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

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
Kevin Wright