유닉스에서 임의의 스크립트를 어떻게 데몬 화합니까?


93

임의의 일반 스크립트 또는 명령을 데몬 으로 바꿀 수있는 데몬 라이저를 원합니다 .

처리하고 싶은 두 가지 일반적인 경우가 있습니다.

  1. 영원히 실행되어야하는 스크립트가 있습니다. 죽는 경우 (또는 재부팅시) 다시 시작하십시오. 한 번에 두 개의 복사본이 실행되도록하지 마십시오 (복제가 이미 실행 중인지 감지하고이 경우 실행하지 마십시오).

  2. 영원히 반복적으로 실행하고 싶은 간단한 스크립트 또는 명령 줄 명령이 있습니다 (실행 사이에 잠시 멈춤). 다시 말하지만, 두 개의 스크립트 사본이 한 번에 실행되는 것을 허용하지 마십시오.

물론 사례 2의 경우 스크립트 주위에 "while (true)"루프를 작성한 다음 사례 1에 대한 솔루션을 적용하는 것은 간단하지만보다 일반적인 솔루션은 사례 1의 스크립트에 다음과 같이 적용되므로 사례 2를 직접 해결합니다. 잘 (스크립트가 정말 경우 스크립트가 이제까지 물론 (다이에 구성되지 않은 경우 그냥 짧은 또는 전혀 일시 정지를 할 수 있습니다 않고 다음 죽지 않을 일시 정지하지 않습니다 실제로 문제)).

솔루션은 기존 스크립트에 파일 잠금 코드 또는 PID 기록을 추가하는 것과 관련이 없어야합니다.

좀 더 구체적으로 말하자면, 다음과 같이 실행할 수있는 "daemonize"프로그램을 원합니다.

% daemonize myscript arg1 arg2

또는 예를 들어

% daemonize 'echo `date` >> /tmp/times.txt'

이는 times.txt에 추가 된 날짜 목록을 계속 유지합니다. (데몬화할 인수가 위의 경우 1과 같이 영원히 실행되는 스크립트 인 경우 데몬 이즈는 여전히 올바른 작업을 수행하고 필요할 때 다시 시작합니다.) 그런 다음 .login에 위와 같은 명령을 넣을 수 있습니다. 및 / 또는 시간 또는 분 단위로 cron (예기치 않게 죽어가는 것에 대해 얼마나 걱정했는지에 따라 다름).

주의 : daemonize 스크립트는 데몬 화중인 명령 문자열을 기억해야합니다. 그래야 동일한 명령 문자열이 다시 데몬 화되는 경우 두 번째 복사본이 시작되지 않습니다.

또한 솔루션은 OS X와 ​​Linux 모두에서 이상적으로 작동해야하지만 둘 중 하나에 대한 솔루션도 환영합니다.

편집 : 당신이 그것을 호출 해야하는 경우 괜찮습니다 sudo daemonize myscript myargs.

(내가이 모든 것을 잘못 생각하고 있거나 빠르고 더러운 부분적인 해결책이 있다면, 저도 듣고 싶습니다.)


추신 : 유용하다면 여기 에 파이썬과 관련된 비슷한 질문이 있습니다.

그리고 유사한 질문에 대한 답변에는 임의의 스크립트를 빠르고 더러운 데몬 화하는 데 유용한 관용구가 있습니다.


1
순수 쉘 버전 은 serverfault.com/questions/311593/… 을 참조하십시오
w00t

답변:


90

nohup 및 & 연산자를 사용하여 Unix의 모든 실행 파일을 데몬화할 수 있습니다.

nohup yourScript.sh script args&

nohup 명령을 사용하면 스크립트를 종료하지 않고 셸 세션을 종료 할 수 있으며 &는 스크립트를 백그라운드에 배치하여 세션을 계속하라는 셸 프롬프트를 표시합니다. 이것에 대한 유일한 사소한 문제는 표준 출력이고 표준 오류는 모두 ./nohup.out으로 전송되므로이 매너에서 여러 스크립트를 시작하면 출력이 서로 얽혀 있습니다. 더 나은 명령은 다음과 같습니다.

