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

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

startup.shコアラインが次のようなpython3スクリプト(と呼ぶ)を起動するbashスクリプトがあります。

nohup python3 -u <script> &

直接入力してこのスクリプトを呼び出すと、sshPythonスクリプトは終了後もバックグラウンドで実行され続けます。しかし、これを実行すると、次のようになります。

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

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

2つの違いは何ですか?

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

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

EDIT3: たぶんスクリプトの他のものかもしれません。以下はスクリプトの大部分です。

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"

EDIT4: 最後の行を実行し、最後に寝ると次のようになります。

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 Serverメッセージが表示されます。

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

SSH経由で手動でログインしてプロセスを直接終了すると、「完了」と表示されます。

EDIT5:EDIT4を使用してエンドポイントに要求するとページが返されますが、Bottleにエラーが発生します。

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>&1STDERRをSTDOUTにリダイレクトすることです。

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

時には名前が付けられていませんが、番号が付けられています!組み込みの数字は順番に0、1、2です。デフォルトでは、明示的に名前や番号を最初に指定しない場合は、STDOUTについて話します。

この場合、上記のコマンドが標準出力を/ dev / nullにリダイレクトして、不要なエントリ(しばしばビットバケットと呼ばれる)をダンプできる場所で、標準エラーを標準出力にリダイレクトすることがわかります。目的地の前の&)。

したがって、簡単に説明すると、「このコマンドのすべての出力はブラックホールにプッシュする必要があります」です。これはプログラムを本当に静かにする良い方法です!
> /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"シェルスクリプトstart.shがsshコマンドで実行されます。

説明から:

コマンドを指定すると、ログインシェルではなくリモートホストで実行されます。

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

ローカル端末で実行することと異なる点は、nohup python3 -u <script> &ローカルバックグラウンドプロセスとして実行され、sshコマンドはリモートバックグラウンドプロセスとして実行しようとすることです。

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

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

バラより画面マニュアル

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

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

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

huponexitまた、シェルがシャットダウン、シャットダウン、または削除されたときにシェルがどのように機能するかについての概要も参照してください。現在の問題は、シェルセッションの終了方法に関連しているようです。2

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

  2. huponexitがtrueの場合、nohupまたはdisownを使用して、プロセスがシャットダウン時に終了しないように、シェルからプロセスを分離できます。または画面を使ってタスクを実行します。

  3. huponexitがfalse(現在の一部のLinuxではデフォルト)の場合、通常のログアウト時にバックグラウンドジョブは終了しません。

  4. ただし、huponexitがfalseの場合でもssh接続が終了したり、切断された場合(通常のログアウトとは異なり)、バックグラウンドプロセスは終了し続けます。これは、(2) で disown または nohup を使用して回避できます。

最後に、shopt huponexitの使用方法のいくつかの例は次のとおりです。サム

$ 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?これにより、リモートプロセスがローカルプロセスに依存するのを防ぎstdin、ローカルプロセスがssh session終了すると閉じます。 stdin.

答え4

pythonこれはスクリプトやpythonスクリプト自体の問題のように聞こえます。実際に行われたすべての操作(リダイレクトを単純化することに加えて)は、プログラムを実行する前にnohupシグナルハンドラをHUP(無視)に設定することです。プログラムが起動し始めると、プログラムがリセットされたり、独自のハンドラをインストールしたりSIG_IGNできなくなります。SIG_DFL

試すことができる1つは、コマンドを括弧で囲んで二重分岐効果を取得し、スクリプトがpythonシェルプロセスの子プロセスにならないようにすることです。たとえば、

( nohup python3 -u <script> & )

もう一度試すもう1つの方法は、(別のシェルの代わりに使用する場合)組み込みシェルを代わりにbash使用することです。すべてが文書化されているとおりに機能する場合は、実際には違いはありませんが、対話型シェルではこれにより問題が発生するのを防ぎます。信号がスクリプトに伝播しないようにします。次の行または以下のような行にdisownを追加できます(aの後にaを追加するのはエラーです)。disownnohupHUPpython;&bash

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

上記の方法や一部の組み合わせが機能しない場合、問題を解決できる唯一の場所はpythonスクリプト自体です。

関連情報