Supervisorによるプロセス管理

Linux/Unixのプロセスを管理するpython製のOSSであるSupervisorを使っているソフトウェアに触れる機会があったのですが、あまり知識がなかったのでSupervisorについて調べてまとめてみました。

Supervisorについて

  • Linux/Unixのプロセスを管理するソフトウェアでPythonで開発されている。
  • クライアント/サーバー型である。
  • Python3の環境においてはdo not work[1]Supervirord4.0は、Python 2.6以上、Python 3.2以上で動作させられるよう計画されているとされており、Python2の環境での利用が推奨されている。
    (2016/11/10現在、githubのOverviewより)
  • Windows環境はサポートされていない。
  • init(Process id 1)の代替ではない。
  • プロセスがクラッシュした時、リスタートしてくれる。
  • プロセスを自身のサブプロセスとして実行するので、Pidfileのようなプロセスの実態との不一致がない。
  • サブプロセスをdaemon化しない。
  • プロセスをグループで管理することができ、”start all”や”restart all”のようにグループに対してコマンドを実行してくれる。
  • ShellやWebUIからプロセスの状態をコントロールでき、またXML-RPCインターフェースを持っている。
  • 十分に枯れている。

supervisorを使っているソフトウェアとして、私が触れたことがある例では、HadoopディストリビューションCDHの管理ソフトウェアであるClouderaManager[2]http://www.cloudera.com/products/cloudera-manager.htmlがあります。

Supervisorを構成するコンポーネント

Supervisorを構成しているコンポーネントは以下。

supervisord

子プロセスをコントロールするサーバープロセス。設定ファイルは、/etc/supervisord.confでINI形式[3]https://en.wikipedia.org/wiki/INI_fileで記述する。

supervisorctl

クライアントプログラム。UnixドメインソケットやTCPソケット経由でsupervisordと対話する。

設定は、[supervisorctl]セクションに記述する。

Web Server

supervisorctlの機能をブラウザ経由で実行できるWebインターフェース。

設定は、[inet_http_server]セクションに記述する。

インストール

pythonがインストールされていない場合は、まずpythonをインストールする必要がある。あとはsetuptoolsまたはpipでインストールできる。

# using easy_install
$ easy_install supervisor
# or using pip
$ pip install supervisor

ディストリビューションのパッケージの場合、例えばCentOSではyumでいける。以下はepel経由でインストールしている。

yum install -y --enablerepo=epel supervisor

以下のecho_supervisord_confコマンドで設定ファイルのsampleを確認でき、このテンプレートをベースに設定を記述していくことができる。

