Как вы создаете демон в Python?

Поиск в Google показывает фрагменты кода x2. Первый результат этого рецепта кода, который содержит много документации и объяснений, а также некоторые полезные обсуждения ниже.

Тем не менее, другой пример кода, хотя и не содержит так много документации, включает пример кода для передачи таких команд, как запуск, остановка и перезапуск. Он также создает PID-файл, который может быть полезен для проверки, запущен ли демон и т. Д.

Оба примера объясняют, как создать демон. Есть ли какие-то дополнительные вещи, которые необходимо учитывать? Один образец лучше другого, и почему?

15 ответов

Решение

Текущее решение

Эталонная реализация PEP 3143 (Стандартная библиотека процессов демона) теперь доступна как python-daemon.

Исторический ответ

Пример кода Сандера Марешала превосходит исходный, который был первоначально опубликован в 2004 году. Однажды я предоставил демонизатор для Pyro, но, вероятно, использовал бы код Сандера, если бы мне пришлось делать это заново.

Есть много интересных вещей, о которых нужно позаботиться, когда вы станете хорошим демоном:

  • предотвращение дампов ядра (многие демоны запускаются от имени root, а дампы ядра могут содержать конфиденциальную информацию)

  • вести себя правильно внутри chroot тюремное заключение

  • установить UID, GID, рабочий каталог, umask и другие параметры процесса соответственно для варианта использования

  • отказаться от повышенных suid, sgid привилегии

  • закройте все дескрипторы открытых файлов, с исключениями в зависимости от варианта использования

  • вести себя корректно, если он запущен в уже отсоединенном контексте, например init, inetd, так далее.

  • установить обработчики сигналов для разумного поведения демона, а также с определенными обработчиками, определяемыми сценарием использования

  • перенаправить стандартные потоки stdin, stdout, stderr поскольку у процесса-демона больше нет управляющего терминала

  • обрабатывать PID-файл как совместную консультативную блокировку, которая сама по себе представляет собой червь с множеством противоречивых, но обоснованных способов поведения

  • разрешить надлежащую очистку после завершения процесса

  • на самом деле стать процессом демона, не приводя к зомби

Некоторые из них являются стандартными, как описано в канонической литературе по Unix (Расширенное программирование в среде UNIX, покойный У. Ричард Стивенс, Аддисон-Уэсли, 1992). Другие, такие как перенаправление потоков и обработка файлов PID, являются обычным поведением, которого ожидают большинство пользователей-демонов, но они менее стандартизированы.

Все они охватываются спецификацией "Стандартной библиотеки процессов демона" PEP 3143. Реализация ссылок на python-daemon работает на Python 2.7 или более поздней версии и Python 3.2 или более поздней.

Вот мой базовый демон Python 'Howdy World', с которого я начинаю, когда разрабатываю новое приложение-демон.

#!/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,

Альтернатива - создать обычную недемонизированную программу на Python, а затем внешне ее демонизировать с помощью http://supervisord.org/. Это может сэкономить много головной боли и является переносимым на *nix- и язык.

Обратите внимание на пакет python-daemon, который решает множество проблем за демонами из коробки.

Среди других возможностей, которые он позволяет (из описания пакета Debian):

  • Отделить процесс в своей собственной группе процессов.
  • Установите среду процесса, подходящую для работы внутри chroot.
  • Откажитесь от привилегий suid и sgid.
  • Закройте все открытые файловые дескрипторы.
  • Измените рабочий каталог, uid, gid и umask.
  • Установите соответствующие обработчики сигналов.
  • Откройте новые файловые дескрипторы для stdin, stdout и stderr.
  • Управление указанным файлом блокировки PID.
  • Зарегистрируйте функции очистки для обработки при выходе.

Возможно, это не прямой ответ на вопрос, но 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

Эта функция преобразует приложение в демон:

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())

YapDi - это относительно новый Python-модуль, который появился в Hacker News. Выглядит довольно полезно, может быть использовано для преобразования скрипта Python в режим демона изнутри скрипта.

Так как python-daemon еще не поддерживает python 3.x, и из того, что можно прочитать в списке рассылки, он может никогда не будет, я написал новую реализацию PEP 3143: pep3143daemon

pep3143daemon должен поддерживать как минимум Python 2.6, 2.7 и 3.x

Он также содержит класс PidFile.

Библиотека зависит только от стандартной библиотеки и от шести модулей.

Это может быть использовано как замена для python-daemon.

Вот документация.

Боюсь, модуль демона, упомянутый @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.

Хотя вы можете предпочесть решение на чистом Python, предоставляемое модулем python-daemon, существует daemon(3) функционировать в libc- по крайней мере, на BSD и Linux - что будет правильным.

Вызвать его из Python очень просто:

import ctypes

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

Осталось только создать (и заблокировать) PID-файл. Но с этим ты справишься сам...

Еще одна вещь, о которой стоит подумать при демонизации в python:

Если вы используете ведение журнала Python и хотите продолжить использовать его после демонизации, обязательно позвоните close() на обработчики (особенно обработчики файлов).

Если вы этого не сделаете, обработчик все еще может думать, что у него есть открытые файлы, и ваши сообщения просто исчезнут - другими словами, убедитесь, что регистратор знает, что его файлы закрыты!

Это предполагает, что когда вы демонизируете, вы закрываете ВСЕ дескрипторы открытого файла без разбора - вместо этого вы можете попробовать закрыть все, кроме файлов журналов (но обычно проще закрыть все, а затем снова открыть те, которые вы хотите).

Я изменил несколько строк в примере кода Сандера Марешала (упомянутого @JeffBauer в принятом ответе), чтобы добавить quit() метод, который выполняется до остановки демона. Это иногда очень полезно.

Вот.

Примечание: я не использую модуль "python-daemon", потому что документация все еще отсутствует (см. Также много других вопросов SO) и довольно неясна (как правильно запустить / остановить демон из командной строки с этим модулем?)

После нескольких лет и многих попыток, теперь я понимаю, что есть лучший способ, чем запуск, остановка, перезапуск демона непосредственно из Python: вместо этого используйте инструменты ОС!

Короче вместо того чтобы делать python myapp start а также python myapp stopЯ делаю это, чтобы запустить приложение:

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

или же screen -dmS myapp python myapp.py запустить и отсоединить его в одной команде.

Затем:

screen -r myapp

подключить к этому терминалу снова. Оказавшись в терминале, можно использовать CTRL+C, чтобы остановить его.

Самый простой способ создания демона с помощью Python - это использование Twisted- событийной среды. Он обрабатывает все вещи, необходимые для демонизации для вас. Он использует шаблон Reactor для обработки одновременных запросов.

В 80% случаев, когда люди говорят "демон", им нужен только сервер. Поскольку вопрос совершенно неясен в этом вопросе, трудно сказать, какой может быть область возможных ответов. Поскольку сервер адекватен, начните с него. Если фактический "демон" действительно нужен (это редко), читайте дальше nohup как способ демонизации сервера.

До тех пор, пока фактический демон фактически не требуется, просто напишите простой сервер.

Также посмотрите на справочную реализацию WSGI.

Также посмотрите на простой HTTP-сервер.

"Есть ли какие-то дополнительные вещи, которые необходимо учитывать? " Да. Около миллиона вещей. Какой протокол? Сколько запросов? Как долго обслуживать каждый запрос? Как часто они будут приезжать? Будете ли вы использовать выделенный процесс? Потоки? Подпроцессы? Написание демона - большая работа.

Другие вопросы по тегам