Bagaimana Anda membuat daemon dengan Python?

244

Pencarian di Google mengungkapkan cuplikan kode x2. Hasil pertama adalah resep kode ini yang memiliki banyak dokumentasi dan penjelasan, bersama dengan beberapa diskusi yang bermanfaat di bawahnya.

Namun, sampel kode lain , walaupun tidak mengandung begitu banyak dokumentasi, termasuk kode sampel untuk meneruskan perintah seperti mulai, berhenti dan mulai ulang. Ini juga membuat file PID yang berguna untuk memeriksa apakah daemon sudah berjalan dll.

Kedua sampel ini menjelaskan cara membuat daemon. Adakah hal tambahan yang perlu dipertimbangkan? Apakah satu sampel lebih baik daripada yang lain, dan mengapa?

davidmytton
sumber
1
Saya selalu menemukan kode daemonisasi tidak diperlukan. Mengapa tidak biarkan saja shell yang melakukannya?
emil.p.stanchev
17
Karena itu tidak melakukan setsid atau setpgrp.
bmargulies
4
Gunakan supervisord.org . Dengan cara ini Anda tidak perlu melakukan fork () atau mengalihkan Anda stdin / stderr. Tulis saja program normal.
guettli

Jawaban:

169

Solusi saat ini

Implementasi referensi PEP 3143 (pustaka proses daemon standar) sekarang tersedia sebagai python-daemon .

Jawaban historis

Contoh kode Sander Marechal lebih unggul daripada yang asli, yang awalnya diposting pada tahun 2004. Saya pernah berkontribusi daemonizer untuk Pyro, tetapi mungkin akan menggunakan kode Sander jika saya harus melakukannya lagi.

Jeff Bauer
sumber
72
Sunting: Karena saya awalnya memposting balasan ini, implementasi referensi PEP 3143 sekarang tersedia: pypi.python.org/pypi/python-daemon
Jeff Bauer
@JeffBauer Tautan asli telah mati, saya ingat itu berguna, Anda tidak akan tahu tautan langsung untuk itu, bukan?
CrazyCasta
1
@CrazyCasta: Versi Sander Marechal masih tersedia di Wayback Machine
Jeff Bauer
1
@ Jeff Jauer: Kode Sander masih lebih baik daripada http://pypi.python.org/pypi/python-daemon. Lebih terpercaya. Hanya satu contoh: cobalah memulai dua kali daemon yang sama dengan python-daemon: kesalahan besar jelek. Dengan kode Sander: pemberitahuan yang bagus "Daemon sudah berjalan."
Basj
2
Karena dokumentasi modul "python-daemon" masih hilang (lihat juga banyak pertanyaan SO lainnya) dan agak tidak jelas (bagaimana memulai / menghentikan dengan benar daemon dari baris perintah dengan modul ini?), Saya memodifikasi contoh kode Sander Marechal untuk ditambahkan quit()metode yang dijalankan sebelum daemon dihentikan. Ini dia.
Basj
163

Ada banyak hal yang perlu diperhatikan ketika menjadi proses daemon yang berperilaku baik :

  • mencegah core dumps (banyak daemon dijalankan sebagai root, dan core dumps dapat berisi informasi sensitif)

  • berperilaku benar di dalam chrootpenjara

  • mengatur UID, GID, direktori kerja, umask, dan parameter proses lainnya secara tepat untuk use case

  • melepaskan ditinggikan suid, sgidhak istimewa

  • tutup semua deskriptor file terbuka, dengan pengecualian tergantung pada use case

  • berperilaku benar jika mulai dalam konteks yang sudah terpisah, seperti init, inetd, dll

  • mengatur penangan sinyal untuk perilaku daemon yang masuk akal, tetapi juga dengan penangan spesifik yang ditentukan oleh use case

  • mengarahkan aliran standar stdin, stdout, stderrkarena proses daemon tidak lagi memiliki terminal pengendali

  • menangani file PID sebagai kunci penasehat koperasi, yang merupakan keseluruhan kaleng cacing itu sendiri dengan banyak cara yang bertentangan tetapi valid untuk berperilaku

  • memungkinkan pembersihan yang tepat ketika proses dihentikan

  • sebenarnya menjadi proses daemon tanpa mengarah ke zombie

Beberapa di antaranya adalah standar , seperti yang dijelaskan dalam literatur Unix kanonik ( Pemrograman Lanjutan di Lingkungan UNIX , oleh almarhum W. Richard Stevens, Addison-Wesley, 1992). Lainnya, seperti aliran redirection dan penanganan file PID , adalah perilaku konvensional yang diharapkan sebagian besar pengguna daemon tetapi kurang standar.

Semua ini dicakup oleh spesifikasi “Perpustakaan proses daemon standar” PEP 3143 . The python-daemon implementasi referensi bekerja pada Python 2,7 atau lambat, dan Python 3.2 atau yang lebih baru.

hidung besar
sumber
26
"Gaol" dieja dengan benar, karena itulah cara W. Richard Stevens mengeja :-)
bignose
7
Gaol adalah hal yang berbahasa Inggris . Poster itu dari Australia jadi masuk akal.
devin
1
Adakah rencana untuk membuat versi ramah py3k?
Tim Tisdall
97

Inilah daemon Python dasar 'Howdy World' yang saya mulai dengan, ketika saya sedang mengembangkan aplikasi daemon baru.

#!/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()

Perhatikan bahwa Anda membutuhkan python-daemonpustaka. Anda dapat menginstalnya dengan:

pip install python-daemon

Maka mulailah saja dengan ./howdy.py start, dan hentikan dengan ./howdy.py stop.

Dustin Kirkland
sumber
5
Itu daemonmodul Anda mengimpor bukan merupakan bagian standar dari Python (belum). Perlu diinstal dengan pip install python-daemonatau setara.
Nate
6
Saya memasang python-daemon seperti yang Anda gambarkan, tetapi ketika saya mencoba menjalankan aplikasi saya (sama seperti 3 baris terakhir Anda), saya mendapatkan ImportError: tidak dapat mengimpor nama pelari
Nostradamnit
Bisakah Anda memeriksa apakah sudah dipasang dengan benar? $ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
Dustin Kirkland
4
Saran ini tampaknya sudah usang - pada September 2013, bagaimanapun, python.org/dev/peps/pep-3143 tidak menyebutkan "pelari" yang dapat diimpor. Ini tentu saja akan menjelaskan pengamatan @ Nostradamnit.
offby1
2
Ini masih berfungsi dengan baik bagi saya, pada bulan September 2013, di Ubuntu 13.04, dengan paket stock Python, python2.7 dan python-daemon diinstal. Namun, dengan python3, saya melihat kesalahan, "dari daemon import runner ImportError: Tidak ada modul bernama 'daemon'"
Dustin Kirkland
42

Perhatikan paket python-daemon yang memecahkan banyak masalah di belakang daemon di luar kotak.

Di antara fitur-fitur lain yang dimungkinkan untuk (dari deskripsi paket Debian):

  • Lepaskan proses ke dalam grup prosesnya sendiri.
  • Tetapkan lingkungan proses yang sesuai untuk berjalan di dalam chroot.
  • Tinggalkan hak istimewa suid dan sgid.
  • Tutup semua deskriptor file yang terbuka.
  • Ubah direktori kerja, uid, gid, dan umask.
  • Atur penangan sinyal yang sesuai.
  • Buka deskriptor file baru untuk stdin, stdout, dan stderr.
  • Kelola file kunci PID yang ditentukan.
  • Daftarkan fungsi pembersihan untuk pemrosesan saat keluar.
Viliam
sumber
35

Alternatif - buat program Python normal yang tidak di-daemonisasi, kemudian ubah secara eksternal menggunakan supervisord . Ini dapat menghemat banyak sakit kepala, dan * nix- dan bahasa-portabel.

Chris Johnson
sumber
1
Saya pikir ini adalah cara terbaik. Terutama jika Anda ingin menjalankan beberapa daemon pada satu sistem operasi. Jangan kode, gunakan kembali.
guettli
Ini menyederhanakan banyak masalah. Saya telah menulis daemon sejati - itu tidak mudah.
Chris Johnson
1
Jawaban terbaik disembunyikan di sini :)
kawing-chiu
1
Ini emas. Setelah menghabiskan berjam-jam mencoba menjalankan melalui python-daemon, ini adalah solusi out of the box yang bekerja untuk saya. Dokumentasi dan contoh yang bagus membuat daemon saya aktif dan berjalan dalam beberapa menit.
Nikhil Sahu
17

Mungkin bukan jawaban langsung untuk pertanyaan, tetapi systemd dapat digunakan untuk menjalankan aplikasi Anda sebagai daemon. Berikut ini sebuah contoh:

[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

Saya lebih suka metode ini karena banyak pekerjaan yang dilakukan untuk Anda, dan kemudian skrip daemon Anda berperilaku serupa dengan seluruh sistem Anda.

-Orby

Luke Dupin
sumber
Ini adalah cara yang tepat dan waras. 1) Perlu disimpan ke /etc/systemd/system/control.service 2) dikelola sudosystemctl start control.service
jimper
7

YapDi adalah modul python yang relatif baru yang muncul di Hacker News. Terlihat cukup berguna, dapat digunakan untuk mengubah skrip python menjadi mode daemon dari dalam skrip.

Sergey R
sumber
6

karena python-daemon belum mendukung python 3.x, dan dari apa yang bisa dibaca di milis, mungkin tidak akan pernah, saya telah menulis implementasi baru PEP 3143: pep3143daemon

pep3143daemon harus mendukung setidaknya python 2.6, 2.7 dan 3.x

Ini juga berisi kelas PidFile.

Perpustakaan hanya tergantung pada perpustakaan standar dan pada modul enam.

Ini dapat digunakan sebagai pengganti drop untuk python-daemon.

Ini dokumentasinya .

stephan schultchen
sumber
6

Fungsi ini akan mengubah aplikasi menjadi daemon:

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())
Ivan Kolesnikov
sumber
5

Saya khawatir modul daemon yang disebutkan oleh @Dustin tidak bekerja untuk saya. Sebagai gantinya saya menginstal python-daemon dan menggunakan kode berikut:

# 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.

Berlari itu mudah

> python myDaemon.py

hanya untuk kelengkapan di sini adalah konten direktori samplemodule

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

Konten moduleclass.py bisa

class moduleclass():
    ...

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

Satu hal lagi yang perlu dipikirkan ketika melakukan daemonisasi dengan python:

Jika Anda menggunakan python logging dan Anda ingin terus menggunakannya setelah melakukan daemonisasi, pastikan untuk memanggil close()penangan (terutama penangan file).

Jika Anda tidak melakukan ini, pawang masih dapat berpikir bahwa ia memiliki file yang terbuka, dan pesan Anda akan hilang begitu saja - dengan kata lain pastikan logger mengetahui file-nya ditutup!

Ini mengasumsikan ketika Anda daemonise Anda menutup SEMUA deskriptor file terbuka tanpa pandang bulu - alih-alih Anda bisa mencoba menutup semua kecuali file log (tapi biasanya lebih mudah untuk menutup semua lalu buka kembali yang Anda inginkan).

Matthew Wilcoxson
sumber
Apakah Anda pikir membuka pengendali penebangan baru lebih baik daripada meneruskan pengendali penebangan ke daemon menggunakan opsi files_preserve DaemonContext misalnya?
HeyWatchIni
Anda hanya menutup logger, Anda tidak membuat yang baru (itu hanya akan membuka kembali ketika perlu). Tetapi meskipun sangat mudah untuk melakukan itu, mungkin lebih baik menggunakan DaemonContext karena mungkin melakukan beberapa hal pintar lainnya (dengan asumsi melestarikan masih memungkinkan daemonisasi yang tepat).
Matthew Wilcoxson
2

Meskipun Anda mungkin lebih suka solusi Python murni yang disediakan oleh modul python-daemon, ada daemon(3)fungsi di libc- setidaknya, pada BSD dan Linux - yang akan melakukan hal yang benar.

Memanggilnya dari python itu mudah:

import ctypes

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

Satu-satunya hal yang tersisa untuk dilakukan adalah membuat (dan mengunci) file-PID. Tetapi Anda dapat menangani diri sendiri ...

Mikhail T.
sumber
1

Saya memodifikasi beberapa baris dalam contoh kode Sander Marechal (disebutkan oleh @JeffBauer dalam jawaban yang diterima ) untuk menambahkan quit()metode yang dijalankan sebelum daemon dihentikan. Ini terkadang sangat berguna.

Ini dia.

Catatan: Saya tidak menggunakan modul "python-daemon" karena dokumentasinya masih hilang (lihat juga banyak pertanyaan SO lainnya) dan agak tidak jelas (bagaimana memulai / menghentikan dengan benar daemon dari baris perintah dengan modul ini?)

Basj
sumber
-1

Setelah beberapa tahun dan banyak upaya (saya mencoba semua jawaban yang diberikan di sini, tetapi semuanya memiliki kelemahan kecil pada akhirnya), sekarang saya menyadari bahwa ada cara yang lebih baik daripada ingin memulai, berhenti, restart daemon langsung dari Python : gunakan alat OS sebagai gantinya.

Misalnya, untuk Linux, alih-alih melakukan python myapp startdan python myapp stop, saya melakukan ini untuk memulai aplikasi:

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

atau screen -dmS myapp python myapp.pyuntuk memulai dan melepaskannya dalam satu perintah .

Kemudian:

screen -r myapp

untuk melampirkan ke terminal ini lagi. Begitu sampai di terminal, dimungkinkan untuk menggunakan CTRL + C untuk menghentikannya.

Basj
sumber
-2

Cara termudah untuk membuat daemon dengan Python adalah dengan menggunakan kerangka kerja berbasis peristiwa Twisted . Ini menangani semua hal yang diperlukan untuk daemonisasi untuk Anda. Ini menggunakan Pola Reaktor untuk menangani permintaan bersamaan.

Travis B. Hartwell
sumber
5
Itu palu yang terlalu besar untuk digunakan. Kebanyakan orang hanya ingin menjalankan skrip Python pendek yang mereka tulis sebagai daemon. python-daemon, seperti dijelaskan di atas, adalah jawaban yang benar.
Tom Swirly
2
Meskipun jawaban ini cukup arogan, itu berguna.
fiatjaf
-28

80% dari waktu, ketika orang mengatakan "daemon", mereka hanya menginginkan server. Karena pertanyaannya benar-benar tidak jelas dalam hal ini, sulit untuk mengatakan apa kemungkinan domain jawaban itu. Karena server memadai, mulailah dari sana. Jika sebenarnya "daemon" benar-benar diperlukan (ini jarang terjadi), baca terus nohupsebagai cara untuk daemonisasi server.

Sampai saat daemon yang sebenarnya benar-benar diperlukan, cukup tulis server yang sederhana.

Lihat juga implementasi referensi WSGI .

Lihat juga Server HTTP Sederhana .

"Apakah ada hal tambahan yang perlu dipertimbangkan?" Ya. Sekitar sejuta hal. Protokol apa? Berapa banyak permintaan? Berapa lama untuk melayani setiap permintaan? Seberapa sering mereka akan tiba? Apakah Anda akan menggunakan proses khusus? Utas? Subproses? Menulis daemon adalah pekerjaan besar.

S.Lott
sumber
12
Tak satu pun dari perpustakaan itu yang melakukan satu fork(), apalagi dua. Mereka tidak ada hubungannya dengan daemonisasi.
Brandon Rhodes
8
Pada sistem operasi Unix, proses "daemon" - seperti pelayan udara yang oleh orang Yunani disebut "daemon" - adalah proses yang "berdiri di samping." Alih-alih langsung melayani satu pengguna melalui TTY pengguna itu, daemon bukan milik TTY, tetapi dapat menjawab permintaan dari banyak pengguna pada sistem, atau - seperti crondatau syslogd- melakukan layanan tata graha untuk seluruh sistem. Untuk membuat proses daemon, setidaknya seseorang harus melakukan double- fork()dengan semua deskriptor file ditutup, sehingga seseorang kebal terhadap sinyal dari semua terminal pengendali, termasuk konsol sistem. Lihat jawaban bignose.
Brandon Rhodes
5
@S Lott - “server” menggambarkan apa proses tidak (mendengarkan permintaan masuk bukannya memulai tindakan sendiri); "A daemon" menjelaskan bagaimana suatu proses berjalan (tanpa jendela atau terminal pengendali). SimpleHTTPServermemang merupakan server, tetapi server yang tidak tahu cara memonemonisasi dirinya sendiri (misalnya, Anda dapat Ctrl-C). nohupadalah utilitas untuk daemonize proses naif - sehingga server nohupped Anda memang baik daemon dan server, persis seperti yang Anda klaim. Pertanyaan Stack Overflow ini pada dasarnya bertanya: "Bagaimana saya bisa menerapkan nohupdengan Python?"
Brandon Rhodes
5
Ya itu tetapi pemahaman saya tentang pertanyaan OP adalah bahwa ia ingin melakukan daemonisasi dari dalam program python dan tanpa menggunakan sesuatu yang lain.
Noufal Ibrahim
4
@ S Lott - Anda tidak perlu terkesan! Penulis setiap jawaban lain tahu apa arti "daemon", jadi kemampuan saya untuk menafsirkan pertanyaan ini hampir tidak unik. :) Dan dari mana Anda mendapatkan gagasan bahwa saya ingin penulis menciptakan kembali roda? Saya pikir nohupini adalah alat yang baik, dan saya akan menghapus -1 suara saya jika Anda hanya memindahkan ide yang berguna ke dalam jawaban Anda yang sebenarnya. Bahkan, jika Anda menyebutkan supervisorddan bagaimana hal itu juga akan menyelamatkan penulis dari keharusan melakukan pencatatan, skrip start-stop, dan memulai kembali pembatasan, maka saya bahkan akan memberi Anda +1. :)
Brandon Rhodes