web-dev-qa-db-ja.com

python-daemonでデーモンを設定するにはどうすればよいですか?

私はデーモンが初めてなので、これが初心者の質問であればおologiesびします。

他のいくつかの回答(たとえば、 この質問 )で python-daemon パッケージが PEP 314 =標準。

残念ながら、python-daemonは ドキュメンテーションのビットライト (または、知識/経験のビットライトの可能性が高い...;)であり、おそらく本当に基本的なものが欠けていると思います。ここに私がやっていることがあります:

私は次のものを持っています:

_import daemon

logfile = open('daemon.log', 'w')

context = daemon.DaemonContext(stdout = logfile, stderr = logfile)

context.open()

with context:
    do_something_1()
    do_something_2()
_

質問:python-daemonを使用してデーモンを設定するにはどうすればよいですか?デーモンを起動および停止するにはどうすればよいですか?


サイドノート:

私は基本的に、ここで.open()メソッドをどのように使用するべきか、または使用すべきかどうかについて大まかな推測をしています。この点についてはドキュメントは明確ではありませんでした。私がそれを含めても含めなくても、同じことが起こるようです。

だから、今私は何をしますか?このファイルを実行しようとすると、たとえば:

_python startConsumerDaemons.py
_

do_something_1()を実行しているように見えますが、2番目は実行していないようです。そして、プログラムをターミナルウィンドウにアタッチしたままにしておくようです。 IE、stdoutはリダイレクトされず、ターミナルウィンドウを閉じるとプロセスが強制終了されます。だから、私はここで何か間違ったことをしていると確信しています...私は何を違うようにすべきですか?

そして、最後に、デーモンを実行した後、どのようにデーモンを停止/再起動しますか(たとえば、基礎となるコードに変更を加えた場合)。

29
CQP

ここに私が持っているものがあります。また、sysv initスクリプトもあります。 レポはGitHubにあります 、そして私は 簡単なブログ記事 を見つけました。

実行できるデーモンプロセスは1つだけです。他のほとんどのLinuxデーモンと同様に、PIDロックファイルによって管理されます。やめるには

kill `cat /var/run/eg_daemon.pid`

実行中かどうかを確認するには:

ps -elf | grep `cat /var/run/eg_daemon.pid`

Pidfileサブモジュールを使用して、PIDファイルは自動的に管理されます。デーモンが停止すると、pidfileはクリアされます。 initスクリプトについては、リンクされたGitHubリポジトリをご覧ください。

Pythonデーモンコード:

#!/usr/bin/env python3.5
import sys
import os
import time
import argparse
import logging
import daemon
from daemon import pidfile

debug_p = False

def do_something(logf):
    ### This does the "work" of the daemon

    logger = logging.getLogger('eg_daemon')
    logger.setLevel(logging.INFO)

    fh = logging.FileHandler(logf)
    fh.setLevel(logging.INFO)

    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(formatstr)

    fh.setFormatter(formatter)

    logger.addHandler(fh)

    while True:
        logger.debug("this is a DEBUG message")
        logger.info("this is an INFO message")
        logger.error("this is an ERROR message")
        time.sleep(5)


def start_daemon(pidf, logf):
    ### This launches the daemon in its context

    ### XXX pidfile is a context
    with daemon.DaemonContext(
        working_directory='/var/lib/eg_daemon',
        umask=0o002,
        pidfile=pidfile.TimeoutPIDLockFile(pidf),
        ) as context:
        do_something(logf)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Example daemon in Python")
    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')
    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')

    args = parser.parse_args()

    start_daemon(pidf=args.pid_file, logf=args.log_file)

完全を期すために、ここにinitスクリプトを示します。 "kill"は実際にはPOSIXシグナルを送信するための単なる方法であることに注意してください。概要についてはsignal(7)のmanページを参照してください。 python-daemonコンテキストはシグナルをキャッチし、ファイル記述子を完全に閉じるプロセスを終了し、PIDファイルを自動的に削除します。だから、それは本当にきれいな終了です。

デーモン構成のリロードを行うために、SIGUSR1または同様のものをキャッチするコードを作成できます。 Pythonデーモンを停止するのに利点はありません。

#!/bin/bash
#
# eg_daemon      Startup script for eg_daemon
#
# chkconfig: - 87 12
# description: eg_daemon is a dummy Python-based daemon
# config: /etc/eg_daemon/eg_daemon.conf
# config: /etc/sysconfig/eg_daemon
# pidfile: /var/run/eg_daemon.pid
#
### BEGIN INIT INFO
# Provides: eg_daemon
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Short-Description: start and stop eg_daemon server
# Description: eg_daemon is a dummy Python-based daemon
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

if [ -f /etc/sysconfig/eg_daemon ]; then
        . /etc/sysconfig/eg_daemon
fi

eg_daemon=/var/lib/eg_daemon/eg_daemon.py
prog=eg_daemon
pidfile=${PIDFILE-/var/run/eg_daemon.pid}
logfile=${LOGFILE-/var/log/eg_daemon.log}
RETVAL=0

OPTIONS=""

start() {
        echo -n $"Starting $prog: "

        if [[ -f ${pidfile} ]] ; then
            pid=$( cat $pidfile  )
            isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )

            if [[ -n ${isrunning} ]] ; then
                echo $"$prog already running"
                return 0
            fi
        fi
        $eg_daemon -p $pidfile -l $logfile $OPTIONS
        RETVAL=$?
        [ $RETVAL = 0 ] && success || failure
        echo
        return $RETVAL
}

stop() {
    if [[ -f ${pidfile} ]] ; then
        pid=$( cat $pidfile )
        isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}' )

        if [[ ${isrunning} -eq ${pid} ]] ; then
            echo -n $"Stopping $prog: "
            kill $pid
        else
            echo -n $"Stopping $prog: "
            success
        fi
        RETVAL=$?
    fi
    echo
    return $RETVAL
}

reload() {
    echo -n $"Reloading $prog: "
    echo
}

# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p $pidfile $eg_daemon
    RETVAL=$?
    ;;
  restart)
    stop
    start
    ;;
  force-reload|reload)
    reload
    ;;
  *)
    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"
    RETVAL=2
esac

exit $RETVAL
10
phzx_munki

完全な例は こちらから入手可能 です。

Python-daemonの内部動作をよりよく理解できるはずです。

さらに、提供されるコードは、デーモンを単純に開始/停止するためのinitスクリプトの例を示しています。ただし、引数stopを使用して元の関数を再度呼び出すだけで、開始/停止できます。

python original_func.py stop
5
gromain

'with'ステートメントのドキュメント でわかるように、このステートメントは、目的に関連する「マジック」を実行します。具体的には:

1つの「アイテム」を含むwithステートメントの実行は、次のように進行します。

  1. コンテキスト式(with_itemで指定された式)が評価されて、コンテキストマネージャーが取得されます。

  2. コンテキストマネージャーの__exit__()は、後で使用するために読み込まれます。

  3. コンテキストマネージャーの__enter__()メソッドが呼び出されます。

  4. Withステートメントにターゲットが含まれていた場合、__enter__()からの戻り値がターゲットに割り当てられます。

  5. スイートが実行されます。

  6. コンテキストマネージャーの__exit__()メソッドが呼び出されます。例外によってスイートが終了した場合、そのタイプ、値、およびトレースバックが__exit__()への引数として渡されます。それ以外の場合、3つのNone引数が提供されます。

これは何を意味するのでしょうか? 問題のPEP によく目を向けると、これはpython-daemonのドキュメントとしても機能します(実際に大幅に改善される可能性があります)。__enter__()および__exit__()

このクラスは、___enter___および___exit___メソッドを介してコンテキストマネージャープロトコルも実装します。

__enter__()

インスタンスのopen()メソッドを呼び出して、インスタンスを返します。

__exit__(exc_type, exc_value, exc_traceback)

インスタンスのclose()メソッドを呼び出し、例外が処理された場合はTrueを、そうでない場合はFalseを返します。

言い換えると、open()は必要ありません。PEPに記載されている例(正しく説明されていませんが)はそのまま機能します。 withステートメントは何かを意味しますが、ループを保持しません。スコープの終わりに達すると、exit()を呼び出します。 python-daemonはclose()を意味します。したがって、しばらくの間、Trueまたは無限ループを考慮する必要があります。

2番目のスクリプトが機能しない場合の動作については、実際に言うことはできません。最初のスクリプトが既に動作していることに驚いています。デーモンが停止している場合、スクリプトに問題があることは確かです。consumerDaemonLogFileを確認できます。 (補足として、タイプミス 'sderr'-> 'stderr'があります)

また、PEPで、指定されていない場合、作業ディレクトリプロパティはデフォルトで「/」になっていることがわかります。スクリプトで相対パスを使用している場合、これが問題の原因になる可能性があります。

最後に、最後の質問については、PIDを見つけるデーモンを簡単に殺すことができます。

_ps ax | grep startConsumerDaemons.py
_

sIGTERMを送信します。

_kill <pid>
_

Gromainが提供する答えは、 'daemon.runner()'を使用して開始および停止するより便利な方法を提供しますが、セットアップははるかに複雑です。

3
ArnauOrriols

daemon.DaemonContextコンストラクターは、lockfileオプションを受け入れます。プロセスのPIDを記録するロックファイルライブラリを使用します。

ライブラリは元々lockfile.PIDLockFileクラスですが、そのライブラリは現在、適切な代替品なしで廃止されています。ただし、同じセマンティクスで別のオブジェクトを実装できます。

次に、指定されたPIDファイルの内容を読み取るだけで、プロセスのPIDが検出されます。そのPIDを使用して、実行中のデーモンにシグナルを送信します。

0
bignose

モジュール「python-daemon」に関する有用なドキュメントがまだありません。私は個人的にそれを使うことをあきらめました、そして今、私はサンダーマレシャルのデーモンコードをうまく使うことができました この回答で参照されます

python testdaemon.py stopを呼び出したときにできるように、少し変更しました。

コードはこちら


サンプル使用法:

import sys, daemon, time

class testdaemon(daemon.Daemon):
    def run(self):
        self.i = 0
        with open('test1.txt', 'w') as f:
            f.write(str(self.i))
        while True:
            self.i += 1
            time.sleep(1)

    def quit(self):
        with open('test2.txt', 'w') as f:
            f.write(str(self.i))

daemon = testdaemon()

if 'start' == sys.argv[1]: 
    daemon.start()
Elif 'stop' == sys.argv[1]: 
    daemon.stop()
Elif 'restart' == sys.argv[1]: 
    daemon.restart()
0
Basj

Linuxでは、次を実行してデーモンを停止できます。

$ ps -x

デーモンに対応するPIDを見つけて、プロセスを強制終了します。

0
Joseph Feeney