Python daemon and systemd service

I have a simple Python script working as a daemon. I am trying to create a systemd script to be able to run this script at run time.

Current systemd script:

[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 __name__ == '__main__': with daemon.DaemonContext(): check = Node() check.run() 

run contains while True loop.

I am trying to start this service using systemctl start zebra-node.service . Unfortunately, the service never finished indicating the sequence - I have to press Ctrl + C. The script is running, but the status is activated and after a while changes to deactivating. I am using python-daemon now (but before I tried without it, the symptoms were similar).

Should I implement some additional functions in my script or is the systemd file incorrect?

+67
python systemd python-daemon
Oct 25 '12 at 13:25
source share
5 answers

The reason that it does not complete the startup sequence is because for forking Type, your startup process must end and end (see $ Man systemd.service - branching search).

Just use only the main process, do not demonize

One option is to do less. When using systemd, there is often no need to create daemons, and you can directly run the code without demonization.

 #!/usr/bin/python -u from somewhere import Node check = Node() check.run() 

This allows you to use a simpler type of service called simple to make your module file look like this.

 [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 

Note that -u in python shebang is not necessary, but in case you print something to stdout or stderr, -u ensures that output buffering is not in place and the printed lines are immediately intercepted by systemd and written to magazine. Without him, this would have appeared with some delay.

To do this, I added the StandardOutput=syslog and StandardError=syslog lines to the module file. If you do not need printed materials in your journal, do not worry about these lines (they do not have to be present).

systemd makes demonization obsolete

Although the title of your question clearly asks for demonization, I think the essence of the question is β€œhow to make my service work,” and although using the main process seems a lot easier (you don’t need to take care of the daemons at all), it can be considered the answer to your question.

I think that many people use demonization only because "everyone does it." When using systemd, demonization reasons are often obsolete. There may be several reasons to use demonization, but now it will be a rare case.

EDIT: fixed python -p to the correct python -u . thanks kmftzg

+98
May 12 '15 at 11:29
source share

You can demonize, as described by the Shnuki and Amita. But with systemd this is not necessary. There are two ways to initialize the daemon: socket activation and explicit notification using sd_notify ().

Socket activation works for daemons that want to listen on a network port or UNIX socket or similar. Systemd will open the socket, listen to it, and then start the daemon when connected. This is the preferred option because it gives great flexibility to the administrator. [1] and [2] provide a good introduction, [3] describe the C API, and [4] describe the 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

Explicit notification means that the daemon opens the sockets themselves and / or performs any other initialization, and then notifies init that it is ready and can handle requests. This can be implemented using the "forking" protocol, but in fact, it is best to send a systemd notification using sd_notify (). The Python package is called systemd.daemon.notify and will be a single line to use [5].

[5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify

In this case, the unit file will have Type = notify and call systemd.daemon.notify ("READY = 1") after it has installed the sockets. No branching or demonization required.

+21
Jan 27 '14 at 18:28
source share

You are not creating a PID file.

systemd expects your program to write its PID in /var/run/zebra.pid . Since you are not doing this, systemd probably thinks your program is failing, so deactivate it from here.

To add a PID file, install lockfile and change the code to this:

 import daemon import daemon.pidlockfile pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid") with daemon.DaemonContext(pidfile=pidfile): check = Node() check.run() 

(Quick note: a recent lockfile update changed its API and made it incompatible with python-daemon. To fix it, edit daemon/pidlockfile.py , remove LinkFileLock from the import, and add from lockfile.linklockfile import LinkLockFile as LinkFileLock .)

Beware of one more thing: DaemonContext changes the working directory of your program to / , making the WorkingDirectory your service file useless. If you want DaemonContext in chdir to another directory, use DaemonContext(pidfile=pidfile, working_directory="/path/to/dir") .

+14
Oct 26 '12 at 7:25
source share

Also, when creating DaemonContext() you will most likely need to set daemon_context=True .

This is because if python-daemon detects that if it is running on the init system, it does not disconnect from the parent process. systemd expects the daemon process working with Type=forking to do this. Therefore, you need it, otherwise systemd will continue to wait and finally kill the process.

If you're interested, in the daemon module of python-daemon you will see this code:

 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 

Hope this explains better.

+3
Jul 25 '13 at 2:57
source share

I came across this question when trying to convert some init.d python services to systemd under CentOS 7. This seems to work fine for me by placing this file in /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 

Then I deleted my old init.d service file from /etc/init.d and ran sudo systemctl daemon-reload to reboot systemd.

I wanted my service to restart automatically, so restart options. I also found that using idle for Type makes more sense than simple .

Downtime is very similar to downtime; however, the actual execution of the service binary is delayed until all active jobs are submitted. This can be used to avoid alternating the output of shell services with the output of status on the console.

Read more about the options I used here .

I also experimented with maintaining the old service and the ability to reinstall the systemd service, but I ran into some problems.

 [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 

The problems I ran into were that the init.d script service was used instead of the systemd service, if both were named the same. If you killed the init.d initiated process, the systemd script will then go back in time. But if you run service <service-name> stop , this will refer to the old init.d service. So I found that the best way is to drop the old init.d service and the service command mentioned instead of the systemd service.

Hope this helps!

+1
Feb 08 '17 at 2:07 on
source share



All Articles