년도별 글 목록: 2014

Ubuntu 14.04 에서 PHP로 Daemon 만들기

우분투에서 PHP로 데몬(Daemon)을 만드는 방법은 어렵지 않다.

예제를 통해 직접 데몬을 만들어 보자. 데몬을 만들려면 2개의 파일이 필요한데 데몬을 동작시키는(시작과 종료) 파일과 데몬으로 동작할 파일이다.

 

예제를 위한 디렉토리 준비 및 파일구조 계획

예제 디렉토리를 만든다.

$ mkdir /home/my/php_daemon
$ mkdir /home/my/php_daemon/bin
$ mkdir /home/my/php_daemon/log

예제를 진행하면서 아래와 같은 파일구조를 가져갈 것이다.

  • 데몬을 동작시키는 파일: /home/my/php_daemon/pug
  • 데몬으로 동작할 파일: /home/my/php_daemon/bin/pug
  • Log 파일: /home/my/php_daemon/log/pug.log
  • PID 파일: /home/my/php_daemon/log/pug/pug.pid

 

데몬을 동작시키는 파일 만들기

데몬을 동작시키는 파일은 우분투에서 뼈대 파일(/etc/init.d/skeleton)을 제공해준다. 단순히 뼈대 파일을 복사해서 환경에 맞게 편집하면 된다.

$ cp /etc/init.d/skeleton /home/my/php_daemon/pug
$ chmod 775 /home/my/php_daemon/pug
$ vi /home/my/php_daemon/pug
...
# PATH에 데몬으로 동작할 파일이 있는 디렉토리 경로를 추가한다.
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/home/my/php_daemon/bin
DESC="PHP User Group"
NAME=pug
# 데몬으로 동작할 파일 경로
DAEMON=/home/my/php_daemon/bin/$NAME
DAEMON_ARGS=""
# PID의 디렉토리 경로가 .../$NAME/$NAME.pid 임에 유의
PIDFILE=/home/my/php_daemon/log/$NAME/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
...

다른 데몬들과 마찬가지로 service 명령으로 데몬을 실행하기 위해서는 /etc/init.d 디렉토리에 데몬을 동작시키는 파일이 있어야 한다. 소스코드를 관리하기 쉽도록 파일을 복사하는 대신에 심볼릭 링크를 사용하겠다.

$ sudo ln -s /home/my/php_daemon/pug /etc/init.d/

여기까지 해서 데몬을 동작시키는 파일은 준비가 되었다.

 

데몬으로 동작할 파일 만들기

파일이 데몬으로써 동작하기 위해서는 프로세스 생성하거나 PID 파일을 만드는 뻔하고 복잡한 일이 요구되는데 다행히 이런 것들이 미리 구현되어 있는 System_Daemon 라이브러리가 있다.

Composer를 이용해 System_Daemon 라이브러리를 설치한다. Composer를 설치하는 방법은 바뀔 수 있으니 공식 사이트의 Installation 을 참고하자.

$ cd /home/my/php_daemon/
$ curl -sS https://getcomposer.org/installer | php
$ vi composer.json
{
    "require": {
        "pear/system_daemon": "dev-master"
    }
}
$ ./composer.phar install

이제 System_Daemon 라이브러리를 이용해서 데몬으로 동작할 파일을 만들어보자.

$ vi /home/my/php_daemon/bin/pug
#! /usr/bin/php
<?php
require __DIR__ . '/../vendor/autoload.php';

$options = array(
    'appName' => 'pug',
    'appDir' => __DIR__,
    'appDescription' => 'PHP User Group',
    'authorName' => 'XXXX',
    'authorEmail' => '[email protected]',
    'sysMaxExecutionTime' => '0',
    'sysMaxInputTime' => '0',
    'sysMemoryLimit' => '1024M',
    'appRunAsGID' => 1000,
    'appRunAsUID' => 1000,
    'logLocation' => '/home/my/php_daemon/log/pug.log',
    'appPidLocation' => '/home/my/php_daemon/log/pug/pug.pid',
);

// 데몬 옵션
System_Daemon::setOptions($options);
// 데몬 시작
System_Daemon::start();

$count = 0;
while (! System_Daemon::isDying()) {
    ++$count;
    // 로그파일에 출력
    System_Daemon::info(date('Y-m-d H:i:s') . " - {$count}");
    // 5초 휴식
    System_Daemon::iterate(5);
}

// 데몬 종료
System_Daemon::stop();

잊지말고 실행 권한도 부여하자.

$ chmod 775 /home/my/php_daemon/bin/pug

System_Daemon 옵션중 appPidLocation 의 값이 /home/my/php_daemon/pug 파일의 PIDFILE 값과 동일해야 함에 유의하자.

위 appPidLocation 값은 반드시 …/pug/pug.pid 이어야 한다. …/pug.pid 또는 …/other/pug.pid 로 하면 데몬을 실행할때 아래와 같은 오류가 발생한다.

err: Since version 0.6.3, the pidfile needs to be in it’s own subdirectory like: %s/pug/pug.pid

 

데몬 실행하기

다른 데몬들을 실행하는 것과 동일하게 실행하면 된다.

# 데몬 시작
$ sudo service pug start

# 실행중인 데몬의 PID 값
$ cat /home/my/php_daemon/log/pug/pug.pid

# 데몬이 실행되는 동안 로그가 쌓이는 것 확인
$ tail -n 10 /home/my/php_daemon/log/pug.log

# 데몬 종료
$ sudo service pug stop

 

System_Daemon 추가 정보

위의 데몬 예제에서는 System_Daemon::isDying() 메소드를 이용해서 데몬의 무한루프로 동작을 탈출시켰다. 그런데 어떤 상황에서는 System_Daemon::isDying() 메소드만으로는 데몬의 무한루프 동작을 탈출하기 어려울 때가 있다. 이때는 System_Daemon::setSigHandler() 메소드를 활용하면 된다.

#! /usr/bin/php
<?php
declare(ticks = 1);

function callbackSignalTerm($signal) {
    if ($signal === SIGTERM) {
        System_Daemon::stop();
    }
}

System_Daemon::setSigHandler(SIGTERM, 'callbackSignalTerm');
...
System_Daemon::start();
...

PHP 코드 시작 첫줄의 declare(ticks = 1) 가 없으면 무한반복 상태에서 System_Daemon::setSigHandler() 메소드에 등록한 함수가 호출되지 않는다. declare(ticks = 1) 대해서는 PHP 메뉴얼을 살펴보자.

 

문제에 대한 대처

데몬을 만들다가 발생하는 문제는 대부분 아래와 같으므로 알 수 없는 문제가 발생하면 하나씩 체크해보자.

  • 데몬 관련 파일의 실행 권한
  • 로그나 PID 파일의 쓰기 권한
  • 디렉토리나 파일 경로 오류

 

참고자료