nohup yourScript.sh script args >script.out 2>script.error&

그러면 선택한 파일로 표준이 전송되고 선택한 다른 파일로 표준 오류가 전송됩니다. 표준 출력 및 표준 오류에 대해 하나의 파일 만 사용하려면 다음을 수행 할 수 있습니다.

nohup yourScript.sh script args >script.out 2>&1 &

2> & 1은 표준 오류 (파일 설명자 2)를 표준 출력 (파일 설명자 1)과 동일한 파일로 리디렉션하도록 쉘에 지시합니다.

명령을 한 번만 실행하고 죽으면 다시 시작하려면 다음 스크립트를 사용할 수 있습니다.

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

첫 번째 인수는 사용할 pid 파일의 이름입니다. 두 번째 인수는 명령입니다. 그리고 다른 모든 인수는 명령의 인수입니다.

이 스크립트의 이름을 restart.sh로 지정하면 다음과 같이 호출 할 수 있습니다.

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

대박; 감사합니다. 재시작 지연 옵션도 있어야하는지 궁금합니다. 또는 다음과 함께 사용하는 것이 더 좋을 수도 있습니다. stackoverflow.com/questions/555116/…
dreeves

4
이것은 SIGHUP 만 처리하며 처리해야하는 다른 (일반적으로) 치명적인 신호가 있습니다.
Tim Post

이 스크립트를 개선하는 또 다른 방법은 아마도 $ PIDFILE을 인수로 지정하도록 요구하지 않고 자체적으로 $ PIDFILE을 배치 할 좋은 위치를 찾는 것입니다. 자체적으로 정리도 안됩니다! (A와 간단해야하는 trap EXIT)
스티븐 루

또한 <in 의 사용이 test정수 비교가 아닌 ASCII 비교 임을 고려하십시오 . 여전히 작동하지만 버그로 이어질 수 있습니다.
Steven Lu

이 스크립트에 대한 수정 사항을 여기에 게시했습니다 .
Steven Lu

34

긴 답변에 대해 사과드립니다 (내 답변이 사양을 어떻게 결정하는지에 대한 의견을 참조하십시오). 나는 포괄적이려고 노력하고 있으므로 가능한 한 다리를 잘 올리십시오. :-)

프로그램을 설치할 수 있고 (루트 액세스 권한이 있음) 데몬 실행을 위해 스크립트를 설정하기 위해 일회성 작업을 기꺼이 수행하려는 경우 (즉, 명령 줄에서 실행할 명령 줄 인수를 지정하는 것보다 더 복잡합니다. 하지만 서비스 당 한 번만 수행하면됩니다.) 더 강력한 방법이 있습니다.

daemontools 사용이 포함됩니다 . 나머지 게시물에서는 daemontools를 사용하여 서비스를 설정하는 방법을 설명합니다.

초기 설정

  1. daemontools를 설치하는 방법 의 지침을 따릅니다 . 일부 배포판 (예 : Debian, Ubuntu)에는 이미 패키지가 있으므로 사용하십시오.
  2. 라는 디렉토리를 만듭니다 /service. 설치 프로그램이 이미이 작업을 수행 했어야하지만 확인 만하거나 수동으로 설치하는 경우입니다. 이 위치가 마음에 들지 않으면 svscanboot스크립트 에서 변경할 수 있지만 대부분의 daemontools 사용자는/service 하지 않으면 혼란스러워집니다.
  3. 표준을 사용하지 않는 Ubuntu 또는 다른 배포판을 사용하는 경우 init(예 :를 사용하지 않음 /etc/inittab)에서 호출 inittab할 정렬 svscanboot을 위한 기본으로 사전 설치된을 사용해야합니다 init. 어렵지는 않지만 initOS에서 사용하는 구성 방법을 알아야 합니다. 서비스를 찾는 주요 작업을 수행 svscanboot하는를 호출하는 스크립트입니다 svscan. 어떤 이유로 든 죽으면 다시 시작 init하도록 에서 호출됩니다 init.

