SSH セッションが終了すると、Python バックグラウンド プロセスが終了するのはなぜですか?

SSH セッションが終了すると、Python バックグラウンド プロセスが終了するのはなぜですか?

startup.shキー行を含むpython3 スクリプト ( と呼びます) を起動する bash スクリプトがあります。

nohup python3 -u <script> &

直接ログインしてこのスクリプトを呼び出すとssh、Python スクリプトは終了後もバックグラウンドで実行され続けます。ただし、これを実行すると:

ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"

プロセスはssh実行が終了し、セッションが閉じられるとすぐに終了します。

この2つの違いは何でしょうか?

編集: Python スクリプトは Bottle 経由で Web サービスを実行しています。

編集2:私も試してみました初期化スクリプトの作成を呼び出しstartup.shて実行しましたssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>"が、同じ動作になりました。

編集3: おそらくスクリプト内の別の部分でしょう。スクリプトの大部分は次のとおりです:

chmod 700 ${key_loc}

echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}

echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"

編集4: 最後の行を最後にスリープさせて実行すると:

ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"

echo "Finished"

決して到達せずecho "Finished"、これまで見たことのないボトル サーバー メッセージが表示されます。

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.

手動で SSH 接続してプロセスを強制終了すると、「完了」と表示されます。

EDIT5: EDIT4 を使用して、任意のエンドポイントにリクエストを送信すると、ページが返されますが、ボトルでエラーが発生します。

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.


----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)

答え1

コマンドを標準の入力/出力およびエラー フローから切断します。

nohup python3 -u <script> </dev/null >/dev/null 2>&1 &  

sshこれ以上の出力がなく、これ以上の入力を必要としないインジケータが必要です。入力を別のものにして出力をリダイレクトすると、入出力がssh端末から来たり端末に行ったりしないので、安全に終了できます。つまり、入力はどこか別の場所から来る必要があり、出力 (STDOUT と STDERR の両方) はどこか別の場所に行く必要があります。

部分はの入力として</dev/null指定します。これがここで役立つ理由は次のとおりです。/dev/null<script>

/dev/null を stdin にリダイレクトすると、そのプロセスからのすべての読み取り呼び出しに即時 EOF が与えられます。これは通常、プロセスを tty から切り離すのに役立ちます (このようなプロセスはデーモンと呼ばれます)。たとえば、ssh 経由でリモートでバックグラウンド プロセスを開始する場合、プロセスがローカル入力を待機しないように stdin をリダイレクトする必要があります。 https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475

sshあるいは、現在のセッションを開いたままにする必要がない限り、別の入力ソースからのリダイレクトは比較的安全です。

部分では、>/dev/nullシェルは標準出力を /dev/null にリダイレクトし、基本的にそれを破棄します。>/path/to/fileも機能します。

最後の部分は2>&1、STDERR を STDOUT にリダイレクトすることです。

プログラムには、標準的な入力と出力のソースが 3 つあります。対話型プログラムの場合は、標準入力は通常キーボードから、他のプログラムの出力を処理している場合は別のプログラムから取得されます。プログラムは通常、標準出力に出力しますが、標準エラーに出力することもあります。これらの 3 つのファイル記述子 (「データ パイプ」と考えることができます) は、STDIN、STDOUT、STDERR と呼ばれることがよくあります。

名前が付けられず、番号が付けられることもあります。組み込みの番号は、0、1、2 の順です。デフォルトでは、明示的に名前または番号 1 を指定しない場合は、STDOUT について話していることになります。

このコンテキストから、上記のコマンドは標準出力を /dev/null にリダイレクトしていることがわかります。これは、不要なものをダンプできる場所 (ビット バケットと呼ばれることが多い) であり、次に標準エラーを標準出力にリダイレクトします (これを行うときは、宛先の前に & を付ける必要があります)。

したがって、簡単に説明すると、「このコマンドからのすべての出力はブラックホールに押し込まれる必要があります」となります。これは、プログラムを本当に静かにするための 1 つの良い方法です。
> /dev/null 2>&1 はどういう意味ですか? | Xaprb

答え2

