2012年10月17日

プロセス管理に daemontools ではなく Supervisor を利用してみませんか

概要

サーバでは様々なプロセスを実行する必要があるが、これをどう管理するかは悩み所になる。
ここでは強力で柔軟なプロセス管理ツール「Supervisor」に関して記述する。

Supervisorに関して

Supervisor」は Python で記述された、プロセス管理ツール。
プロセス管理ツールと言うと「upstart」や「daemontools」が利用されていると思われる。
「upstart」は強力ではあるが、柔軟性がまだそれほど無いため、高度なプロセス管理はまだ難しい状況だと思われる。
「daemontools」は ノウハウが蓄積されており、強力で柔軟性が高いツールだが、2001年以降公式では更新されておらず、動作が不安定にってしまう環境も存在している。パッチを当てれば良いが、環境によって動作が変化してしまう場合もあり、安心して利用できない状況になってしまった。
Supervisor」は積極的に開発され、非常に安定したツールとなっている。
機能的には「daemontools」とほぼ同様の事が可能。
Python で記述されることで、Unix互換システムにおいて、快適に動作する。当然 Mac でも動作する。
現在の Linux サーバは Python はほぼ必須でインストールされている、という状況なので、ツールが Python で記述されていることは利点の一つになりえる。

Supervisorの利点は以下となる。全機能はマニュアル「Supervisor」を参照。

  • 積極的に開発されている
  • 設定ファイルが比較的簡単
  • プロセスのグループ化が容易
  • プロセスの状態により処理を実行することが可能
  • サブプロセスの管理も可能
  • ログを高度に処理できる
  • Webインターフェースが存在する
  • XMP-RPCインターフェースが存在する

インストール

Supervisorをインストールする場合、通常virtualenvにより環境を構築する。
参考に、以下インストール手順を示した fabfile を配置した。以下のスクリプトはインストールディレクトリは「/opt/python」で、作業ディレクトリは「/opt/supervisor」として記述してある、また、この fabfile はあまり、汎用的には記述していない、あくまで参考である。

実行は「fab install」で実行可能

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fabric.decorators import task
from fabric.api import (
    sudo,
    run,
    cd,
    put,
    env,
)
from fabric.contrib.files import exists


@task
def install():
    """
    Supervisor
    """
    setup_venv()
    install_supervisor()
    setup_bin()
    setup_conf()
    setup_upstart()


@task
def setup_venv():
    """
    virtualenv による Python 環境構築
    """
    with cd(env.path):
        run('wget https://raw.github.com/pypa/virtualenv/master/virtualenv.py')
        sudo('python virtualenv.py --distribute /opt/python')


@task
def install_supervisor():
    """
    Supervisor インストール
    """
    run('source /opt/python/bin/activate')
    sudo('/opt/python/bin/pip install Supervisor')


@task
def setup_bin():
    """
    実行ファイル設定
    """
    work_dir = '/opt/supervisor/bin'
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}/*'.format(work_dir),
        '{0}/'.format(work_dir), use_sudo=True)
    sudo('chown root:root {0}/*'.format(work_dir))
    sudo('chmod 755 {0}/*'.format(work_dir))


@task
def setup_conf():
    """
    設定ファイル
    """
    work_dir = '/opt/supervisor'
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}/etc'.format(work_dir))
        sudo('mkdir -p {0}/var/log'.format(work_dir))
        sudo('mkdir -p {0}/var/run'.format(work_dir))

    work_dir = '/opt/supervisor/etc'
    conf_file = '{0}/supervisord.conf'.format(work_dir)
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)

    work_dir = '/opt/supervisor/etc/supervisor.d'
    conf_file = '{0}/*.conf'.format(work_dir)
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)


@task
def setup_upstart():
    """
    upstart 設定
    """
    conf_file = '/etc/init/supervisor.conf'
    put('./fabfile{}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)
    sudo('chown root:root {0}'.format(conf_file))

管理用スクリプトの配置

Supervisor をインストールすると、インストール場所の「bin」以下に「supervisord」と「supervisorctl」の2ファイルが生成される。
これを直接利用しても良いが、自分は、一つのサーバで複数のSupervisorを起動する要件が発生する場合があるため、以下のようにスクリプトを作成している。
このスクリプトは実行する Python の指定と、設定ファイルの指定をしているだけだが、用途によっては便利だろう。

以下が「supervisord」と同じ機能のファイル

#!/opt/python/bin/python
# -*- coding: utf-8 -*-
import os
import sys

SRC_DIR=os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.argv.extend(['-c', '{0}/etc/supervisord.conf'.format(SRC_DIR)])

import supervisor.supervisord

if __name__ == '__main__':
    supervisor.supervisord.main()

以下が「supervisorctl」と同じ機能のファイル。

#!/opt/python/bin/python
# -*- coding: utf-8 -*-
import os
import sys

SRC_DIR=os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.argv[1:1] = ['-c', '{0}/etc/supervisord.conf'.format(SRC_DIR)]

import supervisor.supervisorctl

if __name__ == '__main__':
    supervisor.supervisorctl.main(sys.argv[1:])

設定ファイル

設定ファイルの雛形は「echo_supervisord_conf」プログラムによって生成可能。
とりあえず以下のように記述すると最低限の動作はする。「%(here)s」は設定ファイルのあるディレクトリを示す。
「include」の「file」は現在のバージョンではまだ「%(here)s」が使えない。

[supervisord]
childlogdir = %(here)s/../var/log
logfile = %(here)s/../var/log/supervisor.log
logfile_maxbytes = 50MB
logfile_backups = 10
loglevel = info
pidfile = %(here)s/../var/run/supervisor.pid
umask = 022
nodaemon = false
nocleanup = false

[inet_http_server]
port = 127.0.0.1:9001
username =
password =

[supervisorctl]
serverurl = http://127.0.0.1:9001
username =
password =

[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface

[include]
files = /opt/supervisor/etc/supervisor.d/*.conf

upstartの設定

Supervisor自身をSupervisorで管理することはできないので、Supervisorは upstartで管理する。
以下のようなファイルを「/etc/init」以下に「supervisor.conf」として配置する。

description     "supervisor"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

chdir /opt/supervisor
exec sudo -i /opt/python/bin/python /opt/supervisor/bin/supervisord -n -d /opt/supervisor

pre-stop exec sudo -i /opt/python/bin/python /opt/supervisor/bin/supervisorctl stop all

プロセス管理設定

これで Supervisor は起動している。「supervisorctl status」を実行するとまだ管理対象が存在しないことがわかる。
とりあえず、nginx の管理を設定してみる。

管理対象のプロセスはフォアグラウンドで起動させる必要がある。nginx の設定を変更する。

daemon off;

Supervisor の設定ファイルで「include」の「files」で指定したディレクトリに以下のようなファイルを「nginx.conf」のような名前で配置する。

[program:nginx]
command=/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf
redirect_stderr=true
stdout_logfile=%(here)s/../var/log/%(program_name)s_stdout.log
stderr_logfile=%(here)s/../var/log/%(program_name)s_stderr.log
autostart=true
autorestart=true

ここで注意すべきなのは「%(here)s」は、元設定ファイルのカレントディレクトリになるということ。パスには注意する。
以下のようにすることで、有効になる。

# 設定読み込み
supervisorctl reread
# 設定反映
supervisorctl update
# 確認
supervisorctl status

設定を反映するまでは、有効にならない。

以下のように、起動、終了、再起動が可能。

supervisorctl stop nginx
supervisorctl start nginx
supervisorctl restart nginx

「autostart=true」を設定しているので、Supervisor が起動すると自動起動する。
「autorestart=true」を設定しているため、異状終了しても管理対象プロセスは復旧してくる。kill などして、確認してみると良い。

Webインターフェース、XML-RPC

SupervisorはHTTPのWebインターフェエースをもっている。また XML-RPC による管理も可能。
HTTPSを利用したい場合は、nginx などの HTTPサーバを経由させれば良い。
上の設定ファイルを利用している場合はポート9001になる。

その他機能

他の機能はマニュアルを読むのが良い。機能は高機能なので、いろいろできる。

blog comments powered by Disqus