서비스 별 설정

  1. 각 서비스에는 서비스 에 대한 하우스 키핑 정보를 저장 하는 서비스 디렉토리 가 필요합니다 . 또한 이러한 서비스 디렉토리를 보관할 위치를 만들어 모두 한곳에 보관할 수 있습니다. 일반적으로를 사용 /var/lib/svscan하지만 새로운 위치는 괜찮습니다.
  2. 저는 보통 스크립트 를 사용 하여 서비스 디렉토리를 설정하고 많은 수동 반복 작업을 저장합니다. 예 :

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    여기서는 some-service-name서비스에 제공 할 이름, user해당 서비스 loguser를 실행할 사용자, 로거를 실행할 사용자입니다. (로깅은 조금만 설명합니다.)

  3. 서비스는 포 그라운드에서 실행 되어야합니다 . 프로그램이 기본적으로 배경이지만 비활성화 할 수있는 옵션이 있으면 그렇게하십시오. 프로그램이 비활성화 할 방법없이 배경이있는 경우 fghack에는 절충안이 있더라도을 읽어보십시오 . 더 이상을 사용하여 프로그램을 제어 할 수 없습니다 svc.
  4. 편집 run스크립트를 원하는 작업을 수행하는지 확인하십시오. sleep서비스가 자주 종료 될 것으로 예상되는 경우 맨 위에 전화 를 걸어야 할 수도 있습니다 .
  5. 모든 것이 올바르게 설정되면 /service서비스 디렉토리 를 가리키는 심볼릭 링크를 만듭니다 . (서비스 디렉토리를 내부에 직접 넣지 마십시오 .의 감시 /service에서 서비스를 제거하기가 더 어려워집니다 svscan.)

벌채 반출

  1. daemontools 로깅 방법은 서비스가 로그 메시지를 표준 출력 (또는로 생성 된 스크립트를 사용하는 경우 표준 오류 mkservice)에 기록하도록하는 것입니다. svscan로그 메시지를 로깅 서비스로 보내는 작업을 처리합니다.
  2. 로깅 서비스는 표준 입력에서 로그 메시지를 가져옵니다. 에서 생성 한 로깅 서비스 스크립트 mkservicelog/main디렉토리에 자동 회전 된 타임 스탬프 로그 파일을 생성합니다 . 현재 로그 파일은라고 current합니다.
  3. 로깅 서비스는 기본 서비스와 독립적으로 시작 및 중지 할 수 있습니다.
  4. 로그 파일을 파이핑하면 tai64nlocal타임 스탬프가 사람이 읽을 수있는 형식으로 변환됩니다. (TAI64N은 나노초 카운트의 64 비트 원자 타임 스탬프입니다.)

제어 서비스

  1. svstat서비스 상태를 가져 오는 데 사용 합니다. 로깅 서비스는 독립적이며 자체 상태가 있습니다.
  2. 을 사용하여 서비스 (시작, 중지, 다시 시작 등)를 제어합니다 svc. 예를 들어 서비스를 다시 시작하려면 svc -t /service/some-service-name; -t"보내기 SIGTERM"를 의미합니다 .
  3. 사용 가능한 기타 신호에는 -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2) 및 -k(SIGKILL )가 있습니다.
  4. 서비스를 중단하려면을 사용하십시오 -d. 또한 다음과 같은 파일을 생성하여 부팅시 서비스가 자동으로 시작되지 않도록 할 수 있습니다.down서비스 디렉토리에 .
  5. 서비스를 시작하려면 -u . 이전에 다운 (또는 자동 시작하지 않도록 설정)하지 않은 경우에는 필요하지 않습니다.
  6. 감독자에게 종료를 요청하려면 -x; 일반적으로 -d서비스를 종료 하는데도 사용됩니다 . 이는 서비스를 제거 할 수있는 일반적인 방법이지만 /service먼저 서비스 연결을 해제해야합니다 svscan. 그렇지 않으면 수퍼바이저를 다시 시작합니다. 또한 로깅 서비스 ( mkservice -l)를 사용하여 서비스를 생성 한 경우 svc -dx /var/lib/svscan/some-service-name/log서비스 디렉토리를 제거하기 전에 로깅 감독자 (예 :)도 종료해야합니다 .

