프로세스가 종료되면 프로세스를 다시 시작하기 위해 bash 스크립트를 작성하는 방법


226

대기열을 확인하고 각 항목에 대해 작업을 수행하는 Python 스크립트가 있습니다.

# checkqueue.py
while True:
  check_queue()
  do_something()

bash 스크립트가 실행 중인지 확인하고 그렇지 않은 경우 시작하는 bash 스크립트를 작성하는 방법 대략 다음 의사 코드 (또는 어쩌면 ps | grep? 와 같은 작업을 수행해야 함 ) :

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

나는 crontab에서 그것을 부를 것입니다 :

# crontab
*/5 * * * * /path/to/keepalivescript.sh

4
2017 년에 이것을 추가하기 위해 supervisord를 사용하십시오. crontab은 이런 종류의 작업을 수행하지 않습니다. 실제 오류가 발생하면 bash 스크립트가 끔찍합니다. stackoverflow.com/questions/9301494/…
mootmoot

다른 비 시스템 솔루션 대신 inittab을 사용하여 다시 생성하는 것은 어떻습니까? superuser.com/a/507835/116705
Lars Nordin이

답변:


635

PID 파일, 크론 또는 자녀가 아닌 프로세스를 평가하려는 다른 것을 피하십시오.

유닉스에서는 오직 당신의 자녀 만 기다릴 수있는 이유가 있습니다. 이 문제를 해결하려고 시도하는 모든 방법 (ps 구문 분석, pgrep, PID 저장 ...)은 결함이 있으며 그 안에 구멍이 있습니다. 그냥 말할 아니오 .

대신 프로세스를 부모 프로세스로 모니터하는 프로세스가 필요합니다. 이것은 무엇을 의미 하는가? 프로세스를 시작한 프로세스 만 프로세스가 끝날 때까지 안정적으로 기다릴 수 있음을 의미합니다. bash에서 이것은 절대적으로 사소합니다.

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

위의 bash 코드는 루프 myserver에서 실행됩니다 until. 첫 번째 줄이 시작 myserver되고 끝날 때까지 기다립니다. 종료되면 until종료 상태를 확인합니다. 종료 상태가 0인 경우 정상적으로 종료되었음을 의미합니다 (어쨌든 종료하도록 요청했으며 성공적으로 종료되었음을 의미 함). 이 경우 다시 시작하고 싶지 않습니다 (방금 시스템 종료를 요청했습니다!). 종료 상태 인 경우 하지 0 , untilSTDERR에 에러 메시지를 방출 루프 (라인 1 등을) 다시 시작 루프 본문, 실행 1 초 후에을 .

왜 우리는 잠시 기다려? 시작 시퀀스에 문제가 있고 myserver즉시 충돌하는 경우, 일정하게 다시 시작하고 손에 충돌하는 매우 집중적 인 루프를 갖게됩니다. 은 sleep 1그에서 긴장을 멀리합니다.

이제이 bash 스크립트 (비동기 적으로)를 시작하기 만하면 myserver필요에 따라이를 모니터링 하고 다시 시작할 수 있습니다. 부팅시 모니터를 시작하려면 (서버를 "생존"으로 재부팅) @reboot규칙을 사용하여 사용자의 cron (1)에서 모니터를 예약 할 수 있습니다 . 다음과 crontab같이 크론 규칙을 엽니 다 .

crontab -e

그런 다음 규칙을 추가하여 모니터 스크립트를 시작하십시오.

@reboot /usr/local/bin/myservermonitor

대안 적으로; inittab (5) 및 / etc / inittab을보십시오. myserver특정 init 레벨에서 시작하고 자동으로 다시 생성되도록 라인을 추가 할 수 있습니다 .


편집하다.

PID 파일을 사용 하지 않는 이유에 대한 정보를 추가하겠습니다 . 그들은 매우 인기가 있지만; 그들은 또한 매우 결함이 있으며 올바른 방법으로하지 않는 이유가 없습니다.

이걸 고려하세요:

  1. PID 재활용 (잘못된 프로세스 종료) :

    • /etc/init.d/foo start: 시작 foo, fooPID 쓰기/var/run/foo.pid
    • 얼마 후 : foo어떻게 든 죽는다.
    • 잠시 후 : 임의의 프로세스가 시작 (호출 bar)하면 임의 foo의 PID를 사용합니다. 예전 PID를 사용 한다고 상상해보십시오 .
    • 당신은 foo사라졌습니다 : /etc/init.d/foo/restart읽고 /var/run/foo.pid, 아직 살아 있는지 확인하고, 찾고 bar, 생각하고 foo, 죽이고, 새로운 것을 시작합니다 foo.
  2. PID 파일이 오래되었습니다. PID 파일이 오래된 지 여부를 확인하려면 지나치게 복잡하거나 (사소한 말이 아닌) 논리가 필요하며 이러한 논리는 다시 취약합니다 1..

  3. 쓰기 권한이 없거나 읽기 전용 환경에 있다면 어떻게해야합니까?

  4. 무의미한 복잡한 문제입니다. 위의 예제가 얼마나 간단한 지보십시오. 전혀 복잡 할 필요가 없습니다.

