목공책 하나 들이셔요~

2014년 6월 20일 금요일

cron으로 프로세스 관리하기

제 업무는 주로 리눅스에서 동작하는 서버(데몬, daemon)를 개발하는 것입니다. 잘 작성된 서버 프로그램은 일단 시작되면 시스템을 끄기 전까지는 다운되지 않고 그 역할을 잘 수행해야 합니다. 그런데 버그없는 프로그램 없다고... 때로는 예상하지 못한 이유로 프로세스가 죽기도 하고, 메모리 누수(memory leak) 때문에 정기적으로 프로세스를 재시작해야 할 경우도 있습니다.

게다가 납품하는 시스템이 외부에서는 접근이 불가능한 보안 네트웍 내부에 있는 경우 불의의 사태로 프로세스가 다운되었을 경우 자동으로 재시작하는 방법이 있어야 합니다.

전통적인 프로세스 자동 재시작 방법

전통적으로 리눅스(혹은 유닉스) 시스템에서 프로세스를 자동 재시작하는 방법은 inittab을 이용하는 방법입니다. /etc/inittab에 지정한 형식으로 실행할 프로세스를 명기하면 자동으로 런레벨에 맞게 프로세스를 실행하며 respawn 옵션을 주면 프로세스가 죽을 때 마다다 자동으로 실행시키기도 합니다.

id:2345:respawn:/path/to/process options

그런데 최근의 Ubuntu에서는 inittab 대신 upstart가 사용됩니다. inittab을 실행하는 init 프로세스는 동기식으로 프로세스들을 실행하는데, upstart는 비동기로 이벤트 기반으로 프로세스를 실행하고 관리합니다. 우분투 시스템을 보면 /etc/init 디렉토리에 upstart와 관련된 스크립트들을 볼 수 있습니다. 다음은 nmbd를 실행시키는 스크립트의 예입니다. 여기서도 respawn이라는 명령이 있습니다.

description "NetBIOS name server"
author      "Steve Langasek <steve.langasek@ubuntu.com>"

start on (local-filesystems and net-device-up IFACE!=lo)
stop on runlevel [!2345]
expect fork
respawn
pre-start script
        [ -f /etc/samba/smb.conf ] || { stop; exit 0; }
        install -o root -g root -m 755 -d /var/run/samba
        NMBD_DISABLED=`testparm -s --parameter-name='disable netbios' 2>/dev/null || true`
        [ "x$NMBD_DISABLED" = xYes ] && { stop; exit 0; }
        exit 0
end script

exec nmbd -D

이 외에도 별도로 설치해야 하는 daemontools를 이용하여 프로세스 관리를 할 수도 있는데, 설정이 다소 복잡하네요.

저는 이 중에서 inittab을 이용한 프로세스 관리를 해 본 적 있는데 시스템 환경이 달라져서 (예를 들어 디스크가 꽉 찼다던지) 프로세스가 정상적으로 실행될 수 없을 때 여유시간 없이 너무 빨리 프로세스를 재시작하는 경향이 있어서 문제였습니다. 게다가 inittab의 경우 root계정에서만 설정할 수 있다는 것도 아쉬운 부분이구요.

그래서 cron을 이용하여 프로세스를 관리하는 방법을 고안하게 되었습니다.

cron으로 프로세스 관리하기

cron은 분단위로 다양한 규칙을 적용하여 스케쥴을 운영할 수 있는 프로그램입니다. cron은 crontab을 통해서 스케쥴을 등록할 수 있는데 그 문법은 아래 그림과 같습니다. 만일 매분마다 실행하는 스케쥴이 필요하다면 * * * * * 와 같이 * 다섯개를 기록하면 됩니다.


기본적인 아이디어는 이렇습니다. 매 분마다 해당 프로세스가 살아있는지 확인하고 만일 프로세스가 다운되었으면 해당 프로세스를 재시작하게 하는 겁니다. 이를 위해서 프로세스가 살아있는지 확인하는 방법이 필요합니다. 이를 위해 가장 적합한 방법은 pgrep 명령을 쓰는 겁니다.


다음과 같은 쉘파일을 만듭니다.

#!/bin/sh
MYPROC="/path/to/process -i options"

/usr/bin/pgrep  -f  -x  "$MYPROC"  >  /dev/null  2>&1  ||  $MYPROC

위와 같이 작성하고 myproc.sh 이라는 이름으로 저장합니다. 위 스크립트는 MYPROC에 정의된 프로세스가 있는지 pgrep으로 확인하고 없으면 MYPROC을 실행하여 프로세스를 실행하게 됩니다. pgrep의 -f -x 옵션은 프로세스 이름을 대조할 때 전체(-f, full)를 사용하고 정확해야(-x, exact)함을 지시합니다.

"/dev/null" 2>&1 구문은 stderr(2) 출력을 stdout(1)로 보내라는 의미이며, 이것을 null 디바이스로 보내 표준출력으로 나오는 메시지들을 무시하게 됩니다. 관리하는 프로세스가 별도의 로그파일 기록 기능이 있다면 이렇게 처리해주면 됩니다.

스크립트에서 "||" 연산자는 앞에서 수행한 커맨드의 리턴값이 0이 아니면 (즉 실패라면) || 뒤의 명령을 실행하라는 겁니다. 만일 프로세스가 죽어 있다면 pgrep의 결과는 실패여서 0이 아닌 값이 나올 것이고, 그러면 || 뒤의 명령을 실행하게 되는 겁니다. ||과 반대되는 역할은 && 연산자입니다.

만일 관리하는 프로세스가 여러개라면 위의 쉘 스크립트에 같은 방식으로 계속 추가하면 됩니다.

마지막으로 crontab에 이 쉘을 등록하면 됩니다. "crontab -e"를 실행하여 다음과 같이 스케쥴을 등록합니다.

*  *  *  *  *   /bin/sh  /home/me/myproc.sh

이렇게 하면 끝입니다. 1분을 기다려서 제대로 프로세스가 뜨는지 확인하고, 이후 강제로 죽인 다음 프로세스가 1분내에 재시작되는지 확인해 보면 됩니다.

만일 매일 새벽 02:00에 프로세스를 죽이고 싶다면 pkill 커맨드를 이용하여 비슷한 방법으로 crontab에 등록하면 됩니다.

거의 모든 리눅스 및 유닉스 시스템에서 cron을 지원하기 때문에 이 방법을 이용하면 간편하고 동일한 방법으로 프로세스를 무인 관리할 수 있습니다.

댓글 2개: