Pythonでデーモンをどのように作成しますか?


244

Googleで検索すると、 x2コードスニペットが表示されます。最初の結果はこのコードレシピです、多くのドキュメンテーションと説明があり、その下にいくつかの有用な議論があるです。

ただし、他のコードサンプルには、それほど多くのドキュメントは含まれていませんが、start、stop、restartなどのコマンドを渡すためのサンプルコードが含まれています。また、デーモンがすでに実行されているかどうかなどを確認するのに便利なPIDファイルも作成します。

これらのサンプルはどちらもデーモンの作成方法を説明しています。考慮する必要がある追加の事項はありますか?1つのサンプルが他のサンプルよりも優れているのはなぜですか?


1
私は常にデーモン化コードが不要であることを発見しました。なぜシェルにそれをさせないのですか?
emil.p.stanchev 2010年

17
これは、setsidまたはsetpgrpを実行しないためです。
bmargulies

4
supervisord.orgを使用してください。この方法では、fork()したり、stdin / stderrにリダイレクトしたりする必要はありません。通常のプログラムを書くだけです。
guettli 2013年

回答:


169

現在のソリューション

PEP 3143(標準デーモンプロセスライブラリ)のリファレンス実装がpython-daemonとして利用可能になりました。

歴史的な答え

Sander Marechalのコードサンプルは、2004年に最初に投稿されたオリジナルよりも優れています。Pyroのデーモン化プログラムに貢献したことがありますが、やり直さなければならない場合は、Sanderのコードを使用するでしょう。


72
編集:最初にこの返信を投稿したので、PEP 3143のリファレンス実装が利用可能になりました:pypi.python.org/pypi/python-daemon
Jeff Bauer

@JeffBauer元のリンクが停止しました。それが役に立ったことを覚えています。たぶん、そのためのライブリンクを知っていませんか?
CrazyCasta 2014

1
@CrazyCasta:Sander MarechalのバージョンはWayback Machineで
Jeff Bauer

1
@JeffBauer:サンダーのコードはまだよりも優れていhttp://pypi.python.org/pypi/python-daemonます。より信頼できる。ただ1つの例: 同じデーモンを2回起動してみてくださいpython-daemon:大きな醜いエラー。Sanderのコードを使用した場合:「Daemonはすでに実行されています」というすばらしい通知。
Basj

2
「python-daemon」モジュールのドキュメントはまだ見当たらず(他の多くのSOの質問も参照)、かなりわかりにくいため(このモジュールを使用してコマンドラインからデーモンを適切に開始/停止する方法は?)、Sander Marechalのコードサンプルを変更して追加しましたquit()デーモンが停止する前に実行されるメソッド。ここにあります。
Basj 2016年

163

正常に動作するデーモンプロセスになる場合、面倒な処理たくさんあります

  • コアダンプを防止します(多くのデーモンはrootとして実行され、コアダンプには機密情報が含まれる場合があります)

  • chroot刑務所内で正しく行動する

  • ユースケースに応じて、UID、GID、作業ディレクトリ、umask、およびその他のプロセスパラメータを適切に設定する

  • 昇格suidsgid特権を放棄する

  • 使用例に応じて除外して、開いているすべてのファイル記述子を閉じます

  • 既に切り離さコンテキスト内で開始した場合、正しく動作などinitinetdなど

  • 賢明なデーモンの動作用にシグナルハンドラーをセットアップしますが、ユースケースによって決定される特定のハンドラーも使用します

  • 標準ストリームをリダイレクトしないstdinstdoutstderrデーモンプロセスがもはや制御端子を有しているので

  • PIDファイルを協調的なアドバイザリロックとして処理します。これは、それ自体がワームの全体の缶であり、多くの相反するが有効な方法で動作します。

  • プロセスが終了したときに適切なクリーンアップを許可する

  • 実際にゾンビにつながることなくデーモンプロセスになる

これらの一部は標準的なUnixの文献(UNIX環境での高度なプログラミング、故W.リチャードスティーブンスによるAddison-Wesley、1992)に記載されているように、標準的なものです。その他、ストリームリダイレクトやPIDファイル処理などは従来の動作ですほとんどのデーモンは、ユーザーが期待するだろうが、それはあまり標準化されています。

