2016-03-23

start-stop-daemonいじってみた

Ubuntuとかでnginxのinitscript見るとstart-stop-daemon使ってデーモン化していて、デーモン化する手段として覚えておいた方が良さそうだなーと思ったので触ってみました。

以下、(俺が)挙動を理解する用のサンプル

#!/bin/bash
DAEMON=/usr/bin/python
PIDFILE=/home/xxx/hoge.pid
DAEMON_ARGS=/home/xxx/hoge.py

case "$1" in
  "start")
    start-stop-daemon --start --quiet --background --exec $DAEMON --make-pidfile --pidfile $PIDFILE -- $DAEMON_ARGS
    result=$?
    if [ $result != "0" ]
    then
      pid=`cat $PIDFILE`
      echo "daemon is already running. (pid=${pid})"
      exit 1
    fi
    ;;
  "stop")
    start-stop-daemon --stop --quiet --pidfile $PIDFILE
    result=$?
    if [ $result != "0" ]
    then
      echo "daemon is not running. (check $PIDFILE)."
      exit 1
    fi
    rm -f $PIDFILE
    ;;
esac
--quiteしないで二重起動しようとすると
/usr/bin/python already running.

起動していないのに停止しようとすると

No process in pidfile '/home/tzm/hoge.pid' found running; none killed.

とメッセージが出力されます。

また、対象のPIDのプロセスが既に立ち上がってるかどうかは以下で確認可能です(戻り値が0であれば既に立ち上がっている状態)
$ start-stop-daemon --stop --quiet --signal 0 --pidfile $pidfile

以下の”標準出力をログに出力したい場合”でexec使う場合は、/bin/bashとexec先のプログラムが異なるので、startするたびに別のプロセスが生成されてしまいます。その場合は、上記コマンドを使って事前チェックで弾くか、--execではなく--startasを引数に利用することで二重起動を回避可能です(詳細は後述)。

標準出力をログファイルに出力したい場合

以下のようにして標準出力をするためにexec+リダイレクトしてあげればOK。
$ start-stop-daemon --start --make-pidfile --pidfile $PIDFILE \
 --background --exec /bin/bash -- -c \
 "exec $DAEMON $DAEMON_ARGS >> /tmp/hoge.log 2>&1"

デーモンはプロセスが端末と切り離されているので、通常のスクリプト実行とは挙動が異なる可能性があることに注意する必要があります。たとえばPythonだと通常のスクリプト実行と違い、標準出力やファイル出力がデフォルトでブロックバッファになります。

明示的に行バッファにするには

"exec stdbuf -oL -eL $DAEMON $DAEMON_ARGS >> /tmp/hoge.log 2>&1"

とするか、スクリプト内で明示的に行バッファを指定してファイル書き出しなどをすれば良いです。

Pythonの場合は、

open("/path/to/file.log", "w", 1)

とすればファイル書き出しは行バッファになり、標準出力の場合も色々と方法はあるみたいです。

--exec vs --startas

executableなscriptをデーモン化するときや、上記の標準出力をログファイルに出力するためにexecするようなケースにおいて、--execだと冪等性が確保できない(2回起動したら2つインスタンスが作成される)ので、startasを使うようです。startasはPIDだけで起動有無を確認する仕様らしいです。

start-stop-daemon: --exec vs --startas - Chris Lamb

その他メモ

対象のPIDのプロセスがあるかどうかを確認
[ ! -d /proc/$pid ]

実プロセスと実際のコマンドが有っているかの確認

$ cat /proc/$PID/cmdline | tr "\000" "\n"

おまけ(Upstart)

まぁこんな苦労しなくてもUpstart使えば一発です。
description "test python daemon"
author  "hoge <hoge@example.com>"

start on runlevel [2345]
stop on runlevel [016]

chdir /home/hoge
exec python ./fuga.py >> /var/log/fuga.log 2>&1
respawn

参考URL

このエントリーをはてなブックマークに追加