Python 데몬 및 systemd 서비스


78

데몬으로 작동하는 간단한 Python 스크립트가 있습니다. 시작 중에이 스크립트를 시작할 수 있도록 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 __name__ == '__main__':
    with daemon.DaemonContext():
        check = Node()
        check.run()

runwhile True루프를 포함 합니다.

이 서비스를 systemctl start zebra-node.service. 불행히도 서비스가 순서를 설명하는 것을 완료하지 못했습니다. Ctrl + C를 눌러야합니다. 스크립트가 실행 중이지만 상태가 활성화 중이며 잠시 후 비활성화 중으로 변경됩니다. 이제 나는 python-daemon을 사용하고 있습니다 (하지만 그것없이 시도하기 전에 증상은 비슷했습니다).

스크립트에 몇 가지 추가 기능을 구현해야합니까? 아니면 시스템 파일이 올바르지 않습니까?


답이 문제를 해결 했습니까? 그렇지 않은 경우 DaemonContext ()를 만들 때 daemon_context = True 설정을 시도합니다. 작동 할 수 있습니다.

1
@pawelbial 유감입니다. Python 코드 예제가 완전하지 않기 때문에 (가져 오기가 누락 daemon되고 Node출처가 명확하지 않음 ) 상황을 재현하는 것이 쉽지 / 가능하지 않습니다.
Jan Vlcinsky

@pawelbial 이것은 질문과 간접적으로 관련되어 있지만 도움이 될 수 있습니다 : unix.stackexchange.com/a/226853/33386
Jonathan Komar

답변:


116

시작 순서가 완료되지 않는 이유는 Type forkingyour startup process가 fork하고 종료 할 것으로 예상되기 때문입니다 ($ man systemd.service-forking 검색 참조).

기본 프로세스 만 사용하고 데몬 화하지 마십시오.

한 가지 옵션은 덜하는 것입니다. 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파이썬 오두막에서이 필요하지 않지만 경우에 당신은 표준 출력 또는 표준 오류에 뭔가를 출력의 -u차종은 물론, 즉시 systemd에 의해 체포 및 저널에 기록됩니다 장소 및 인쇄 라인에는 출력 버퍼링이 없습니다. 그것 없이는 약간의 지연으로 나타날 것입니다.

이를 위해 단위 파일에 라인 StandardOutput=syslogStandardError=syslog. 저널에 인쇄 된 출력물에 관심이 없다면이 줄은 신경 쓰지 마십시오 (존재할 필요는 없습니다).

systemd 데몬 화를 쓸모 없게 만든다

질문의 제목은 데몬 화에 대해 명시 적으로 묻지 만, 질문의 핵심은 "내 서비스를 실행하는 방법"이고 메인 프로세스사용하는 것이 훨씬 간단 해 보이지만 (데몬에 대해 전혀 신경 쓸 필요가 없습니다) 귀하의 질문에 대한 답변으로 간주 될 수 있습니다.

많은 사람들이 "모두가 그렇게하기 때문에"데몬 화를 사용한다고 생각합니다. systemd를 사용하면 데몬 화의 이유는 종종 쓸모가 없습니다. 데몬 화를 사용하는 데는 몇 가지 이유가있을 수 있지만 지금은 드문 경우입니다.

편집 : python -p적절한 python -u. 감사합니다 kmftzg


4
데몬 화하는 이유는 systemd를 사용하지 않는 다른 플랫폼을 지원하기 위해서입니다. systemd에 대해 별도의 코드 경로를 만들어야하는 것은 systemd가 이식성을 방해하는 또 다른 방법입니다.
Nick Bastin

2
@NickBastin 및 이것은 이식성이 진행 및 단순화를 방해하는 또 다른 방법입니다.
intelfx

2
@NickBastin systemd는 forking 응용 프로그램을 지원하므로 원하지 않는 경우 systemd에 대한 별도의 코드 경로가 필요하지 않습니다. 문서를 읽지 않는 것은 실용적인 기술을 저해하고 근거없는 의견을 만들 수 있습니다.
Jan Vlcinsky