見るman ssh

 ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
     [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport]
     [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
     [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
     [user@]hostname [command]

実行すると、ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"シェル スクリプト startup.sh が ssh コマンドとして実行されます。

説明より:

コマンドが指定されている場合は、ログイン シェルではなくリモート ホスト上で実行されます。

これに基づいて、スクリプトをリモートで実行する必要があります。

ローカル ターミナルで実行する場合との違いは、nohup python3 -u <script> &これはローカル バックグラウンド プロセスとして実行されるのに対し、ssh コマンドはこれをリモート バックグラウンド プロセスとして実行しようとすることです。

スクリプトをローカルで実行する場合は、sshコマンドの一部としてstartup.shを実行しないでください。次のようなコマンドを試してください。ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

スクリプトをリモートで実行し、SSH セッションが終了した後もこのプロセスを続行したい場合は、まずscreenリモート ホストでセッションを開始する必要があります。次に、画面内で Python スクリプトを実行する必要があります。これにより、SSH セッションを終了した後もスクリプトが引き続き実行されます。

見るスクリーン ユーザーズマニュアル

screen が最良の選択肢だと思いますが、nohup を使用する必要がある場合は、nohup コマンドを実行する前にリモート ホストでの設定を検討してください。または、を使ってプロセスをマークし、そのプロセスに SIGHUP が送信されないようにすることshopt -s huponexitもできます。disown -h [jobID]1

バックグラウンドでシェルプロンプトを終了した後もジョブを実行し続けるにはどうすればよいですか?

SIGHUP (ハングアップ) シグナルは、制御端末または制御プロセスの終了時にシステムによって使用されます。SIGHUP を使用して、構成ファイルを再ロードしたり、ログ ファイルを開いたり閉じたりすることもできます。つまり、端末からログアウトすると、実行中のジョブはすべて終了します。これを回避するには、disown コマンドに -h オプションを渡します。このオプションは各ジョブ ID をマークし、シェルが SIGHUP を受信して​​も SIGHUP がジョブに送信されないようにします。

また、huponexitシェルが終了、強制終了、またはドロップされたときにどのように動作するかについては、この概要を参照してください。現在の問題は、シェル セッションの終了方法に関連していると思われます。2

  1. ssh 接続を介して開かれたシェルのすべての子プロセス (バックグラウンドであるかどうかに関係なく) は、huponexit オプションが設定されている場合にのみ、ssh 接続が閉じられるときに SIGHUP で強制終了されます。これが当てはまるかどうかを確認するには、shopt huponexit を実行します。

  2. huponexit が true の場合、終了時にプロセスが強制終了されないように、nohup または disown を使用してプロセスをシェルから切り離すことができます。または、screen を使用して実行します。

  3. huponexit が false の場合 (最近の一部の Linux ではこれがデフォルトです)、バックグラウンド ジョブは通常のログアウトでは終了されません。

  4. しかし、huponexit が false であっても、ssh 接続が強制終了されるか、切断されると (通常のログアウトとは異なります)、バックグラウンド プロセスは強制終了されます。これは、(2) のように disown または nohup によって回避できます。

最後に、shopt huponexit の使用方法の例をいくつか示します。3

$ shopt -s huponexit; shopt | grep huponexit
huponexit       on
# Background jobs will be terminated with SIGHUP when shell exits

$ shopt -u huponexit; shopt | grep huponexit
huponexit       off
# Background jobs will NOT be terminated with SIGHUP when shell exits

答え3

-nを起動するときに、オプションを試してみる価値があるかもしれません 。このオプションは、終了するとすぐに閉じられるsshローカル へのリモート プロセスの依存関係を防ぎます。また、リモート プロセスがその にアクセスしようとするたびに、その が終了する原因になります。stdinssh sessionstdin

答え4

pythonこれは、スクリプトまたはスクリプト自体の動作に問題があるように思われますpython。実際に行うことはnohup(リダイレクトを簡素化する以外)、プログラムを実行する前にシグナルのハンドラーを(無視)HUPに設定することだけです。プログラムが実行を開始したら、それを元に戻したり、独自のハンドラーをインストールしたりすることをSIG_IGN妨げるものは何もありません。SIG_DFL

試してみるとよいことの 1 つは、コマンドを括弧で囲むことです。これにより、二重フォーク効果が得られ、スクリプトpythonがシェル プロセスの子ではなくなります。例:

( nohup python3 -u <script> & )

試してみる価値があるかもしれないもう 1 つの方法は (bash別のシェルではなく を使用している場合)、disownの代わりに組み込みを使用することですnohup。すべてがドキュメントどおりに動作している場合は、実際に違いは生じませんが、対話型シェルでは、これによりシグナルがスクリプトHUPに伝播されなくなります。次の行または以下のように同じ行に disown を追加できます (の後に を追加すると でエラーになることにpython注意してください)。;&bash

python3 -u <script> </dev/null &>/dev/null & disown

上記、またはそれらの組み合わせが機能しない場合は、問題に対処できる唯一の場所はpythonスクリプト自体です。

関連情報