Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 3143 – 표준 데몬 프로세스 라이브러리

Author:
Ben Finney <ben+python at benfinney.id.au>
Status:
Deferred
Type:
Standards Track
Created:
26-Jan-2009
Python-Version:
3.x
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

잘 동작하는 Unix 데몬이 되도록 프로그램을 작성하는 일은 다소 복잡하고 올바르게 해내기 까다롭지만, 프로그램이 그 밖에 무엇을 해야 하든 그 단계는 어떤 데몬이든 대체로 비슷합니다.

이 PEP는 데몬 프로세스가 되는 작업에 대한 간단한 인터페이스를 제공하는 패키지를 Python 표준 라이브러리에 도입합니다.

PEP 보류

이 PEP에서 다루는 개념에 대한 추가 탐구는, PEP의 목표를 추진하고 피드백을 수집하여 반영하는 데 관심이 있으면서 이를 효과적으로 해낼 충분한 시간을 가진 주도자가 현재 없기 때문에 보류되었습니다.

명세

사용 예

DaemonContext를 직접 사용하는 간단한 예:

import daemon

from spam import do_main_program

with daemon.DaemonContext():
    do_main_program()

더 복잡한 사용 예:

import os
import grp
import signal
import daemon
import lockfile

from spam import (
    initial_program_setup,
    do_main_program,
    program_cleanup,
    reload_program_config,
    )

context = daemon.DaemonContext(
    working_directory='/var/lib/foo',
    umask=0o002,
    pidfile=lockfile.FileLock('/var/run/spam.pid'),
    )

context.signal_map = {
    signal.SIGTERM: program_cleanup,
    signal.SIGHUP: 'terminate',
    signal.SIGUSR1: reload_program_config,
    }

mail_gid = grp.getgrnam('mail').gr_gid
context.gid = mail_gid

important_file = open('spam.data', 'w')
interesting_file = open('eggs.data', 'w')
context.files_preserve = [important_file, interesting_file]

initial_program_setup()

with context:
    do_main_program()

인터페이스

새 패키지 daemon이 표준 라이브러리에 추가됩니다.

데몬 프로세스로 실행되는 프로그램의 설정과 프로세스 컨텍스트를 나타내기 위해 DaemonContext 클래스가 정의됩니다.

DaemonContext 객체

DaemonContext 인스턴스는 프로그램이 데몬이 될 때의 동작 설정과 프로세스 컨텍스트를 나타냅니다. 동작과 환경은 open 메서드를 호출하기 전에 인스턴스에 옵션을 설정하여 사용자 정의합니다.

각 옵션은 DaemonContext 생성자에 키워드 인자로 전달하거나, open을 호출하기 전이라면 언제든지 인스턴스의 속성에 대입하여 나중에 변경할 수 있습니다. 즉, wibble과 wubble이라는 이름의 옵션에 대해 다음 호출은:

foo = daemon.DaemonContext(wibble=bar, wubble=baz)
foo.open()

다음과 동등합니다.:

foo = daemon.DaemonContext()
foo.wibble = bar
foo.wubble = baz
foo.open()

다음 옵션들이 정의되어 있습니다.

files_preserve
Default:
None

데몬을 시작할 때 닫지 않아야 하는 파일의 목록입니다. None이면 열려 있는 모든 파일 디스크립터가 닫힙니다.

목록의 요소는 (파일 객체의 fileno() 메서드가 반환하는) 파일 디스크립터이거나 Python file 객체입니다. 각 요소는 데몬 시작 중에 닫지 않을 파일을 지정합니다.

chroot_directory
Default:
None

프로세스의 유효 루트 디렉터리로 설정할 디렉터리의 전체 경로입니다. None이면 루트 디렉터리를 변경하지 않음을 지정합니다.

working_directory
Default:
'/'

데몬 시작 시 프로세스가 전환해야 하는 작업 디렉터리의 전체 경로입니다.