4
@NickBastin OP는 "간단한 파이썬 스크립트"와 "systemd"의 사용에 대해 이야기합니다. 비 systemd 플랫폼 이식성에 대한 요청은 없습니다. 이러한 요청은 systemd를 비난하는 귀하의 반응에만 존재합니다.
Jan Vlcinsky

1
supervisord 또는 pm2와 같은 다른 프로세스 관리 도구가 있습니다. 나는 "not-to-daemonize"가 분명히 일을하는 유닉스 방식이라고 생각한다.
ospider

23

Schnouki와 Amit이 설명하는 것처럼 데몬 화하는 것이 가능합니다. 그러나 systemd에서는 이것이 필요하지 않습니다. 데몬을 초기화하는 더 좋은 방법은 소켓 활성화와 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에 준비가되었으며 요청을 처리 할 수 ​​있음을 알립니다. 이것은 "포킹 프로토콜"로 구현할 수 있지만 실제로 sd_notify ()를 사용하여 systemd에 알림을 보내는 것이 더 좋습니다. 파이썬 래퍼는 systemd.daemon.notify라고하며 [5]를 사용하는 한 줄이됩니다.

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

이 경우 유닛 파일은 Type = notify를 가지며 소켓을 설정 한 후 systemd.daemon.notify ( "READY = 1")를 호출합니다. 분기 나 데몬 화가 필요하지 않습니다.


좋아 보인다. systemd.daemonpip를 통해 제공하는이 파이썬 라이브러리를 설치하는 방법은 무엇입니까?
guettli

github.com/systemd/python-systemd#installation의 공식 설치 지침은 pip로 설치하는 방법을 보여줍니다. 작동하지 않는 경우 github.com/systemd/python-systemd/issues 에서 문제를 제출하세요 .
zbyszek

15

PID 파일을 생성하지 않습니다.

systemd는 프로그램이 PID를 /var/run/zebra.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").


2
DaemonContext프로그램의 작업 디렉토리를 변경하여 내 데몬 문제를 해결 하는 방법에 대한 마지막 단락
DJG

2
코드가 적을수록 더 많기 때문에 "단순히 메인 프로세스 만 사용하고 데몬 화하지 마십시오"라는 대답을 선호합니다.
guettli

API가 Python3에서 변경된 경우 idk. 그러나이 있어야 import daemon.pidfile하지import daemon.pidlockfile
reox

잠금이 제거되면 데몬이 완료된 경우에도 항상 잠금 상태로 유지됩니다.
alper

4

또한 daemon_context=True생성 할 때 설정해야 할 가능성이 가장 높습니다 .DaemonContext() .

이는 python-daemoninit 시스템에서 실행 중이면 부모 프로세스에서 분리되지 않기 때문입니다. 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

이것이 더 잘 설명되기를 바랍니다.


2
내가 잘못 될 수있다,하지만 난이 플래그가 "detach_process"하지하지 "daemon_context"라고 생각
피터 터너

4

일부 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 서비스 파일을 삭제하고 systemd를 다시로드하기 위해 /etc/init.d실행 sudo systemctl daemon-reload했습니다.

내 서비스가 자동으로 다시 시작되기를 원했기 때문에 다시 시작 옵션이 필요했습니다. 나는 또한 idlefor 사용 Typesimple.

유휴 동작은 단순함과 매우 유사합니다. 그러나 서비스 바이너리의 실제 실행은 모든 활성 작업이 발송 될 때까지 지연됩니다. 이것은 콘솔의 상태 출력과 쉘 서비스 출력의 인터리빙을 피하기 위해 사용될 수 있습니다.

여기에서 사용한 옵션에 대한 자세한 내용 .

나는 또한 오래된 서비스를 유지하고 서비스를 체계화하는 실험을했지만 몇 가지 문제가 발생했습니다.

[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 서비스를 참조했습니다.

도움이 되었기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.