$ echo_supervisord_conf 
; Sample supervisor config file.
;
; For more information on the config file, please see:
; http://supervisord.org/configuration.html
;
; Notes:
;  - Shell expansion ("~" or "$HOME") is not supported.  Environment
;    variables can be expanded using this syntax: "%(ENV_HOME)s".
;  - Comments must have a leading space: "a=b ;comment" not "a=b;comment".
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)
;chmod=0700                 ; socket file mode (default 0700)
;chown=nobody:nogroup       ; socket file uid:gid owner
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))
;[inet_http_server]         ; inet (TCP) server disabled by default
;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))
[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)
;umask=022                   ; (process file creation umask;default 022)
;user=chrism                 ; (default is current user, required if root)
;identifier=supervisor       ; (supervisord identifier, default is 'supervisor')
;directory=/tmp              ; (default is not to cd during start)
;nocleanup=true              ; (don't clean up tempfiles at start;default false)
;childlogdir=/tmp            ; ('AUTO' child log dir, default $TEMP)
;environment=KEY="value"     ; (key value pairs to add to environment)
;strip_ansi=false            ; (strip ansi escape codes in logs; def. false)
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris              ; should be same as http_username if set
;password=123                ; should be same as http_password if set
;prompt=mysupervisor         ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history  ; use readline history if available
; The below sample program section shows all possible program subsection values,
; create one or more 'real' program: sections to be able to control them under
; supervisor.
;[program:theprogramname]
;command=/bin/cat              ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1                    ; number of processes copies to start (def 1)
;directory=/tmp                ; directory to cwd to before exec (def no cwd)
;umask=022                     ; umask for process (default None)
;priority=999                  ; the relative start priority (default 999)
;autostart=true                ; start at supervisord start (default: true)
;startsecs=1                   ; # of secs prog must stay up to be running (def. 1)
;startretries=3                ; max # of serial start failures when starting (default 3)
;autorestart=unexpected        ; when to restart if exited after running (def: unexpected)
;exitcodes=0,2                 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT               ; signal used to kill process (default TERM)
;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false             ; send stop signal to the UNIX process group (default false)
;killasgroup=false             ; SIGKILL the UNIX process group (def false)
;user=chrism                   ; setuid to this UNIX account to run the program
;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
;stdout_logfile=/a/path        ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false   ; emit events on stdout writes (default false)
;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
;stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false   ; emit events on stderr writes (default false)
;environment=A="1",B="2"       ; process environment additions (def no adds)
;serverurl=AUTO                ; override serverurl computation (childutils)
; The below sample eventlistener section shows all possible
; eventlistener subsection values, create one or more 'real'
; eventlistener: sections to be able to handle event notifications
; sent by supervisor.
;[eventlistener:theeventlistenername]
;command=/bin/eventlistener    ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1                    ; number of processes copies to start (def 1)
;events=EVENT                  ; event notif. types to subscribe to (req'd)
;buffer_size=10                ; event buffer queue size (default 10)
;directory=/tmp                ; directory to cwd to before exec (def no cwd)
;umask=022                     ; umask for process (default None)
;priority=-1                   ; the relative start priority (default -1)
;autostart=true                ; start at supervisord start (default: true)
;startsecs=1                   ; # of secs prog must stay up to be running (def. 1)
;startretries=3                ; max # of serial start failures when starting (default 3)
;autorestart=unexpected        ; autorestart if exited after running (def: unexpected)
;exitcodes=0,2                 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT               ; signal used to kill process (default TERM)
;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false             ; send stop signal to the UNIX process group (default false)
;killasgroup=false             ; SIGKILL the UNIX process group (def false)
;user=chrism                   ; setuid to this UNIX account to run the program
;redirect_stderr=false         ; redirect_stderr=true is not allowed for eventlisteners
;stdout_logfile=/a/path        ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
;stdout_events_enabled=false   ; emit events on stdout writes (default false)
;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
;stderr_events_enabled=false   ; emit events on stderr writes (default false)
;environment=A="1",B="2"       ; process environment additions
;serverurl=AUTO                ; override serverurl computation (childutils)
; The below sample group section shows all possible group values,
; create one or more 'real' group: sections to create "heterogeneous"
; process groups.
;[group:thegroupname]
;programs=progname1,progname2  ; each refers to 'x' in [program:x] definitions
;priority=999                  ; the relative start priority (default 999)
; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.
;[include]
;files = relative/directory/*.ini

supervisordの起動

以下でsupervisordのプロセスを起動する。

supervisord [options]

supervisordのコマンドラインオプション

-c FILE

  • 設定ファイルを指定。

-n, –nodaemon

  • デーモン化しない、foregroundで動作させる。
  • デフォルトでは、daemonで動作する。

-h, –help

  • ヘルプを表示する。

-u USER, –user=USER

  • プロセスownerをUSERにする。USERには、usernameまたはuser idを指定する。

-m OCTAL, –umask=OCTAL

  • supervisordが使うデフォルトマスク。例:022
  • デフォルトは、022

-d PATH, –directory=PATH

  • supervisordがdaemonとして実行されるときにcdするPATH。

-l FILE, –logfile=FILE

  • ログファイル。
  • デフォルトで、$CWD/supervisord.log

-y BYTES, –logfile_maxbytes=BYTES

  • ログファイルを指定されたBYTES(byte)でローテートする。
  • デフォルトで、50MB
  • 例:1 = 1 byte、1M = 1 000 000 bytes、1G = 1 000 000 000 bytes

-y NUM, –logfile_backups=NUM

  • ログファイルがバックアップされる世代数。
  • デフォルトで、10

-e LEVEL, –loglevel=LEVEL

  • ログレベル。例:trace、debug、info、warn、error、critical
  • デフォルトで、info

-j FILE, –pidfile=FILE

  • pidを書くファイル。
  • デフォルトで、$CWD/supervisord.pid

-i STRING, –identifier=STRING

  • 現在のsupervisorインスタンスを識別できる任意の文字列。
  • デフォルトは、supervisor

-q PATH, –childlogdir=PATH

  • AUTO指定の子プロセスのログディレクトリ。

-k, –nocleanup

  • 起動時にAUTO指定の子プロセスのログを削除しない。

-a NUM, –minfds=NUM

  • supervisordの利用可能なファイル記述子の上限(RLIMIT_NOFILE)を設定する。
  • NUMがhard limitを超える場合、hard = NUMとなる。
  • デフォルトは、1024

-t, –strip_ansi

  • 子プロセスのログからANSIエスケープシーケンス文字を除去する。

-v, –version

  • バージョンを表示する。

–profile_options=LIST

  • プロファイルのためのオプションリストをカンマ区切りで指定する。supervisordはプロファイラの下で動作する。
  • 例:cumulative、calls、callers

–minprocs=NUM

  • supervisordのプロセス生成数の上限(RLIMIT_NPROC)を設定する。
  • NUMがhard limitを超える場合は、NUM = hardとなる。
  • デフォルトは、200

自動起動設定

以下に各種Linux/Unixディストリビューションのinitスクリプトを公開してくれているので、環境に合わせてダウンロードして設置する。

https://github.com/Supervisor/initscripts

supervisorctlの実行

supervisorctlはコマンドラインツールで、supervisordに接続してプロセスの各種コントロール要求を送る。

supervisorctl [options] [action [arguments]]

supervisorctlのコマンドラインオプション

-c, –configuration

  • 設定ファイルを指定する。
  • デフォルトは、/etc/supervisord.conf

-h, –help

  • ヘルプを表示する。

-i, –interactive

  • 対話モードで起動する。
  • 引数なしでコマンドを実行するとpromptが起動する。

-s, –serverurl URL

  • 接続するサーバープロセス(supervisord)のURL。
  • デフォルトで、http://localhost:9001/

-u, –username

  • ユーザー認証のユーザー名。

-p, –password

  • ユーザー認証のパスワード。

-r, –history-file

  • 操作履歴を保持する(readlineがある場合)

コマンド例

### status
# 管理下のプロセスのステータスを確認
supervisorctl status
### start
# sampleプロセスを起動
supervisorctl start sample
### restart
# sampleプロセスを再起動
supervisorctl restart sample
# すべてのプロセスを再起動
supervisorctl restart all
### tail
# sampleプロセスのログを追尾する
supervisorctl tail -f sample
### stop
# sampleプロセスを停止
supervisorctl stop sample
### remove
# sampleプロセスを非アクティブ化
supervisorctl remove sample
### add
# sampleプロセスをアクティブ化
supervisorctl add sample
### fg
# sampleプロセスにフォアグランドで接続
supervisorctl fg sample
### reload
# supervisordを再起動
supervisorctl reload
### reread
# 設定をリロード
supervisorctl reread
### shutdown
# supervisordをshutdown
supervisorctl shutdown

サンプルプログラムをsupervisordで実行してみる

まずは、簡単なサンプルプログラムを用意しsupervisor管理下で実行してみる。

supervisord.conf

設定はとりあえず最小限で以下のとおり。

[inet_http_server]
port=127.0.0.1:9001
username=user
password=password
[supervisord]
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
username=user
password=password
[program:sample]
command=/bin/sh /path/to/hello.sh
process_name=%(program_name)s
stdout_logfile=hello.log

commandのhello.shは以下。

#!/usr/bin/env bash
i=0
while true; do
printf "hello = %d\n" $i
let i=i+1
sleep 1
done

以上を準備し、supervisordをforegroundで起動してみる。

$ supervisord --nodaemon
2016-11-12 21:05:40,078 INFO Increased RLIMIT_NOFILE limit to 1024
2016-11-12 21:05:40,086 INFO RPC interface 'supervisor' initialized
2016-11-12 21:05:40,087 INFO RPC interface 'supervisor' initialized
2016-11-12 21:05:40,087 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2016-11-12 21:05:40,087 INFO supervisord started with pid 15347
2016-11-12 21:05:41,094 INFO spawned: 'sample' with pid 15350
2016-11-12 21:05:42,105 INFO success: sample entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

supervisordのWebUIでプロセス管理

上記実行後、127.0.0.1:9001でHTTPサーバーが起動しており、以下にアクセスするとWebUIを確認できる。なお、アクセス時に認証を要求されるが、confで設定したuserとpasswordを指定すればよい。

http://localhost:9001/

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-27-12-02-08

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-20-53-16

上記では、プログラム名「sample」がrunning状態となっており、sampleのリンクやTail -fをクリックすると以下のような出力を確認できる。また、プロセス単体及びプロセスをまとめて起動・停止を行なうことができる。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-21-13-57

supervisorctlでsupervisordに接続

続いてsupervisorctlでアクセスしてみる。helpコマンドでコマンドリストが表示される。help <command>でコマンド詳細を確認できる。

$ supervisorctl 
sample                           RUNNING   pid 17832, uptime 0:01:31
supervisor> status
sample                           RUNNING   pid 17832, uptime 0:01:52
supervisor> help
default commands (type help <topic>):
=====================================
add    exit      open  reload  restart   start   tail   
avail  fg        pid   remove  shutdown  status  update 
clear  maintail  quit  reread  signal    stop    version
supervisor> help status
status <name>     Get status for a single process
status <gname>:*  Get status for all processes in a group
status <name> <name>    Get status for multiple named processes
status          Get all process status info

ここでは、先ほどのsampleプログラムを停止してみる。stopでプロセス名を指定しないとusageが表示される。続いてプロセス名を正しく指定して、stopコマンドを実行すると停止できる。

supervisor> stop
Error: stop requires a process name
stop <name>       Stop a process
stop <gname>:*        Stop all processes in a group
stop <name> <name>  Stop multiple processes or groups
stop all        Stop all processes
supervisor> stop sample
sample: stopped

WebUIでrunningとなっていたプロセスのステータスもstoppedになっていることを確認できる。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-22-13-30

プロセスが異常終了した場合

もしプロセスが何らかの理由でダウンした場合、デフォルトの設定では再度プロセスを開始する。killコマンドでSIGTERMを送ってみる。

2016-11-27 11:00:50,319 INFO exited: sample (terminated by SIGTERM; not expected)
2016-11-27 11:00:51,077 INFO spawned: 'sample' with pid 8563
2016-11-27 11:00:52,081 INFO success: sample entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

続いてSIGSEGVを送ってみる。

2016-11-27 11:06:07,606 INFO exited: sample (terminated by SIGSEGV; not expected)
2016-11-27 11:06:07,723 INFO spawned: 'sample' with pid 9243
2016-11-27 11:06:08,728 INFO success: sample entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

予期せぬ異常終了時にもプロセスがリスタートされていることを確認できた。ただ、本来はプロセスが異常終了しないように根本原因を特定し問題を解決する必要があるだろう。

プロセスを開始できなかった(終了する)場合

仮にプロセスが開始できなかった場合、リトライ回数の上限まで起動を試みた後fatal状態になる。

試しにスクリプトをexitstaus 0で終了させてみる。

#!/usr/bin/env bash
#
i=0
while false; do
printf "hello = %d\n" $i
let i=i+1
sleep 1
done

ログには以下のように出力された。

2016-11-27 15:10:12,856 INFO Increased RLIMIT_NOFILE limit to 1024
2016-11-27 15:10:12,863 INFO RPC interface 'supervisor' initialized
2016-11-27 15:10:12,864 INFO supervisord started with pid 23659
2016-11-27 15:10:13,867 INFO spawned: 'sample' with pid 23662
2016-11-27 15:10:13,875 INFO exited: sample (exit status 0; not expected)
2016-11-27 15:10:14,877 INFO spawned: 'sample' with pid 23663
2016-11-27 15:10:14,884 INFO exited: sample (exit status 0; not expected)
2016-11-27 15:10:16,887 INFO spawned: 'sample' with pid 23664
2016-11-27 15:10:16,894 INFO exited: sample (exit status 0; not expected)
2016-11-27 15:10:19,904 INFO spawned: 'sample' with pid 23665
2016-11-27 15:10:19,910 INFO exited: sample (exit status 0; not expected)
2016-11-27 15:10:20,915 INFO gave up: sample entered FATAL state, too many start retries too quickly

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-27-15-33-25

数回リトライした後FATAL状態に移行し、プロセス開始が停止されたことを確認できる。

以上サンプルプログラムをsupervisor経由で実行し、supervisorでプロセス管理するイメージを掴むことができた。運用ではsupervisord自体の監視が必要になるだろう。

supervisordのシグナルハンドリング

supervisordがハンドリングするシグナルには以下のようなものがある。

TERM、INT、QUIT

supervisordとそのすべての子プロセスをshutdownする。以下はSIGTERMを送った例。

kill -s TERM $(supervisorctl pid)

シグナルを受信したsupervisordは以下のようなログを出力する。

2016-11-14 15:51:08,677 WARN received SIGTERM indicating exit request
2016-11-14 15:51:08,677 INFO waiting for sample to die
2016-11-14 15:51:08,677 INFO stopped: sample (terminated by SIGTERM)

HUP

すべての子プロセスを停止し設定をリロード、続いてプロセスを開始する。

kill -s HUP $(supervisorctl pid)

シグナルを受信したsupervisordは以下のようなログを出力する。

2016-11-14 15:59:07,283 WARN received SIGHUP indicating restart request
2016-11-14 15:59:07,283 INFO waiting for sample to die
2016-11-14 15:59:07,284 INFO stopped: sample (terminated by SIGTERM)
2016-11-14 15:59:07,291 INFO supervisord started with pid 30326
2016-11-14 15:59:08,295 INFO spawned: 'sample' with pid 30337
2016-11-14 15:59:09,306 INFO success: sample entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

USR2

メインとすべての子プロセスのログを再オープンする。

kill -s USR2 $(supervisorctl pid)

シグナルを受信したsupervisordは以下のようなログを出力する。

2016-11-14 16:04:22,491 INFO received SIGUSR2 indicating log reopen request
2016-11-14 16:04:22,491 INFO supervisord logreopen

supervisord.conf

supervisord関連の設定はこのconfに記述していくことになる。読み込みの優先順位や主なセクションについてまとめる。詳細は、本家の公式ドキュメントまたはソースのoptions.pyを参照のこと。

設定ファイルの優先順位

設定ファイルは、以下の順で探索される。

  1. <supervisor_bindir>/etc/supervisord.conf
  2. <supervisor_bindir>/supervisord.conf
  3. supervisord.conf
  4. etc/supervisord.conf
  5. /etc/supervisord.conf
  6. /etc/supervisor/supervisord.conf’

[unix_http_server]セクション

UnixドメインソケットでlistenするHTTPサーバーの設定。

項目 説明 デフォルト
file socketのファイルのパス None
chmod fileのパーミッション 0700
chown fileのuserとgroup
例:supervisord:supervisord
supervisordを開始したuserとgroup
username HTTPサーバー認証で使うuser None
password HTTPサーバー認証で使うpassword
クリアテキストまたはsha-1
Basic認証で聞かれる
None

[inet_http_server]セクション

TCPソケットでlistenするHTTPサーバーの設定。

項目 説明 デフォルト
port listenするhost:port。
例1:ローカルアドレスで指定
127.0.0.1:9001
例2:ワイルドカードバインドで指定
*:9001
None
username HTTPサーバー認証で使うuser None
password HTTPサーバー認証で使うpassword
クリアテキストまたはsha-1
Basic認証で聞かれる
None

[supervisord]セクション

supervisordプロセスに関する設定。

項目 説明 デフォルト
logfile ログファイルのパス $CWD/supervisord.log
logfile_maxbytes ログファイルの最大サイズ 50MB
logfile_backups ログファイルを保持する世代数
0を指定すると保持されない
10
loglevel ログレベル info
pidfile プロセスIDが記録されるファイルパス $CWD/supervisord.pid
umask supervisordプロセスのumask 022
nodaemon デーモン化するかしないか
trueの場合、foregroundで動作する
false
minfds ファイル記述子の上限を変更する(RLIMIT_NOFILE)[4]http://docs.python.jp/2/library/resource.html 1024
minprocs プロセス生成数の上限を変更する(RLIMIT_NOPROC)[5]http://docs.python.jp/2/library/resource.html 200
nocleanup 起動時にAUTO指定の子プロセスのログのクリーンアップをしない false
childlogdir AUTO指定の子プロセスのログファイルを作成するディレクトリ tempfile.get_tempdir()
user supervisordがスイッチするuser None
directory supervisordがdaemon時にスイッチするディレクトリ None
strip_ansi 子プロセスのログファイルからANSIエスケープシーケンスを除去する false
environment supervisordに渡される環境変数(子プロセスにも引き継がれる) None
identifier supervisordインスタンスの識別子 supervisor

[supervisorctl]セクション

項目 説明 デフォルト
serverurl supervisordサーバーのURL http://localhost:9001
username HTTPサーバー認証のユーザー名 None
password HTTPサーバー認証のパスワード None
prompt promptで使われる文字列
例:%(ENV_USER)s
supervisor
history_file supervisorctlのコマンド履歴ファイル None

例:history_fileを指定

[supervisorctl]
history_file = supervisorctl_history

status、help、restartとコマンド実行して見た結果、以下の履歴が保存される。

$ cat supervisorctl_history 
status
help
restart sample

[program:x]セクション

supervisord管理下で動作させるプログラムの設定。xにはプログラムを識別する任意の文字列を指定する。

項目 説明 デフォルト
command プログラムの実行コマンド None
process_name プログラム名。numprocsが1より大きい場合は、process_num指定が必要
例:%(program_name)s_%(process_num)02d
%(program_name)s
numprocs プロセス数 1
numprocs_start プロセス数のオフセット値
例:numprocs = 2、numprocs_start = 2の場合
%(process_num)02dは、0203となる。
0
priority 起動、停止における優先度。値が小さいほど優先度が高い 999
autostart 自動起動設定 true
startsecs RUNNING状態とみなすまでの秒数 1
startretries 起動のリトライ回数 3
autorestart 予期せぬ終了時の再起動設定
unexpected:RUNNING状態でexitcodesで終了すると自動再起動
true:exitcodesに関わらず自動再起動
unexpected
exitcodes autorestartオプションのunexpectedで使われる終了コード。このリストにない終了コードでプロセスが終了した場合、自動再起動がかかる。 0,2
stopsignal プロセス停止に使われるシグナル TERM
stopwaitsecs プロセスにstopsignalが送られた後、OSがSIGCHILDをsupervisordに戻すのに待つ時間(秒) 10
stopasgroup プロセスグループに対してstopsignalを送信する。この値がfalseの場合。サブプロセスが実行されている場合はリペアレンティングされる。 false
killasgroup プロセスグループに対してSIGKILLを送信する。デフォルトでは、stopasgroupと同じ値となる。 false
user supervisordがプログラムを実行するときにスイッチするuser
redirect_stderr trueの場合、プロセスの標準エラー出力は標準出力にリダイレクトされる。 false
stdout_logfile プロセスの標準出力が記録されるファイル AUTO
stdout_logfile_maxbytes stdout_logfileのファイルサイズ上限 50MB
stdout_logfile_backups stdout_logfileが保存される世代数 10
stdout_capture_maxbytes プロセスがstdout capture modeの時、FIFOをキャプチャする最大バイト数 0
stdout_events_enabled trueの場合、プロセスがstdoutに書き込む時にPROCESS_LOG_STDOUTイベントが送られる。 0
stderr_logfile プロセスの標準エラー出力が記録されるファイル AUTO
stderr_logfile_maxbytes stderr_logfileのファイルサイズ上限 50MB
stderr_logfile_backups stderr_logfileが保存される世代数 10
stderr_capture_maxbytes プロセスがstderr capture modeの時、FIFOをキャプチャする最大バイト数 0
stderr_events_enabled trueの場合、プロセスがstderrに書き込む時にPROCESS_LOG_STDOUTイベントが送られる。 false
environment プロセスに引き継がれる環境変数
例:environment = HOGE=”foo”
None
directory supervisordがプロセスを起動する時にchdirするディレクトリのパス supervisorのものを継承
umask プロセスのumask supervisorのものを継承
serverurl プロセスと対話するサーバーURL
プロセス中では、環境変数でSUPERVISOR_SERVER_URLとして参照できる。
AUTO

[group:x]セクション

項目 説明 デフォルト
programs グループに属するプログラムをカンマ区切りで記述する None
priority 優先度 999

その他にも設定項目がありますが疲れたのでここで。。

stopasgroupオプションの動作について確認

stopasgroupオプションはデフォルトでfalseだが、trueにした状態でプロセスからサブプロセスを実行して親プロセスにシグナルを送出してみる。

#!/usr/bin/env bash
i=0
function handle_signal() {
echo "*** signal trapped ***"
#  kill $CPID
exit 0
}
function exec_process() {
local pname="$1"
while true; do
printf "$pname: hello = %d\n" $i
echo $SUPERVISOR_SERVER_URL
let i=i+1
sleep 1
done
}
trap "handle_signal" SIGTERM
PID=$$
echo "parent pid: $PID"
exec_process "child-1" &
CPID=$!
echo "child pid: $CPID"
exec_process "parent"

続いて、supervisord.confを以下のようにする。

[program:sample2]
command=/bin/sh /tmp/hello-child.sh
process_name=%(program_name)s
stdout_logfile=hello2-stdout.log
stderr_logfile=hello2-stderr.log
stopasgroup = true

supervisordを起動する。

$ supervisord

プロセスツリーを確認する。

$ pstree | grep [h]ello 
| \-+= 52797 guest /bin/sh /tmp/hello-child.sh
|   |-+- 52798 guest /bin/sh /tmp/hello-child.sh

サププロセスとして実行されていることを確認できる。続いて、supervisorctlからstopコマンドでSIGTERMを送る。

$ supervisorctl stop sample2

pstreeでgrepしても確認できないので、親・子プロセスとも停止している。

続いて、stopasgroup = falseにして実行して同様な操作をしてみたところ以下のようにリペアレンティングされていることを確認できる。

$ pstree | grep [h]ello 
|-+- 52960 guest /bin/sh /tmp/hello-child.sh

supervisorctl stopでなく端末からシグナルを送ってみる。最初の状態は以下。

$ pstree | grep [h]ello 
| \-+= 53171 guest /bin/sh /tmp/hello-child.sh
|   |-+- 53172 guest /bin/sh /tmp/hello-child.sh

続いてSIGTERMを送る。

$ kill -s TERM 53171

親プロセスは変わっていることを確認できる。

$ pstree | grep [h]ello 
|-+- 53172 guest /bin/sh /tmp/hello-child.sh

参考リンク

詳解UNIXプログラミング 第3版
翔泳社 (2014-06-05)
売り上げランキング: 40,034

脚注   [ + ]

1. Supervirord4.0は、Python 2.6以上、Python 3.2以上で動作させられるよう計画されている
2. http://www.cloudera.com/products/cloudera-manager.html
3. https://en.wikipedia.org/wiki/INI_file
4, 5. http://docs.python.jp/2/library/resource.html

byebyehaikikyou

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

シェアする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントする