単純なPythonデーモンとして動作するスクリプトがあります。起動中にこのスクリプトを開始できるようにsystemdスクリプトを作成しようとしています。
現在のsystemdスクリプト:
[Unit]
Description=Text
After=syslog.target
[Service]
Type=forking
User=node
Group=node
WorkingDirectory=/home/node/Node/
PIDFile=/var/run/zebra.pid
ExecStart=/home/node/Node/node.py
[Install]
WantedBy=multi-user.target
node.py:
if __== '__main__':
with daemon.DaemonContext():
check = Node()
check.run()
run
にはwhile True
ループが含まれます。
systemctl start zebra-node.service
でこのサービスを実行しようとしました。残念ながら、サービスはシーケンスの記述を完了していません-Ctrl + Cを押す必要があります。スクリプトは実行中ですが、ステータスはアクティブ化中で、しばらくすると非アクティブ化に変わります。今、私はpython-daemonを使用しています(ただし、それを試してみる前は症状は似ていました)。
スクリプトにいくつかの追加機能を実装する必要がありますか、またはsystemdファイルが正しくありませんか?
理由は、スタートアップシーケンスを完了しないためです。Typeforking
の場合、スタートアッププロセスは分岐して終了することが予想されます($ man systemd.service-分岐の検索を参照)。
1つのオプションは、実行回数を減らすことです。 systemdでは、多くの場合、デーモンを作成する必要はなく、デーモン化せずにコードを直接実行できます。
#!/usr/bin/python -u
from somewhere import Node
check = Node()
check.run()
これにより、simple
と呼ばれるより単純なタイプのサービスを使用できるため、ユニットファイルは次のようになります。
[Unit]
Description=Simplified simple zebra service
After=syslog.target
[Service]
Type=simple
User=node
Group=node
WorkingDirectory=/home/node/Node/
ExecStart=/home/node/Node/node.py
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target
-u
in python Shebangは必要ありませんが、stdoutまたはstderrに何かを出力する場合、-u
は、出力バッファリングが適所にあり、印刷された行はすぐにsystemdによってキャッチされ、ジャーナルに記録されます。
このために、ユニットファイルにStandardOutput=syslog
行とStandardError=syslog
行を追加しました。ジャーナルの印刷出力を気にしない場合は、これらの行を気にしないでください(存在する必要はありません)。
systemd
はデーモン化を廃止しますあなたの質問のタイトルは明示的にデーモン化について尋ねていますが、質問の核心は「私のサービスを実行する方法」とメインプロセスを使用する方がはるかに簡単です(あなたは気にする必要はありません)デーモンについて)、それはあなたの質問への答えと考えることができます。
「誰もがやる」という理由だけで、多くの人がデーモン化を使用していると思います。 systemdでは、デーモン化の理由はしばしば時代遅れです。デーモン化を使用する理由はいくつかありますが、現在ではまれなケースです。
編集:python -p
を適切なpython -u
に修正しました。ありがとうkmftzg
SchnoukiやAmitのようにデーモン化することは可能です。しかし、systemdではこれは必要ありません。デーモンを初期化するより良い方法が2つあります。sd_notify()を使用したソケットのアクティブ化と明示的な通知です。
ソケットのアクティブ化は、ネットワークポートまたはUNIXソケットなどでリッスンするデーモンに対して機能します。 Systemdはソケットを開いてリッスンし、接続が入ったときにデーモンを起動します。これは、管理者に最も柔軟性を与えるため、推奨されるアプローチです。 [1]と[2]はニースの紹介、[3]はC APIの説明、[4]はPython API。
[1] http://0pointer.de/blog/projects/socket-activation.html
[2] http://0pointer.de/blog/projects/socket-activation2.html
[3] http://www.freedesktop.org/software/systemd/man/sd_listen_fds.html
[4] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.listen_fds
明示的な通知とは、デーモンがソケット自体を開き、および/または他の初期化を行い、その後、準備ができてリクエストを処理できることをinitに通知することです。これは「forking protocol」を使用して実装できますが、実際には、sd_notify()を使用してsystemdに通知を送信する方が適切です。 Pythonラッパーはsystemd.daemon.notifyと呼ばれ、[5]を使用する1行になります。
[5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify
この場合、ユニットファイルにはType = notifyがあり、ソケットを確立した後にsystemd.daemon.notify( "READY = 1")を呼び出します。フォークやデーモン化は必要ありません。
PIDファイルを作成していません。
systemdは、プログラムが_/var/run/zebra.pid
_にPIDを書き込むことを期待しています。あなたがそれをしないので、systemdはおそらくあなたのプログラムが失敗していると考えて、それでそれを無効にします。
PIDファイルを追加するには、 lockfile をインストールし、コードを次のように変更します。
_import daemon
import daemon.pidlockfile
pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid")
with daemon.DaemonContext(pidfile=pidfile):
check = Node()
check.run()
_
(クイックノート:lockfile
の最近の更新によりAPIが変更され、python-daemonとの互換性がなくなりました。修正するには、_daemon/pidlockfile.py
_を編集し、インポートからLinkFileLock
を削除し、 _from lockfile.linklockfile import LinkLockFile as LinkFileLock
_。)
別のことに注意してください:DaemonContext
は、プログラムの作業ディレクトリを_/
_に変更し、サービスファイルのWorkingDirectory
を使用不能にします。 DaemonContext
を別のディレクトリにchdirしたい場合は、DaemonContext(pidfile=pidfile, working_directory="/path/to/dir")
を使用します。
また、DaemonContext()
を作成するときにdaemon_context=True
を設定する必要があります。
これは、python-daemon
がinitシステムで実行されている場合、親プロセスから切り離されないことを検出した場合です。 systemd
は、Type=forking
で実行されているデーモンプロセスがそうすることを期待しています。したがって、それが必要です。そうでなければ、systemd
が待機し続け、最終的にプロセスを強制終了します。
興味がある場合は、python-daemon
のデーモンモジュールに次のコードが表示されます。
def is_detach_process_context_required():
""" Determine whether detaching process context is required.
Return ``True`` if the process environment indicates the
process is already detached:
* Process was started by `init`; or
* Process was started by `inetd`.
"""
result = True
if is_process_started_by_init() or is_process_started_by_superserver():
result = False
うまくいけば、これがもっとよく説明できます。
いくつかのpython init.dサービスをCentOS 7でsystemdに変換しようとしたときにこの質問に出会いました。これは、このファイルを/etc/systemd/system/
:
[Unit]
Description=manages worker instances as a service
After=multi-user.target
[Service]
Type=idle
User=node
ExecStart=/usr/bin/python /path/to/your/module.py
Restart=always
TimeoutStartSec=10
RestartSec=10
[Install]
WantedBy=multi-user.target
次に、古いinit.dサービスファイルを/etc/init.d
とSudo systemctl daemon-reload
systemdをリロードします。
サービスを自動的に再起動したかったため、再起動オプションがありました。また、idle
にType
を使用すると、simple
よりも意味があります。
アイドルの動作は単純に非常に似ています。ただし、すべてのアクティブなジョブがディスパッチされるまで、サービスバイナリの実際の実行は遅延します。これは、シェルサービスの出力とコンソールのステータス出力のインターリーブを回避するために使用できます。
使用したオプションの詳細 here 。
また、古いサービスを保持し、systemdでサービスを再起動することを試みましたが、いくつかの問題に遭遇しました。
[Unit]
# Added this to the above
#SourcePath=/etc/init.d/old-service
[Service]
# Replace the ExecStart from above with these
#ExecStart=/etc/init.d/old-service start
#ExecStop=/etc/init.d/old-service stop
私が経験した問題は、両方に同じ名前が付けられている場合、systemdサービスの代わりにinit.dサービススクリプトが使用されることでした。 init.dが開始したプロセスを強制終了すると、systemdスクリプトが引き継ぎます。ただし、service <service-name> stop
古いinit.dサービスを参照します。そのため、古いinit.dサービスを削除し、代わりにsystemdサービスを参照するserviceコマンドを削除することが最善の方法であることがわかりました。
お役に立てれば!