요약

장점 :

  1. daemontools는 서비스를 만들고 관리하는 방탄 방법을 제공합니다. 내 서버에 사용하고 있으며 적극 권장합니다.
  2. 로깅 시스템은 서비스 자동 재시작 기능과 마찬가지로 매우 강력합니다.
  3. 작성 / 조정하는 셸 스크립트로 서비스를 시작하기 때문에 원하는대로 서비스를 조정할 수 있습니다.
  4. 강력한 서비스 제어 도구 : 대부분의 신호를 서비스에 보낼 수 있으며 서비스를 안정적으로 위아래로 가져올 수 있습니다.
  5. 귀하의 서비스는 깨끗한 실행 환경을 보장 init합니다. 제공 하는 것과 동일한 환경, 프로세스 제한 등으로 실행됩니다 .

단점 :

  1. 각 서비스에는 약간의 설정이 필요합니다. 고맙게도 이것은 서비스 당 한 번만 수행하면됩니다.
  2. 서비스는 포 그라운드에서 실행되도록 설정되어야합니다. 또한 최상의 결과를 얻으려면 syslog 또는 기타 파일이 아닌 표준 출력 / 표준 오류에 기록하도록 설정해야합니다.
  3. daemontools 방식을 처음 사용하는 경우 학습 곡선이 가파 릅니다. 을 사용하여 서비스를 다시 시작해야 svc하며 실행 스크립트를 직접 실행할 수 없습니다 (그러면 관리자의 제어를받지 않기 때문입니다).
  4. 많은 하우스 키핑 파일과 많은 하우스 키핑 프로세스. 각 서비스에는 자체 서비스 디렉토리가 필요하며 각 서비스는 하나의 감독자 프로세스를 사용하여 서비스가 종료되면 자동으로 다시 시작합니다. (당신이 많은 서비스가있는 경우, 당신은 볼 을 많이supervise프로세스 테이블에서 프로세스를.)

균형 적으로 daemontools는 귀하의 요구에 맞는 훌륭한 시스템이라고 생각합니다. 설정 및 유지 관리 방법에 대한 질문을 환영합니다.


내 대답이 사양을 결정하는 방법 : 1. 중복을 설정하지 않는 한 (그리고 서비스가 백그라운드 자체를 배경으로하지 않는 한) 중복이 발생하지 않도록 서비스를 설정해야합니다. 2. supervise감독자 인은 종료 된 모든 서비스를 다시 시작합니다. 재 스타트 사이에 1 초를 기다립니다. 시간이 충분하지 않다면 서비스 실행 스크립트의 맨 위에 잠자기 상태로 두십시오.
Chris Jester-Young

2a. supervise에 의해 자체적으로 지원 svscan되므로 감독자가 사망하면 다시 시작됩니다. 2b. svscan에 의해 지원되며 필요에 따라 init자동으로 다시 시작 svscan됩니다. 2c. init당신이 어떤 이유로 든 죽는 다면 , 당신은 어쨌든 망가진 것입니다. :-P
Chris Jester-Young

하우스 키핑에 대한 다른 질문에 답하기 위해 daemontools 시스템은 PID 파일이 오래 될 수 있기 때문에 PID 파일을 사용하지 않습니다. 대신 모든 프로세스 정보는 주어진 서비스를 지원하는 감독자가 보관합니다. 감독자는 도구가 좋아 svstat하고 svc사용할 수 있는 서비스 디렉토리에 파일 (및 FIFO)을 유지합니다 .
Chris Jester-Young

3
우리는 SO와 일반적으로 그물에 이와 같은 더 많은 게시물을 가져야합니다. 원하는 효과를 얻는 방법에 대한 레시피뿐만 아니라 레시피를 설명하는 데 어려움을 겪는 레시피입니다. 왜 두 번 이상 찬성 할 수 없나요? : |
skytreader 2014

12

나는 당신이 시도하고 싶을 것이라고 생각합니다 start-stop-daemon(8). /etc/init.dLinux 배포판의 스크립트에서 예제를 확인하십시오 . 호출 된 명령 줄 또는 PID 파일로 시작된 프로세스를 찾을 수 있으므로 스크립트에 대한 감시자가되는 것을 제외하고 모든 요구 사항과 일치합니다. 그러나 필요한 경우 스크립트를 다시 시작하는 다른 데몬 감시 스크립트를 언제든지 시작할 수 있습니다.


Fedora에는 아직 start-stop-daemon이 없으므로 이에 의존하는 스크립트는 이식 할 수 없습니다. 참조 : fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

OSX 사용자를위한 경고 일뿐 start-stop-daemon입니다 (10.9 기준).
mklement0 dec.

@ mklement0 음 ... 거의 5 년 동안 많은 것이 변했습니다.
Alex B

내, 시간이 얼마나 빨리 가는지. start-stop-daemon그래도 여전히 살아 있고 리눅스에서 시작된다. 하지만 대답 stackoverflow.com/a/525406/45375를 읽은 후 OSX가 자체 작업을 수행한다는 것을 깨달았습니다 launchd.
mklement0 dec.

12

daemonize 살펴 봐야 합니다. 두 번째 복사본을 감지 할 수 있습니다 (하지만 파일 잠금 메커니즘을 사용함). 또한 다른 UNIX 및 Linux 배포판에서도 작동합니다.

자동으로 애플리케이션을 데몬으로 시작해야하는 경우 적절한 초기화 스크립트를 만들어야합니다.

다음 템플릿을 사용할 수 있습니다.

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
정답 후보 인 것 같습니다. 특히 "단일 인스턴스 검사"를 고려합니다.
Martin Wickman

이것이 최선의 답변 일 수 있습니다. 확실하지 않습니다.하지만 그럴 것이라고 생각한다면 질문에서 제가 제시 한 사양이 잘못된 이유에 대한 설명도 포함 할 수 있습니까?
dreeves

나는하지 좋아해요 killproc정지 부분 : 당신이 말하는, 달렸다는 것을 프로세스가 있다면 java, (가) killproc다른 모든 Java 프로세스가 너무 살해되는 원인이됩니다.
Chris Jester-Young

1
/etc/rc.d/init.d/functions에서 daemonize는 새 쉘에서 바이너리를 시작합니다. $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*그래서 나는 그것이 무엇이든
데몬화할 것 같지 않습니다

1
나는 이것이 오래되었다는 것을 알고 있지만 나중에 이것을 찾는 사람에게는 이것이 맞습니다. /etc/init.d/functions에 정의 된 "daemon"은 실제로 데몬 화되지 않습니다 . cgroup을 수행하고, 프로세스가 이미 실행 중인지 확인하고, 사용자를 설정하고, nice 및 ulimit 값을 설정하는 등의 작업을 수행하는 래퍼 일뿐 입니다. 프로세스를 데몬 화 하지 않습니다 . 그것은 여전히 ​​당신 자신의 직업입니다. :)
jakem

7

이미 언급 한 daemonize및에 대한 대안으로 libslack 패키지 daemontools데몬 명령이 있습니다.

daemon 매우 구성 가능하며 자동 재시작, 로깅 또는 pidfile 처리와 같은 모든 지루한 데몬 작업을 처리합니다.


5

특별히 OS X를 사용하고 있다면 launchd가 어떻게 작동하는지 살펴 보는 것이 좋습니다. 스크립트가 실행 중인지 자동으로 확인하고 필요한 경우 다시 시작합니다. 또한 모든 종류의 스케줄링 기능 등을 포함합니다. 요구 사항 1과 2를 모두 충족해야합니다.