프로세스의 현재 작업 디렉터리가 어떤 파일 시스템에 있으면 그 파일 시스템을 마운트 해제할 수 없으므로, 이 값은 기본값으로 두거나 데몬이 실행되는 동안 합리적인 “홈 디렉터리”가 되는 디렉터리로 설정해야 합니다.

umask
Default:
0

데몬 시작 시 프로세스에 설정할 파일 접근 생성 마스크(“umask”)입니다.

프로세스는 부모 프로세스로부터 umask를 상속하므로, 데몬을 시작하면 umask를 이 값으로 재설정하여 데몬이 예상하는 접근 모드로 파일을 생성하도록 합니다.

pidfile
Default:
None

PID 잠금 파일을 위한 컨텍스트 관리자입니다. 데몬 컨텍스트가 열리고 닫힐 때 pidfile 컨텍스트 관리자에 진입하고 빠져나옵니다.

detach_process
Default:
None

True이면 데몬 컨텍스트를 열 때 프로세스 컨텍스트를 분리하고, False이면 분리하지 않습니다.

인스턴스 초기화 중에 지정되지 않으면(None) 기본적으로 True로 설정되며, 프로세스 분리가 불필요하다고 판단되는 경우에만 False로 설정됩니다. 예를 들어 프로세스가 init, initd 또는 inetd에 의해 시작된 경우가 이에 해당합니다.

signal_map
Default:
system-dependent

운영 체제 시그널에서 콜백 동작으로의 매핑입니다.

이 매핑은 데몬 컨텍스트가 열릴 때 사용되며, 각 시그널의 시그널 핸들러에 대한 동작을 결정합니다:

  • None 값은 (시그널 동작을 signal.SIG_IGN으로 설정하여) 시그널을 무시합니다.
  • 문자열 값은 DaemonContext 인스턴스의 속성 이름으로 사용됩니다. 해당 속성의 값이 시그널 핸들러의 동작으로 사용됩니다.
  • 그 밖의 모든 값은 시그널 핸들러의 동작으로 사용됩니다.

기본값은 실행 중인 시스템에 어떤 시그널이 정의되어 있는지에 따라 달라집니다. 아래 목록의 항목 중 해당 시그널이 signal 모듈에 실제로 정의된 항목은 모두 기본 매핑에 나타납니다:

  • signal.SIGTTIN: None
  • signal.SIGTTOU: None
  • signal.SIGTSTP: None
  • signal.SIGTERM: 'terminate'

프로그램이 자식 프로세스와 상호 작용하는 방식에 따라 signal.SIGCHLD 시그널(자식 프로세스가 종료될 때 수신됨)을 포함하는 시그널 매핑을 지정해야 할 수도 있습니다. 어떤 상황에서 시그널 핸들러가 필요한지 판단하는 방법에 대한 자세한 내용은 해당 운영 체제의 문서를 참조하십시오.

uid
Default:
os.getuid()
gid
Default:
os.getgid()

데몬 시작 시 프로세스를 전환할 사용자 ID(“UID”) 값과 그룹 ID(“GID”) 값입니다.

기본값인 프로세스의 실제 UID와 GID를 사용하면 프로세스가 상속받은 실효 권한 상승이 모두 포기됩니다.

prevent_core
Default:
True

참이면 root로 실행되는 데몬에서 민감한 정보가 유출되지 않도록 코어 파일 생성을 막습니다.

stdin
Default:
None
stdout
Default:
None
stderr
Default:
None

stdin, stdout, stderr는 각각 표준 I/O 스트림 sys.stdin, sys.stdout, sys.stderr의 새 파일로 사용될 파일류 객체입니다. 따라서 이 파일은 열려 있어야 하며, stdin의 경우 최소 ‘r’ 모드, stdout과 stderr의 경우 ‘w+’ 모드여야 합니다.

