web-dev-qa-db-ja.com

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

Python3スクリプト(startup.shと呼びましょう)を起動するbashスクリプトがあり、次のキー行があります。

Nohup python3 -u <script> &

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

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>"を実行する initスクリプトの作成 も試しましたが、同じ動作が得られました。

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 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)
21
neverendingqs

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

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の順になっています。デフォルトでは、明示的に名前を付けたり番号を付けたりしない場合は、STDOUTについて話していることになります。

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

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

13
jlliagre

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 User's Manual

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

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

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

また、シェルが終了、強制終了、または削除された場合のhuponexitの動作の概要もご覧ください。あなたの現在の問題はシェルセッションの終了方法に関連していると思います。 2

  1. Ssh接続を介して開かれたシェルのバックグラウンドの有無にかかわらず、すべての子プロセスは、huponexitオプションが設定されている場合にのみssh接続が閉じられると、SIGHUPで強制終了されます。

  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
iyrin

競合状態にあると思います。それはこのようなものになるでしょう:

  • SSH接続が始まります
  • SSHがstartup.shを開始します
  • startup.shはバックグラウンドプロセスを開始します(Nohup)
  • startup.shが終了する
  • sshが終了すると、子プロセスが強制終了されます(つまり、Nohup)

Sshが物事を短くしていなかった場合、次のことが起こりました(これら2つの順序は不明)。

  • Nohupはあなたのpythonスクリプトを開始します
  • Nohupは親プロセスとターミナルから切断します。

したがって、startup.shとsshは、Nohupが実行する前に終了するため、最後の2つの重要なステップは発生しません。

Startup.shの最後に数秒間スリープすると、問題が解消されると思います。どれだけの時間が必要か正確にはわかりません。それを最小限に抑えることが重要である場合、多分あなたはそれが安全かどうかを確認するためにprocで何かを見ることができます。

2
mc0e

たぶん試す価値がある-nsshを開始するときのオプション?これは、ローカルのstdinへのリモートプロセスの依存関係を防ぎます。もちろん、ssh session終了します。そして、これにより、stdinにアクセスしようとすると、リモートの価格が終了します。

2
Georgiy

これは、pythonスクリプトまたはpython自体の動作に関する問題のように聞こえます。 Nohupが実際に行うこと(リダイレクトを単純化すること)は、プログラムを実行する前にHUPシグナルのハンドラーをSIG_IGN(無視)に設定するだけです。プログラムがSIG_DFLに戻るように設定したり、実行が開始されたら独自のハンドラーをインストールしたりすることはできません。

コマンドを括弧で囲むと、二重のフォーク効果が得られ、pythonスクリプトはシェルプロセスの子ではなくなります。例えば:

( Nohup python3 -u <script> & )

試してみる価値のあるもう1つのこと(別のシェルではなくbashを使用している場合)は、disownの代わりにNohupビルトインを使用することです。すべてがドキュメントどおりに機能している場合、これは実際には何の違いもありませんが、インタラクティブシェルでは、これによりHUP信号がpythonスクリプトに伝達されなくなります。次の行または同じ行にdisownを追加できます(;の後に&を追加すると、bashでエラーが発生します)。

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

上記またはいくつかの組み合わせが機能しない場合、問題を解決する唯一の場所はpythonスクリプト自体にあります。

1
Graeme

仕事がセッションに結びついているからだと思います。それが終了すると、すべてのユーザージョブも終了します。

0
user208145

Nohupがその出力ファイルを開くことができる場合は、Nohup.outpythonを介してスクリプトを実行すると、sshがパス上にない可能性があります。

コマンドのログファイルを作成してみます。使ってみてください:

Nohup /usr/bin/python3 -u <script> &>logfile &
0
BillThor