要約
リモートのコンピュータでプログラムを実行し、そのプログラムをバックグラウンドで継続したいということがあるかと思います。例えば、時間のかかるスクリプトを実行して一度パソコンを閉じるときや、APIサーバのような常駐させるプログラムを実行するときです。
その時には、以下のようにコマンドを実行することで、リモート接続を切断してもプログラムが動作し続けます。
nohup 実行したいコマンド &
例)
nohup python main.py &
環境によってはnohupがなくてもバックグラウンドで持続するのですが、違いをあまり理解できていないです…。参考にあげたこちらの記事が詳しく説明してありました。
背景
実行に時間のかかるスクリプトがあり、研究室のサーバで実行をしておくことにしました。VSCodeのRemote SSHからサーバに接続し、python main.py &のように実行してVSCodeを閉じ、少し後に確認をするとプログラムの実行がキャンセルされて止まっていました。
プログラムをサーバ上で動かし続けてローカルのVSCodeは閉じておきたかったので、その方法を確認しました。
原因
ターミナルが終了したときにSIGHUPというシグナルが送られるようです。
元々、コマンドの後ろに&と付けることでプログラムをバックグラウンドで実行できると聞いたことがあったので、python main.py &と実行していました。ですが、このように実行した場合でも上記のSIGHUPのシグナルを受け取るとプログラムが停止されてしまうそうです。
したがって以下のような流れでプログラムの実行がキャンセルされていたようです。
- VSCodeを閉じたことによりターミナルが終了し
SIGHUPのシグナルが送られる。 - バックグラウンドで実行されていたプログラムが
SIGHUPのシグナルにより終了させられる。
対策
一番シンプルだったのが、SIGHUPを受け取っても実行が止まらないように設定することです。実行コマンドの前にnohupと付けることで、そのコマンドはSIGHUPを受け取っても終了しないようになるそうです。
したがって、nohup python main.py &のように実行することで、pythonのスクリプトがバックグラウンドで実行され、かつターミナルが終了しても実行され続けるようになりました。
動作の確認
今までの実行方法
まず、VSCodeのリモートSSHを使ってサーバに接続します。
サーバ内で確認のために以下のようなプログラムを作成しました。このプログラムは3秒おきに現在時刻を出力し続けます。print関数のflush=Trueは、printの出力のバッファリングをしないようにしています。これをしないと後述のnohup.outに出力が記録されませんでした。
python main.py &としてプログラムを実行します。この際、プログラムはバックグラウンドで実行され、ps aux | grep pythonなどでプロセスを確認するとpythonのプログラムが実行されていました。
import time
from datetime import datetime
while True:
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"), flush=True)
time.sleep(3)
一度VSCodeを閉じてターミナルを終了します。
再度リモートSSHでサーバに接続し、ps aux | grep pythonのようにプロセスを確認してみると、先ほどのPythonのプロセスは見られませんでした。
nohupを付けた実行方法
上と同様のプログラムを、今度はnohup python main.py &として実行します。ps aux | grep pythonで該当のプログラムを確認できます。
ここで一度VSCodeを閉じて、再度リモートSSHでサーバに接続します。
ps aux | grep pythonでプロセスを確認すると、まだ先ほどのPythonのプログラムが実行され続けていました!
nohupのときの標準出力
nohupを使ってプログラムを実行すると、出力はターミナルには表示されなくなります。代わりに実行したディレクトリにnohup.outというファイルが作成されて、そこに出力が記録されるようです。
ただ、Pythonの標準のprint関数だと出力がバッファリングされてnohup.outファイルに出力が記録されませんでした。そのため、上記のプログラムでは出力をバッファリングしないようにしています。
参考