Как заставить скрипт Python работать как сервис или демон в Linux

Я написал скрипт Python, который проверяет определенный адрес электронной почты и передает новые электронные письма внешней программе. Как я могу заставить этот скрипт работать 24/7, например, превратить его в демона или службу в Linux. Нужен ли мне также цикл, который никогда не заканчивается в программе, или это можно сделать, просто выполнив код повторно несколько раз?

15 ответов


У вас есть два варианта здесь.

  1. сделать правильный cron-задание это вызывает ваш сценарий. Cron-это общее имя для демона GNU / Linux, который периодически запускает скрипты в соответствии с установленным расписанием. Вы добавляете свой скрипт в crontab или помещаете символическую ссылку на него в специальный каталог, и демон обрабатывает задание его запуска в фоновом режиме. Ты можешь!--7-->подробнее в Википедии. Есть различные демоны cron, но ваша система GNU / Linux должна быть уже установлена.

  2. использовать какой-то python подход (например, библиотека), чтобы ваш скрипт мог демонизировать себя. Да, это потребует простого цикла событий (где ваши события запускают таймер, возможно, обеспечивается функцией сна).

Я бы не рекомендовал вам выбирать 2., потому что вы фактически повторяете функциональность cron. Парадигма системы Linux заключается в пусть несколько простых инструментов взаимодействуют и решают ваши проблемы. Если нет дополнительных причин для создания демона (в дополнение к периодическому запуску), выберите другой подход.

кроме того, если вы используете daemonize с циклом и происходит сбой, никто не будет проверять почту после этого (как указано Невоструев Иван в комментарии этой ответ). В то время как, если скрипт добавлен как задание cron, он просто снова запустится.


вот хороший класс, который берется из здесь:

#!/usr/bin/env python

import sys, os, time, atexit
from signal import SIGTERM

class Daemon:
        """
        A generic daemon class.

        Usage: subclass the Daemon class and override the run() method
        """
        def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
                self.stdin = stdin
                self.stdout = stdout
                self.stderr = stderr
                self.pidfile = pidfile

        def daemonize(self):
                """
                do the UNIX double-fork magic, see Stevens' "Advanced
                Programming in the UNIX Environment" for details (ISBN 0201563177)
                http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
                """
                try:
                        pid = os.fork()
                        if pid > 0:
                                # exit first parent
                                sys.exit(0)
                except OSError, e:
                        sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
                        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, e:
                        sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
                        sys.exit(1)

                # redirect standard file descriptors
                sys.stdout.flush()
                sys.stderr.flush()
                si = file(self.stdin, 'r')
                so = file(self.stdout, 'a+')
                se = file(self.stderr, 'a+', 0)
                os.dup2(si.fileno(), sys.stdin.fileno())
                os.dup2(so.fileno(), sys.stdout.fileno())
                os.dup2(se.fileno(), sys.stderr.fileno())

                # write pidfile
                atexit.register(self.delpid)
                pid = str(os.getpid())
                file(self.pidfile,'w+').write("%s\n" % pid)

        def delpid(self):
                os.remove(self.pidfile)

        def start(self):
                """
                Start the daemon
                """
                # Check for a pidfile to see if the daemon already runs
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if pid:
                        message = "pidfile %s already exist. Daemon already running?\n"
                        sys.stderr.write(message % self.pidfile)
                        sys.exit(1)

                # Start the daemon
                self.daemonize()
                self.run()

        def stop(self):
                """
                Stop the daemon
                """
                # Get the pid from the pidfile
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if not pid:
                        message = "pidfile %s does not exist. Daemon not running?\n"
                        sys.stderr.write(message % self.pidfile)
                        return # not an error in a restart

                # Try killing the daemon process       
                try:
                        while 1:
                                os.kill(pid, SIGTERM)
                                time.sleep(0.1)
                except OSError, err:
                        err = str(err)
                        if err.find("No such process") > 0:
                                if os.path.exists(self.pidfile):
                                        os.remove(self.pidfile)
                        else:
                                print str(err)
                                sys.exit(1)

        def restart(self):
                """
                Restart the daemon
                """
                self.stop()
                self.start()

        def run(self):
                """
                You should override this method when you subclass Daemon. It will be called after the process has been
                daemonized by start() or restart().
                """

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

Из PyPI: библиотека для реализации хорошо себя ведет процесс демона Unix.


вы можете использовать fork() для отсоединения скрипта от tty и продолжения его запуска, например:

import os, sys
fpid = os.fork()
if fpid!=0:
  # Running as daemon now. PID is fpid
  sys.exit(0)

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

while 1:
  do_your_check()
  sleep(5)

надеюсь, это вы начали.


вы также можете запустить скрипт python как службу, используя сценарий оболочки. Сначала создайте сценарий оболочки для запуска скрипта python следующим образом (scriptname arbitary name)

#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &

теперь сделайте файл в /etc / init.d / scriptname

#! /bin/sh

PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid

test -x $DAEMON || exit 0

. /lib/lsb/init-functions

case "" in
  start)
     log_daemon_msg "Starting feedparser"
     start_daemon -p $PIDFILE $DAEMON
     log_end_msg $?
   ;;
  stop)
     log_daemon_msg "Stopping feedparser"
     killproc -p $PIDFILE $DAEMON
     PID=`ps x |grep feed | head -1 | awk '{print }'`
     kill -9 $PID       
     log_end_msg $?
   ;;
  force-reload|restart)
      stop
      start
   ;;
  status)
     status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
   ;;
 *)
   echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}"
   exit 1
  ;;
esac

exit 0

теперь вы можете запускать и останавливать скрипт python с помощью команды / etc / init.D / scriptname запуск или остановка.


Как насчет использования $nohup команда в linux?

Я использую его для запуска моих команд на моем сервере Bluehost.

пожалуйста, совет, если я ошибаюсь.


простой и поддерживает версия Deamonize Установите его из индекса пакета Python (PyPI):

$ pip install daemonize

и затем использовать как:

...
import os, sys
from daemonize import Daemonize
...
def main()
      # your code here

if __name__ == '__main__':
        myname=os.path.basename(sys.argv[0])
        pidfile='/tmp/%s' % myname       # any name
        daemon = Daemonize(app=myname,pid=pidfile, action=main)
        daemon.start()

cron явно отличный выбор для многих целей. Однако он не создает службу или демон, как вы просили в OP. cron просто запускает задания периодически (то есть задание запускается и останавливается), и не чаще одного раза в минуту. Есть проблемы с cron -- например, если предыдущий экземпляр вашего скрипта все еще работает в следующий раз cron расписание приходит и запускает новый экземпляр, это нормально? cron не обрабатывает зависимости; он просто пытается начните работу, когда расписание говорит.

если вы найдете ситуацию, когда вам действительно нужен демон (процесс, который никогда не останавливается), взгляните на supervisord. Он обеспечивает простой способ обернуть обычный, не демонизированный скрипт или программу и заставить его работать как демон. Это гораздо лучший способ, чем создание собственного демона на Python.



во-первых, прочитайте на псевдонимах почты. Почтовый псевдоним будет делать это внутри почтовой системы без необходимости дурачиться с демонами или службами или чем-либо подобным.

Вы можете написать простой скрипт, который будет выполняться компанией sendmail каждый раз, когда сообщение отправляется на определенный почтовый ящик.

см.http://www.feep.net/sendmail/tutorial/intro/aliases.html

Если вы действительно хотите написать бесполезно сложный сервер, вы можете сделать этот.

nohup python myscript.py &

это все, что нужно. Ваш сценарий просто петляет и спит.

import time
def do_the_work():
    # one round of polling -- checking email, whatever.
while True:
    time.sleep( 600 ) # 10 min.
    try:
        do_the_work()
    except:
        pass