객체에 파일 디스크립터를 반환하는 fileno() 메서드가 있으면, 해당 파일은 데몬 시작 중에 닫히는 대상에서 제외됩니다(즉, files_preserve에 나열된 것처럼 취급됩니다).

None이면 해당 시스템 스트림은 os.devnull이 가리키는 파일에 다시 바인딩됩니다.

다음 메서드가 정의되어 있습니다.

open()
Return:
None

데몬 컨텍스트를 열어 현재 프로그램을 데몬 프로세스로 전환합니다. 이 메서드는 다음 단계를 수행합니다:

  • 이 인스턴스의 is_open 프로퍼티가 참이면 즉시 반환합니다. 따라서 한 인스턴스에서 open을 여러 번 호출해도 안전합니다.
  • prevent_core 속성이 참이면, 프로세스에서 코어 덤프가 발생하지 않도록 프로세스의 리소스 제한을 설정합니다.
  • chroot_directory 속성이 None이 아니면, (os.chroot를 통해) 프로세스의 유효 루트 디렉터리를 해당 디렉터리로 설정합니다.

    이를 통해 프로세스의 비정상적인 동작에 시스템이 노출되는 범위를 제한하는 수단으로 “chroot 감옥” 안에서 데몬 프로세스를 실행할 수 있습니다. 지정한 디렉터리는 이 용도에 맞게 미리 준비되어 있어야 한다는 점에 유의하십시오.

  • 프로세스의 UID와 GID를 uid와 gid 속성 값으로 설정합니다.
  • 열려 있는 모든 파일 디스크립터를 닫습니다. 단, files_preserve 속성에 나열된 것과 stdin, stdout, stderr 속성에 해당하는 것은 제외합니다.
  • 현재 작업 디렉터리를 working_directory 속성에 지정된 경로로 변경합니다.
  • 파일 접근 생성 마스크를 umask 속성에 지정된 값으로 재설정합니다.
  • detach_process 옵션이 참이면, 현재 프로세스를 자체 프로세스 그룹으로 분리하고 모든 제어 터미널과의 연결을 끊습니다.
  • signal_map 속성에 지정된 대로 시그널 핸들러를 설정합니다.
  • stdin, stdout, stderr 속성 중 하나라도 None이 아니면, 시스템 스트림 sys.stdin, sys.stdout, sys.stderr 중 해당하는 스트림을 대응하는 속성이 나타내는 파일에 바인딩합니다. 속성에 파일 디스크립터가 있는 경우에는 (이름을 다시 바인딩하는 대신) 디스크립터를 복제합니다.
  • pidfile 속성이 None이 아니면, 해당 컨텍스트 관리자에 진입합니다.
  • (이후의 open 및 close 호출을 위해) 이 인스턴스를 열린 상태로 표시합니다.
  • Python 종료 처리 중에 호출되도록 close 메서드를 등록합니다.

함수가 반환되면 실행 중인 프로그램은 데몬 프로세스가 됩니다.

close()
Return:
None

데몬 컨텍스트를 닫습니다. 이 메서드는 다음 단계를 수행합니다:

  • 이 인스턴스의 is_open 프로퍼티가 거짓이면 즉시 반환합니다. 따라서 한 인스턴스에서 close를 여러 번 호출해도 안전합니다.
  • pidfile 속성이 None이 아니면 해당 컨텍스트 관리자를 빠져나옵니다.
  • 이 인스턴스를 닫힌 상태로 표시합니다(이후의 open 및 close 호출을 위한 것입니다).
is_open
Return:
True if the instance is open, False otherwise.

이 프로퍼티는 인스턴스가 현재 열려 있는지를 나타내는 상태를 노출합니다. 인스턴스의 open 메서드가 호출되었고 그 이후에 close 메서드가 호출되지 않았다면 True입니다.

terminate(signal_number, stack_frame)
Return:
None

signal.SIGTERM 시그널에 대한 시그널 핸들러입니다. 다음 단계를 수행합니다:

  • 시그널을 설명하는 SystemExit 예외를 발생시킵니다.