스크립트 사본 하나만 실행할 수 있도록하려면 PID 파일을 사용해야합니다. 일반적으로 현재 실행중인 인스턴스의 PID를 포함하는 파일을 /var/run/.pid에 씁니다. 프로그램이 실행될 때 파일이 존재하면 파일의 PID가 실제로 실행 중인지 확인합니다 (프로그램이 충돌했거나 PID 파일을 삭제하는 것을 잊었을 수 있음). 그렇다면 중단하십시오. 그렇지 않은 경우 실행을 시작하고 PID 파일을 덮어 씁니다.


5

Daemontools ( http://cr.yp.to/daemontools.html )는 dj bernstein이 작성한이 작업을 수행하는 데 사용되는 꽤 하드 코어 유틸리티 세트입니다. 나는 이것을 약간의 성공으로 사용했습니다. 그것에 대한 성가신 부분은 스크립트를 실행할 때 보이는 결과를 반환하지 않는다는 것입니다. 단지 보이지 않는 반환 코드 일뿐입니다. 그러나 일단 실행되면 방탄입니다.


예, daemontools도 사용하는 항목을 작성하려고했습니다. 나는 내 답변에 대해 훨씬 더 포괄적이기를 희망하고 그런 식으로 현상금을 받기를 희망하기 때문에 내 게시물을 작성할 것입니다. 우리는 볼 것이다. :-)
Chris Jester-Young

3

먼저 http://code.activestate.com/recipes/278731/createDaemon() 에서 가져 옵니다 .

그런 다음 주요 코드 :

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

오, 감사합니다! "daemonize foo arg1 arg2"뿐만 아니라 "daemonize 'foo arg1 arg2'"를 수행 할 수 있도록 약간 더 일반적으로 만들고 싶으십니까?
dreeves

이제 인수를 결합 할 것입니다.하지만 인수 내부에 공백을 넣으려면 변경해야합니다.
Douglas Leeder

감사합니다 더글라스! 하지만 큰 결함이 있습니다. "daemonize foo"를 두 번 실행하면 foo의 두 복사본이 실행됩니다.
dreeves

... 당신은 어떤 PID 녹화 코드를 추가 할 수 있지만, 한 번만 스크립트를 실행하는 것이 좋습니다 수 있습니다
더글러스 Leeder 씨에게

저는 그것이 전체 "daemonize"래퍼 개념의 기본이라고 생각합니다. (예를 들어, 매시간 또는 매분 단위로 크론을 실행하여 항상 실행되고 있는지 확인할 수 있습니다.) 내가 잘못 생각하고 있습니까? createDaemon은 이미 그것을 보장합니까? 재부팅 후에는 어떻습니까?
2009

1

이것은 빈 디렉토리에 복사하여 사용해 볼 수있는 예제가 포함 된 작업 버전입니다 (CPAN 종속성 설치 후 Getopt :: Long , File :: Spec , File :: PidIPC :: System : : Simple- 모두 표준이며 모든 해커에게 적극 권장됩니다 cpan <modulename> <modulename> ....)를 사용 하여 한 번에 설치할 수 있습니다 .


keepAlive.pl :

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl :

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

이제 다음을 사용하여 위의 예를 호출 할 수 있습니다. ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3그러면 파일 pidfile이 생성되고 출력이 표시됩니다.

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

내가 올바르게 이해한다면 이것이 사양에 맞지 않는다고 생각합니다. 솔루션 (감사합니다, btw!)에서 데몬 화하려는 프로그램을 수정하여 PID를 PID 파일에 기록해야합니다. 임의의 스크립트를 데몬화할 수있는 유틸리티가 필요합니다.
dreeves

@dreeves : 예,하지만 그 주위에는 두 가지 방법이 있습니다. 1. keepAlive.pl (예 : example.pl)에 의해 호출 된 스크립트는 단순히 실제 프로그램을 실행하기위한 래퍼 일 수 있습니다. 2. keepAlive.pl은 테이블을 구문 분석 할 수 있습니다. 활성 시스템 프로세스 (CPAN의 Proc :: ProcessTable 포함)를 사용하여 관련 프로세스 및 해당 pid를 찾습니다.
Ether