참조 : 여전히 결함이 PID-파일을 그것을 '권리'를 할 때?

그건 그렇고; PID 파일보다 더 나쁜 것은 파싱입니다 ps! 절대 이러지 마

  1. ps매우 이식이 불가능합니다. 거의 모든 UNIX 시스템에서 찾을 수 있습니다. 비표준 출력을 원하면 인수가 크게 다릅니다. 그리고 표준 출력은 스크립팅 된 구문 분석이 아니라 사람이 소비 할 수 있습니다!
  2. 파싱 ps은 많은 오탐으로 이어집니다. 테이크 ps aux | grep PID예를 들어, 지금 당신이 당신의 데몬을 보았다는 PID와 같은 될 일 인수로 숫자 어딘가로 프로세스를 시작하는 사람을 상상! 두 사람이 X 세션을 시작하고 X가 당신을 죽이려고한다고 상상해보십시오. 그것은 모든 종류의 나쁜 일입니다.

프로세스를 직접 관리하고 싶지 않은 경우 프로세스를 모니터하는 완벽한 시스템이 있습니다. 예를 들어 runit을 살펴보십시오 .


1
@ 채용 소유 : 나는 그것이 필요하다고 생각하지 않습니다. 정당한 이유없이 구현을 복잡하게 만듭니다. 단순성이 항상 더 중요합니다. 자주 다시 시작하면 절전 모드가 시스템 리소스에 나쁜 영향을 미치지 않도록합니다. 어쨌든 이미 메시지가 있습니다.
lhunath

2
@orschiro 프로그램이 작동 할 때 리소스 소비가 없습니다. 실행 즉시 계속 존재하는 경우, 휴면 상태 1 인 자원 소비는 여전히 무시할 수 있습니다.
lhunath

7
내가있어 믿을 수 이 답변을보고. 정말 고마워!
getWeberForStackExchange

2
@ TomášZato 프로세스의 종료 코드를 테스트하지 않고 위의 루프를 수행 할 수 while true; do myprocess; done있지만 이제 프로세스를 중지 할 수있는 방법이 없습니다.
lhunath

2
@ SergeyP.akaazure bash에서 부모가 아이를 죽 이도록 강요하는 유일한 방법은 아이를 직업으로 trap 'kill $(jobs -p)' EXIT; until myserver & wait; do sleep 1; done
바꾸고

33

monit ( http://mmonit.com/monit/ )를 살펴보십시오 . 스크립트의 시작, 중지 및 다시 시작을 처리하며 필요한 경우 상태 확인 및 다시 시작을 수행 할 수 있습니다.

또는 간단한 스크립트를 수행하십시오.

while true
do
/your/script
sleep 1
done

4
Monit은 정확히 당신이 찾고있는 것입니다.
Sarke

4
"1 동안"작동하지 않습니다. "while [1]"또는 "while true"또는 "while :"이 필요합니다. 참조 unix.stackexchange.com/questions/367108/what-does-while-mean
커티스 Yallop

8

가장 쉬운 방법은 파일에서 무리를 사용하는 것입니다. 파이썬 스크립트에서는

lf = open('/tmp/script.lock','w')
if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): 
   sys.exit('other instance already running')
lf.write('%d\n'%os.getpid())
lf.flush()

쉘에서 실제로 실행 중인지 테스트 할 수 있습니다.

if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then 
   echo 'it's not running'
   restart.
else
   echo -n 'it's already running with PID '
   cat /tmp/script.lock
fi

그러나 물론 테스트 할 필요가 없습니다. 이미 실행 중이고 다시 시작하면 'other instance already running'

프로세스가 종료되면 모든 파일 설명자가 닫히고 모든 잠금이 자동으로 제거됩니다.


bash 스크립트를 제거하면 약간 단순화 할 수 있습니다. 파이썬 스크립트가 충돌하면 어떻게됩니까? 파일이 잠금 해제되어 있습니까?
Tom

1
응용 프로그램이 중지 되 자마자 자연스럽게 또는 충돌하여 파일 잠금이 해제됩니다.
Christian Witts

@Tom ... 좀 더 정확하게 말하면 파일 핸들이 닫히 자마자 더 이상 잠금이 활성화되지 않습니다. 파이썬 스크립트가 의도적으로 파일 핸들을 닫지 않고 가비지 수집되는 파일 객체를 통해 자동으로 닫히지 않는 경우 닫는 것은 스크립트가 종료 / 종료되었음을 의미합니다. 재부팅 등에서도 작동합니다.
찰스 더피

1
사용하는 훨씬 더 좋은 방법이 있습니다 flock... 실제로 매뉴얼 페이지에서 방법을 명확하게 보여줍니다! exec {lock_fd}>/tmp/script.lock; flock -x "$lock_fd"는 파이썬과 같은 bash이며 잠금을 유지합니다 (따라서 프로세스를 실행하면 프로세스가 종료 될 때까지 잠금이 유지됩니다).
Charles Duffy