이 클래스는 __enter__ 및 __exit__ 메서드를 통해 컨텍스트 관리자 프로토콜도 구현합니다.

__enter__()
Return:
The DaemonContext instance

인스턴스의 open() 메서드를 호출한 다음 인스턴스를 반환합니다.

__exit__(exc_type, exc_value, exc_traceback)
Return:
True or False as defined by the context manager 프로토콜

인스턴스의 close() 메서드를 호출한 다음, 예외가 처리되었으면 True를, 처리되지 않았으면 False를 반환합니다.

동기

Unix 데몬으로 작성된 프로그램 대부분은 Specification에 있는 것과 매우 비슷한 동작을 구현하거나, Correct daemon behaviour에 비추어 볼 때 제대로 동작하지 않는 데몬입니다.

이러한 단계는 대부분의 구현에서 거의 동일해야 하지만 매우 까다롭고 빠뜨리거나 잘못 구현하기 쉬우므로, 잘 검증된 표준 구현을 표준 라이브러리에 두기에 가장 적합한 대상입니다.

근거

올바른 데몬 동작

[stevens] §2.6에서 Stevens가 설명한 바에 따르면, 프로그램이 Unix 데몬 프로세스가 되려면 다음 단계를 수행해야 합니다.

  • 열려 있는 모든 파일 디스크립터를 닫습니다.
  • 현재 작업 디렉터리를 변경합니다.
  • 파일 접근 생성 마스크를 재설정합니다.
  • 백그라운드에서 실행합니다.
  • 프로세스 그룹에서 분리합니다.
  • 터미널 I/O 시그널을 무시합니다.
  • 제어 터미널에서 분리합니다.
  • 제어 터미널을 다시 획득하지 않습니다.
  • 다음 상황을 올바르게 처리합니다:
    • System V init 프로세스에 의해 시작된 경우.
    • SIGTERM 시그널에 의한 데몬 종료.
    • 자식 프로세스는 SIGCLD 시그널을 발생시킵니다.

daemon 도구 [slack-daemon]는 프로그램을 올바르게 동작하는 Unix 데몬 프로세스로 전환할 때 수행해야 하는 동작을 (기능 요약에서) 나열합니다. 이 도구는 별도의 프로그램을 데몬 프로세스로 실행한다는 점에서 이 PEP의 의도와 다릅니다. 다음 기능은 프로그램이 이미 실행 중인 상태에서 스스로를 데몬으로 시작하는 경우에 적합합니다:

  • 데몬에 맞는 올바른 프로세스 컨텍스트를 설정합니다.
  • initd(8) 또는 inetd(8)에 의해 시작될 때 적절하게 동작합니다.
  • 데몬이 특수 권한으로 잘못 설치된 경우의 보안 위험을 줄이기 위해 모든 suid 또는 sgid 권한을 회수합니다.
  • root로 실행되는 데몬에서 민감한 정보가 유출되지 않도록 코어 파일 생성을 막습니다(선택 사항).
  • PID 파일을 생성하고 잠가서 데몬에 이름을 부여하며, 이를 통해 주어진 이름의 데몬이 한 시점에 하나만 실행될 수 있도록 보장합니다(선택 사항).
  • 데몬을 실행할 사용자와 그룹을 설정합니다(선택 사항, root 전용).
  • chroot 감옥을 만듭니다(선택 사항, root 전용).
  • 데몬의 stdout과 stderr를 가로채서 syslog로 보냅니다(선택 사항).

데몬은 서비스가 아닙니다

이 PEP는 위의 올바른 동작이 적용되는 Unix 방식의 데몬만을 다루며, 다른 운영 체제의 유사한 동작은 다루지 않습니다.