これらはすべて、PEP 3143の「標準デーモンプロセスライブラリ」仕様でカバーされていますpython-デーモンのPython 2.7以降、およびPythonの3.2以降のリファレンス実装の作品。


26
「gaol」のスペルが正しいのは、W。リチャードスティーブンスがスペルを入力した方法
です。-)

7
刑務所は英語のものです。ポスターはオーストラリアからのものなので、理にかなっています。
デビン

1
py3kフレンドリーなバージョンを作る計画はありますか?
Tim Tisdall 2013年

97

これが、新しいデーモンアプリケーションを開発しているときに、最初に使用する基本的な 'Howdy World' Pythonデーモンです。

#!/usr/bin/python
import time
from daemon import runner

class App():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
    def run(self):
        while True:
            print("Howdy!  Gig'em!  Whoop!")
            time.sleep(10)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

python-daemonライブラリが必要になることに注意してください。次の方法でインストールできます。

pip install python-daemon

次に、で開始し./howdy.py start、で停止し./howdy.py stopます。


5
これdaemonインポートモジュールは、Python(まだ)の標準的な部分ではありません。pip install python-daemonまたは同等のものと一緒にインストールする必要があります。
ネイト

6
説明したとおりにpython-daemonをインストールしましたが、アプリを実行しようとすると(最後の3行と同じ)、ImportError:cannot import name runner
Nostradamnit

正しくインストールされているか確認できますか?$ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
ダスティンカークランド

4
この提案は時代遅れであるようです-2013年9月の時点で、とにかく、python.org / dev / peps / pep-3143はインポート可能な「ランナー」について言及していません。もちろん、これは@Nostradamnitの観察を説明します。
offby1 2013

2
これは2013年9月、Ubuntu 13.04で、ストックPythonパッケージ、python2.7およびpython-daemonがインストールされている状態で、まだ問題なく動作します。しかし、python3を使用すると、「デーモンインポートランナーImportError: 'daemon'という名前のモジュールがありません」というエラーが表示されます
Dustin Kirkland

42

python-daemonに注意してください背後にある多くの問題を解決するパッケージにてください。

(Debianパッケージの説明から)それを可能にする他の機能の中で:

  • プロセスを独自のプロセスグループに切り離します。
  • chroot内での実行に適したプロセス環境を設定します。
  • suidおよびsgid特権を放棄します。
  • 開いているすべてのファイル記述子を閉じます。
  • 作業ディレクトリ、uid、gid、umaskを変更します。
  • 適切なシグナルハンドラを設定します。
  • stdin、stdout、およびstderrの新しいファイル記述子を開きます。
  • 指定されたPIDロックファイルを管理します。
  • 終了処理のためのクリーンアップ関数を登録します。

35

別の方法-通常のデーモン化されていないPythonプログラムを作成してから、supervisordを使用して外部からデーモン化します。これは多くの頭痛の種を救うことができ、* nixおよび言語の移植性があります。


1
これが最良の方法だと思います。特に、1つのオペレーティングシステムで複数のデーモンを実行したい場合。コーディングせずに、再利用してください。
guettli 2013年

それは多くの問題を簡素化します。私は本当のデーモンを書きました-それらは簡単ではありません。
クリスジョンソン

1
ベストアンサーはここに隠されています:)
kawing-chiu

1
これは金です。python-daemonを実行するために何時間も費やした後、これは私のために機能するすぐに使えるソリューションです。優れたドキュメントと例により、デーモンが数分で稼働しました。
Nikhil Sahu

17

おそらく質問への直接の回答ではありませんが、systemdを使用してアプリケーションをデーモンとして実行できます。次に例を示します。

[Unit]
Description=Python daemon
After=syslog.target
After=network.target

[Service]
Type=simple
User=<run as user>
Group=<run as group group>
ExecStart=/usr/bin/python <python script home>/script.py

# Give the script some time to startup
TimeoutSec=300

[Install]
WantedBy=multi-user.target

多くの作業が自動的に行われ、デーモンスクリプトがシステムの他の部分と同じように動作するため、この方法を使用します。

-Orby


これは適切で健全な方法です。1)/etc/systemd/system/control.serviceに保存する必要があります2)管理sudosystemctl start control.service
jimper