귀하의 코드가 잘못되어 귀하에게 하향 투표했습니다. 사용하는 flock것이 올바른 방법이지만 스크립트가 잘못되었습니다. crontab에서 설정해야하는 유일한 명령은 다음과 같습니다.flock -n /tmp/script.lock -c '/path/to/my/script.py'
Rutrus

6

시스템에서 여러 가지 사항을 모니터링하고 그에 따라 대응할 수있는 표준 유닉스 도구 인 monit을 사용해야합니다.

문서에서 : http://mmonit.com/monit/documentation/monit.html#pid_testing

pidfile /var/run/checkqueue.pid로 checkqueue.py 프로세스 확인
       pid로 변경되면 "checkqueue_restart.sh"를 실행하십시오.

다시 시작할 때 이메일을 보내도록 monit을 구성 할 수도 있습니다.


2
MONIT는 훌륭한 도구이지만, 그것은 것입니다 하지 POSIX 또는 SUSV 중 하나에 지정되는 형식적인 의미에서 표준입니다.
Charles Duffy

5
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi

멋지다, 그것은 내 의사 코드의 일부를 잘 만들어 내고있다. 두 qns : 1) PIDFILE을 어떻게 생성합니까? 2) psgrep는 무엇입니까? 우분투 서버에 없습니다.
Tom

ps grep은와 같은 기능을하는 작은 앱입니다 ps ax|grep .... 그냥 설치하거나 그 기능을 작성할 수 있습니다. function psgrep () {ps ax | grep -v grep | grep -q "$ 1"}
soulmerge

나는 당신의 첫 번째 질문에 대답하지 않았다는 것을 알았습니다.
soulmerge

7
실제로 사용량이 많은 서버에서는 확인하기 전에 PID가 재활용 될 수 있습니다.
vartec

2

운영 체제에서 얼마나 이식성이 좋은지 잘 모르겠지만 시스템에 'run-one'명령 (예 : "man run-one")이 있는지 확인할 수 있습니다. 특히,이 명령 세트에는 '일관되게 실행'이 포함되며, 이는 정확히 필요한 것 같습니다.

매뉴얼 페이지에서 :

한 번에 COMMAND [ARGS]

참고 : 분명히 스크립트 내에서 호출 할 수는 있지만 스크립트를 가질 필요가 없습니다.


이것이 허용되는 답변보다 유리한 점이 있습니까?
tripleee

1
예, 시스템 코드베이스의 일부로 유지 관리해야하는 것과 동일한 작업을 수행하는 쉘 스크립트를 작성하는 것보다 내장 명령을 사용하는 것이 좋습니다. 쉘 스크립트의 일부로 기능이 필요한 경우에도 위 명령을 사용하여 쉘 스크립팅 질문과 관련이 있습니다.
Daniel Bradley

이것은 "내장"되지 않습니다. 일부 배포판에 기본적으로 설치되어 있으면 대답에 배포판을 지정해야합니다 (그리고 배포판이 아닌 경우 다운로드 할 위치에 대한 포인터 포함).
tripleee

우분투 유틸리티 인 것 같습니다. 우분투에서도 선택 사항입니다. manpages.ubuntu.com/manpages/bionic/man1/run-one.1.html
tripleee

주목할만한 점은 run-one 유틸리티가 이름의 기능을 정확히 수행한다는 것입니다. run-one-nnnnn으로 실행되는 명령의 인스턴스는 하나만 실행할 수 있습니다. 여기에있는 다른 대답은 더 실행 가능한 불가지론 적입니다-명령의 내용에 전혀 신경 쓰지 않습니다.
David Kohen

1

수많은 서버에서 다음 스크립트를 성공적으로 사용했습니다.

pid=`jps -v | grep $INSTALLATION | awk '{print $1}'`
echo $INSTALLATION found at PID $pid 
while [ -e /proc/$pid ]; do sleep 0.1; done

노트:

  • Java 프로세스를 찾고 있으므로 jps를 사용할 수 있습니다 .ps보다 배포판에서 훨씬 일관됩니다.
  • $INSTALLATION 완전히 분명한 프로세스 경로를 포함합니다.
  • 프로세스가 죽기를 기다리는 동안 수면을 사용하십시오.

이 스크립트는 실제로 실행중인 Tomcat 인스턴스를 종료하는 데 사용되며 명령 줄에서 종료하고 대기하고 싶습니다. 따라서 자식 프로세스로 시작하는 것은 단순히 옵션이 아닙니다.


1
grep | awk여전히 반 패턴입니다 . 당신 awk "/$INSTALLATION/ { print \$1 }"은 쓸모없는 grepAwk 스크립트에 정규식 자체로 줄을 찾을 수있는 Awk 스크립트를 포함 시키고 싶습니다 . 대단히 감사합니다.
tripleee

0

내 npm 프로세스에 이것을 사용합니다.

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.