많은 시스템에는 “서비스”라고 부르는 관련 개념이 있습니다. 서비스는 현재 프로그램이 데몬 프로세스로 계속 실행되게 하는 대신, 백그라운드에서 실행할 추가 프로세스를 시작하고 현재 프로세스가 정해진 채널을 통해 그 추가 프로세스와 통신한다는 점에서 이 PEP의 모델과 다릅니다.

이 PEP의 Unix 방식 데몬 모델은 여러 용도 가운데 특히 서비스의 백그라운드 프로세스 부분을 구현하는 데 사용할 수 있습니다. 그러나 이 PEP는 서비스를 설정하고 관리하는 그 밖의 측면은 다루지 않습니다.

참조 구현

python-daemon 패키지 [python-daemon].

다른 데몬 구현

이 PEP 이전에도 이미 여러 서드파티 Python 라이브러리나 도구가 이 PEP의 Correct daemon behaviour 중 일부를 구현했습니다.

Reference Implementation은 다음 구현들을 상당히 직접적으로 계승한 것입니다:

이 PEP와 다른 그 밖의 Python 데몬 구현:

  • zdaemon 도구 [zdaemon]는 Zope 프로젝트를 위해 작성되었습니다. [slack-daemon]과 마찬가지로, 이 도구는 다른 프로그램을 데몬 프로세스로 실행하는 데 사용되므로 이 명세와 다릅니다.
  • Python 라이브러리 daemon [clapper-daemon]은 (홈페이지에 따르면) 더 이상 유지 보수되지 않습니다. 버전 1.0.1 기준으로, 이 라이브러리는 [stevens]의 기본 단계를 구현합니다.
  • daemonize 라이브러리 [seutter-daemonize]도 [stevens]의 기본 단계를 구현합니다.
  • Ray Burr의 daemon.py 모듈 [burr-daemon]은 [stevens] 절차와 함께 PID 파일 처리 및 syslog로의 출력 리디렉션 기능을 제공합니다.
  • Twisted [twisted]에는 어쩌면 당연하게도 Twisted 프레임워크의 나머지 부분과 통합된 프로세스 데몬화 API 구현이 포함되어 있습니다. 이 구현은 이 PEP의 API와 상당히 다릅니다.
  • [clapper-daemon]을 사용하는 Python initd 라이브러리 [dagitses-initd]는 데몬 프로세스를 제어하기 위해 Unix initd(8)에 해당하는 기능을 구현합니다.

참고 문헌

[stevens] (1, 2, 3, 4)
Unix Network Programming, W. Richard Stevens, 1994 Prentice Hall.
[slack-daemon] (1, 2)
The (non-Python) “libslack” implementation of a daemon tool http://www.libslack.org/daemon/ by “raf” <raf@raf.org>.
[python-daemon] (1, 2)
The python-daemon library http://pypi.python.org/pypi/python-daemon/ by Ben Finney et al.
[cookbook-66012] (1, 2)
Python Cookbook recipe 66012, “Fork a daemon process on Unix” http://code.activestate.com/recipes/66012/.
[cookbook-278731]
Python Cookbook recipe 278731, “Creating a daemon the Python way” http://code.activestate.com/recipes/278731/.
[bda.daemon]
The bda.daemon library http://pypi.python.org/pypi/bda.daemon/ by Robert Niederreiter et al.
[zdaemon]
The zdaemon tool http://pypi.python.org/pypi/zdaemon/ by Guido van Rossum et al.
[clapper-daemon] (1, 2)
The daemon library http://pypi.python.org/pypi/daemon/ by Brian Clapper.
[seutter-daemonize]
The daemonize library http://daemonize.sourceforge.net/ by Jerry Seutter.
[burr-daemon]
The daemon.py module http://www.nightmare.com/~ryb/code/daemon.py by Ray Burr.
[twisted]
The Twisted application framework http://pypi.python.org/pypi/Twisted/ by Glyph Lefkowitz et al.
[dagitses-initd]
The Python initd library http://pypi.python.org/pypi/initd/ by Michael Andreas Dagitses.