7

YapDiは、ハッカーニュースに登場した比較的新しいpythonモジュールです。かなり便利に見えますが、Pythonスクリプトをスクリプト内からデーモンモードに変換するために使用できます。


6

python-daemonはまだpython 3.xをサポートしていないので、メーリングリストで読むことができるものから、決してそうなることはないかもしれません。PEP3143の新しい実装を書きました: 。PEP3143の pep3143daemon

pep3143daemonは、少なくともpython 2.6、2.7および3.xをサポートする必要があります

また、PidFileクラスも含まれています。

ライブラリは、標準ライブラリと6つのモジュールにのみ依存しています。

これは、python-daemonのドロップイン代替として使用できます。

こちらがドキュメントです。


6

この関数はアプリケーションをデーモンに変換します:

import sys
import os

def daemonize():
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #1 failed: {0}\n'.format(err))
        sys.exit(1)
    # decouple from parent environment
    os.chdir('/')
    os.setsid()
    os.umask(0)
    # do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # exit from second parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #2 failed: {0}\n'.format(err))
        sys.exit(1)
    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = open(os.devnull, 'r')
    so = open(os.devnull, 'w')
    se = open(os.devnull, 'w')
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

5

@Dustinによって言及されたデーモンモジュールが機能しなかったのではないかと心配です。代わりにpython-daemonをインストールし、次のコードを使用しました。

# filename myDaemon.py
import sys
import daemon
sys.path.append('/home/ubuntu/samplemodule') # till __init__.py
from samplemodule import moduleclass 

with daemon.DaemonContext():
    moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.

走るのは簡単

> python myDaemon.py

完全を期すために、ここにsamplemoduleディレクトリのコンテンツがあります

>ls samplemodule
__init__.py __init__.pyc moduleclass.py

moduleclass.pyのコンテンツは

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.

2

Pythonでデーモン化する際に考慮すべきことのもう1つ:

Python ロギングを使用していて、デーモン化した後も引き続き使用したい場合close()は、ハンドラー(特にファイルハンドラー)を呼び出してください。

これを行わない場合でも、ハンドラーはファイルが開いていると見なすことができ、メッセージは単に表示されなくなります。つまり、ロガーがファイルが閉じていることを確認します。

これは、デーモン化するときに、開いているすべてのファイル記述子を無差別に閉じることを前提としています。代わりに、ログファイル以外のすべてを閉じてみることができます(ただし、通常はすべて閉じてから、必要なファイルを再び開く方が簡単です)。


たとえば、DaemonContextのfiles_preserveオプションを使用してデーモンにロギングハンドラーを渡すよりも、新しいロギングハンドラーを開く方が良いと思いますか?
HeyWatchThis

ロガーを閉じるだけで、新しいロガーを作成するわけではありません(必要なときにロガーを再び開くだけです)。しかし、それを行うのは本当に簡単ですが、おそらく他のいくつかの賢いことをしているので、DaemonContextを使用する方が良いかもしれません(保持することで適切なデーモン化が可能であると仮定します)。
マシューウィルコックスソン2014年

2

python-daemonモジュールによって提供される純粋なPythonソリューションを好むかもしれませんが、少なくともBSDLinuxには、正しくdaemon(3)機能する関数があります。libc

Pythonからそれを呼び出すのは簡単です:

import ctypes

ctypes.CDLL(None).daemon(0, 0) # Read the man-page for the arguments' meanings

残っている唯一のことは、PIDファイルの作成(およびロック)です。しかし、あなたが自分で処理できること...


1

デーモンが停止する前に実行されるメソッドを追加するために、Sander Marechalのコードサンプル(承認された回答で @JeffBauerが言及)の数行を変更しましたquit()。これは時々非常に役立ちます。

ここにあります。

注:「python-daemon」モジュールは使用しません。ドキュメントがまだ見つからず(他の多くのSOの質問も参照)、かなりわかりにくいためです(このモジュールを使用してコマンドラインからデーモンを適切に開始/停止する方法は?)


-1

数年と多くの試み(私はここで与えられたすべての答えを試しましたが、それらすべては最後に小さな欠点がありました)の後で、Pythonから直接デーモンを起動、停止、再起動したいよりも良い方法があることに気づきました:代わりにOSツールを使用してください。