если вы используете терминал (ssh или что-то еще), и вы хотите сохранить долгосрочный скрипт после выхода из терминала, вы можете попробовать следующее:

screen

apt-get install screen

создайте виртуальный терминал внутри (а именно abc):screen -dmS abc

теперь мы подключаемся к abc:screen -r abc

Итак, теперь мы можем запустить скрипт Python: python Keep_sending_mail.py

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

после этого Keep_sending_mail.py'S PID принадлежат виртуальному экрану, а не терминал(СШ)

если вы хотите вернуться, проверьте состояние запуска скрипта, вы можете использовать screen -r abc снова


используйте любой Диспетчер служб, который предлагает ваша система - например, в Ubuntu use выскочка. Это будет обрабатывать все детали для вас, такие как запуск при загрузке, перезагрузка при аварии и т. д.


Я бы рекомендовал это решение. Вам нужно наследовать и переопределить метод run.

import sys
import os
from signal import SIGTERM
from abc import ABCMeta, abstractmethod



class Daemon(object):
    __metaclass__ = ABCMeta


    def __init__(self, pidfile):
        self._pidfile = pidfile


    @abstractmethod
    def run(self):
        pass


    def _daemonize(self):
        # decouple threads
        pid = os.fork()

        # stop first thread
        if pid > 0:
            sys.exit(0)

        # write pid into a pidfile
        with open(self._pidfile, 'w') as f:
            print >> f, os.getpid()


    def start(self):
        # if daemon is started throw an error
        if os.path.exists(self._pidfile):
            raise Exception("Daemon is already started")

        # create and switch to daemon thread
        self._daemonize()

        # run the body of the daemon
        self.run()


    def stop(self):
        # check the pidfile existing
        if os.path.exists(self._pidfile):
            # read pid from the file
            with open(self._pidfile, 'r') as f:
                pid = int(f.read().strip())

            # remove the pidfile
            os.remove(self._pidfile)

            # kill daemon
            os.kill(pid, SIGTERM)

        else:
            raise Exception("Daemon is not started")


    def restart(self):
        self.stop()
        self.start()

была аналогичная проблема, ссылка ниже работал для меня хорошо: http://werxltd.com/wp/2012/01/05/simple-init-d-script-template/#footnote_0_1077

Он не использует ничего конкретного для любого дистрибутива, если chkconfig используется, может быть запущен при запуске системы.


для создания какой-то вещи, которая работает как сервис, вы можете использовать эту вещь :

первое, что вы должны сделать, это установить цемент основы: Работа с цементной рамкой-это работа с рамкой CLI, на которой вы можете развернуть свое приложение.

интерфейс командной строки приложения :

interface.py

 from cement.core.foundation import CementApp
 from cement.core.controller import CementBaseController, expose
 from YourApp import yourApp

 class Meta:
    label = 'base'
    description = "your application description"
    arguments = [
        (['-r' , '--run'],
          dict(action='store_true', help='Run your application')),
        (['-v', '--version'],
          dict(action='version', version="Your app version")),
        ]
        (['-s', '--stop'],
          dict(action='store_true', help="Stop your application")),
        ]

    @expose(hide=True)
    def default(self):
        if self.app.pargs.run:
            #Start to running the your app from there !
            YourApp.yourApp()
        if self.app.pargs.stop:
            #Stop your application
            YourApp.yourApp.stop()

 class App(CementApp):
       class Meta:
       label = 'Uptime'
       base_controller = 'base'
       handlers = [MyBaseController]

 with App() as app:
       app.run()

YourApp.py класс:

 import threading

 class yourApp:
     def __init__:
        self.loger = log_exception.exception_loger()
        thread = threading.Thread(target=self.start, args=())
        thread.daemon = True
        thread.start()

     def start(self):
        #Do every thing you want
        pass
     def stop(self):
        #Do some things to stop your application

имейте в виду, что ваше приложение должно работать на поток демон

чтобы запустить приложение, просто сделать это в командной строке

python interface.py -помогите!--3-->