1

Monit 을 사용해 볼 수도 있습니다 . Monit은 다른 서비스를 모니터링하고보고하는 서비스입니다. 주로 런타임 문제에 대해 알림 (이메일 및 SMS를 통해)하는 방법으로 사용되지만 여기에서 제안한 대부분의 다른 제안을 수행 할 수도 있습니다. 프로그램을 자동 (다시) 시작 및 중지하고, 이메일을 보내고, 다른 스크립트를 시작하고, 선택할 수있는 출력 로그를 유지할 수 있습니다. 또한 견고한 문서가 있으므로 설치 및 유지 관리가 쉽다는 것을 알았습니다.


1

불멸 의 시도를 할 수 있습니다 . * nix 크로스 플랫폼 (OS에 구애받지 않는) 감독자입니다.

macOS에서 빠르게 시도하려면 :

brew install immortal

포트에서 또는 pkg를 사용 하여 FreeBSD 를 사용하는 경우 :

pkg install immortal

들어 리눅스 미리 컴파일 된 바이너리를 다운로드하거나 소스 : https://immortal.run/source/

다음과 같이 사용할 수 있습니다.

immortal -l /var/log/date.log date

또는 더 많은 옵션을 제공 하는 구성 YAML 파일에 의해 , 예를 들면 다음과 같습니다.

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

표준 오류 출력을 별도의 파일에 보관하려면 다음과 같이 사용할 수 있습니다.

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

다른 답변 에 대해 일련의 개선을했습니다 .

  1. 이 스크립트의 stdout은 명령이 이미 실행되고 있음을 감지하여 종료하지 않는 한 자식에서 오는 stdout으로 순전히 구성됩니다.
  2. 종료 될 때 pidfile 이후 정리
  3. 선택적 구성 가능한 시간 초과 기간 (양수 인수를 허용하고로 전송 sleep)
  4. 사용 프롬프트 -h
  5. 단일 명령 실행이 아닌 임의의 명령 실행. 마지막 인수 또는 나머지 인수 (마지막 인수가 두 개 이상인 경우)가로 전송 eval되므로 데몬 화할 마지막 인수 (또는 후행 인수)로이 스크립트에 보낼 문자열로 모든 종류의 쉘 스크립트를 구성 할 수 있습니다.
  6. -lt대신 인수 개수 비교<

다음은 스크립트입니다.

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

용법:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

이 스크립트를 다른 디렉토리에서 실행하는 경우 다른 pidfile을 사용하고 기존 실행중인 인스턴스를 감지하지 못할 수 있습니다. 인수를 통해 제공된 임시 명령을 실행하고 다시 시작하도록 설계되었으므로 동일한 명령 인지 누가 말해야하므로 이미 시작되었는지 여부를 알 수있는 방법이 없습니다. 없습니다. 단일 인스턴스 만 실행하는 이러한 시행을 개선하려면 상황에 맞는 솔루션이 필요합니다.

또한 적절한 데몬으로 작동하려면 다른 답변에서 언급했듯이 (최소한) nohup을 사용해야합니다. 나는 프로세스가받을 수있는 신호에 대한 복원력을 제공하기 위해 노력하지 않았습니다.

주목해야 할 또 하나의 요점은이 스크립트를 죽이는 것 (살해 된 또 다른 스크립트에서 호출 된 경우 또는 신호로)이 특히 자식이 아직 다른 스크립트 인 경우 자식을 죽이는 데 성공하지 못할 수 있다는 것입니다 . 왜 그런지는 잘 모르겠지만 eval, 작동 방식과 관련이있는 것 같습니다 . 따라서 해당 줄을 다른 답변과 같이 단일 명령 만 허용하는 것으로 바꾸는 것이 현명 할 수 있습니다.

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