たとえば、Linuxの場合、python myapp startand python myapp stopを実行する代わりに、これを実行してアプリを起動します。

screen -S myapp python myapp.py    
CTRL+A, D to detach

またはscreen -dmS myapp python myapp.pyすることを開始し、1つのコマンドでそれを切り離します

次に:

screen -r myapp

この端末に再度接続します。ターミナルに入ると、CTRL + Cを使用して停止できます。


-2

Pythonでデーモンを作成する最も簡単な方法は、Twistedイベント駆動型フレームワークを使用することです。デーモン化に必要なすべてのものを処理します。Reactorパターンを使用して同時リクエストを処理します。


5
ハンマーは大きすぎて使用できません。ほとんどの人は、デーモンとして作成した短いPythonスクリプトを実行したいだけです。上記のpython-daemonが正解です。
Tom Swirly、2012年

2
この答えは非常に傲慢でしたが、役に立ちました。
fiatjaf

-28

80%の場合、人々が「デーモン」と言うとき、彼らはサーバーだけを望んでいます。この点では質問が完全に不明確であるため、回答の可能なドメインが何であるかを言うのは難しいです。サーバーで十分なので、そこから始めます。実際の「デーモン」が実際に必要な場合(これはまれです)、nohupサーバーをデーモン化する方法として読んでください。

実際のデーモンが実際に必要になるまでは、単純なサーバーを作成するだけです。

WSGIリファレンス実装も見てください。

Simple HTTP Serverも見てください。

「考慮すべき追加の事項はありますか?」はい。百万ものこと。どのプロトコル?リクエスト数は?各リクエストのサービス期間はどれくらいですか?彼らはどれくらいの頻度で到着しますか?専用のプロセスを使用しますか?スレッド?サブプロセス?デーモンを書くのは大変な仕事です。


12
これらのライブラリはどちらも、fork()2つはもちろんのこと、1つも実行しません。彼らはデーモン化とは何の関係もありません。
Brandon Rhodes、

8
Unixオペレーティングシステムでは、ギリシャ人が「デーモン」と呼んだ空中係員のような「デーモン」プロセスは、「側に立っている」プロセスです。代わりに、直接そのユーザのTTYを通じて単一のユーザーにサービスを提供するのは、デーモンはありませんTTYに所属しますが、システム上の多数のユーザからの要求に答えることができる、または-などのcrondsyslogd-システム全体のハウスキーピングサービスを行います。デーモンプロセスを作成するには、少なくともfork()すべてのファイル記述子を閉じた状態でdouble を実行する必要があります。これにより、システムコンソールを含むすべての制御端末からの信号の影響を受けなくなります。bignoseの回答を参照してください。
Brandon Rhodes

5
@S Lott —「サーバー」は、プロセス何を行うかを記述します(独自のアクションを開始するのではなく、着信要求を待機します)。「デーモン」は、プロセスどのように実行されるかを記述します(ウィンドウまたは制御端末なしで)。SimpleHTTPServer確かにサーバーですが、それ自体をデーモン化する方法をネイティブで認識していないサーバーです(たとえば、Ctrl-Cを押してサーバーにアクセスできます)。nohupは単純なプロセスをデーモン化するためのユーティリティです。つまり、nohuppedサーバーは確かにデーモンサーバーの両方です。このStack Overflowの質問は本質的に「Pythonで実装するにはどうすればよいですか?」nohup
Brandon Rhodes、

5
はい、ありますが、OPの質問に対する私の理解は、彼は自分のpythonプログラム内から、他のものを使用せずにデーモン化を実行したいということです。
Noufal Ibrahim、2011

4
@S Lott —感心する必要はありません!他のすべての回答の作成者は「デーモン」の意味を知っていたため、この質問を解釈する私の能力はほとんど独特ではありません。:)そして、私は著者に車輪を再発明してほしいという考えをどこで得ましたか?私nohupはすばらしいツールだと思います。この便利なアイデアを実際の答えに単純に移すと、-1票は削除されます。実際、あなたが言及supervisordし、それによって著者がロギング、スタートストップスクリプト、およびスロットルの再起動を行わなくても済むようにしたら、私はあなたを+1します。:)
Brandon Rhodes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.