supervisord を使って Tornado をデプロイする
先日、 Tornado の使い方を解説しました。本日はその続きで、デプロイについて書きます。 Tornado で構築したアプリケーションを本格的にデプロイするためには、プログラムをデーモン(バックグラウンドプロセス)化する必要があります。 Tornado 自体にはその機能がないのでググってみると、どうやら Supervisor というプログラムを利用するのが一般的なようです。
この Supervisor がまた便利で、複数の通常アプリケーションをバックグラウンドで起動でき、その開始・終了を個別に管理できます。汎用的に作られているので Python 以外で構築されたアプリケーションも管理できますし、異常終了時の再起動やログのローテートの面倒もみてくれます。覚えておけばなにかと役に立ちそうですから、 Tornado アプリケーションのデプロイを題材にして使い方をまとめることにしました。
Supervisor とは
Supervisor は、 Python で構築されたプロセス管理システムです。複数のプログラムをバックグラウンドで起動し、それぞれ個別に起動・終了などを管理できます。常駐のデーモンである supervisord と管理 UI となる supervisorctl に分かれており、 TCP 通信によるリモート管理にも対応しています。その他、以下の特徴があります。
- フォアグラウンドで動作する通常のプログラムをバックグラウンドで起動できる
- 複数の管理対象プログラムをグループ化し、まとめて起動・終了が可能
- 起動・終了の順序(優先順位)も制御できる
- 異常終了した管理対象プログラムは自動で再起動する
- 管理対象プログラムの標準出力・エラー出力を supvervisorctl で表示できる
- Web ブラウザからも管理可能
- イベントシステムによって各プロセスのモニタリング等が可能
今回は単に Tornado アプリケーションをデーモン化する目的に限定して解説しますが、ほかにも多くのことができます。機会があれば、それらも試していきたいです。
インストール
インストールは easy_install 等を利用すれば簡単にできる…ようなのですが、私はいつもどおりシステムにはインストールせず、手動でプロジェクトに含める形にしました。基本的には、 Supervisor パッケージをダウンロードして setup.py でビルドし、 PYTHONPATH の通った場所にコピーするだけです。
curl -O http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0a12.tar.gz tar zxvf supervisor-3.0a12.tar.gz cd supervisor-3.0a12 python setup.py build cp -R build/lib/supervisor /path/to/your/project
ブラウザ経由の管理コンソールなどの機能も使うなら meld3 や elementtree といったモジュールも必要ですが、それらが不要ならば supervisor だけで大丈夫なようです。ただし、起動時に meld3 名前空間をインポートしようとするので、ダミーのモジュールを作って誤魔化しておきます ^^;
cd /path/to/your/project mkdir meld3 touch meld3/__init__.py
だいぶ手抜きですが、以上でインストールは終了です。
設定ファイルの作成
Supervisor で外部プログラムを管理するには、設定ファイル (supervisord.conf) を作成します。設定ファイルは以下の順序で検索されます。
- 起動オプションの -c で指定されたパス
- カレントディレクトリの supervisord.conf
- カレントディレクトリの etc/supervisord.conf
- /etc/supervisord.conf
したがって、私のようにプロジェクト内にすべてを収めたい人はプロジェクトのルート、もしくは etc ディレクトリに supervisord.conf を作成しておけば便利、ということですね。記述内容は supervisor/skel/sample.conf にサンプルがあるので、それをコピーして必要な箇所を変更するのがよいでしょう。
cd /path/to/your/project cp supervisor/skel/sample.conf ./supervisor.conf
設定ファイルは Windows の .ini ファイルと同様な形式です。多数のセクションがありますが、今回使うものは以下のとおり。
セクション | 説明 |
---|---|
unix_http_server | ローカルの supervisorctl からアクセスするための UNIX ドメインソケットの設定 |
supervisord | デーモン (supervisord) の設定 |
supervisorctl | 管理用コマンドラインツール (supervisorctl) の設定 |
program:〜 | 管理対象の外部プログラムの設定 |
rpcinterface:〜 | RPC インターフェースの追加 |
以下、それぞれのセクションについて簡単に説明していきます。
unix_http_server セクション
supervisord と supervisorctl の通信に使う UNIX ドメインソケットの設定です。 supervisord と superviserctl の両方が同じマシン上で動作しているときに使われます。今回は割愛しますが、ネットワーク経由でリモート管理する場合には TCP ソケットを設定する inet_http_server セクションの記述が必要です。
unix_http_server セクションの設定項目は以下のとおりです。
設定項目 | 説明 |
---|---|
file | ソケットファイルのパス名 |
chmod | ソケットファイルのパーミッション(デフォルト 0700) |
chown | ソケットファイルの所有者(デフォルトは supervisord を起動したユーザー) |
username | 認証用のユーザー名 |
password | 認証用のパスワード(生のパスワード、もしくは「{SHA}パスワードの SHA-1 ハッシュ」の形式で指定 |
inet_http_server のみを使う場合を除いて、 file の指定はほぼ必須です。これがないと supervisord と supervisorctl が通信できなくなってしまいます。
supervisord セクション
supervisord の設定を記述するセクションです。ログローテートの設定や実効ユーザーの指定など、サーバーでよくある設定項目が並びます。
設定項目 | 説明 |
---|---|
logfile | ログファイルのパス名(デフォルトはカレントディレクトリの supervisord.log) |
logfile_maxbytes | ログファイルの最大ファイルサイズ(デフォルト 50MB) |
logfile_backups | ログローテートで残すログファイルの世代数(デフォルトは 10 で、 0 なら削除しない) |
loglevel | 出力するログレベル(デフォルト info) |
pidfile | pidファイルのパス名(デフォルトはカレントディレクトリの supervisord.pid) |
umask | supervisord プロセスの umask(デフォルト 022) |
nodaemon | true を指定すると、 supervisord をデーモン化せず、フォアグラウンドで起動する |
minfds | supervisord の起動に必要なファイルディスクリプタの数(デフォルト 1024) |
minprocs | supervisord の起動に必要なプロセスディスクリプタの数(デフォルト 200) |
nocleanup | true にすると、起動時に古い子プロセスログを削除しない |
childlogdir | 子プロセスのログファイルを作成するパス(デフォルトは Python の tempfile.get_tempdir() の値) |
user | supervisord の実効ユーザー(root で起動した時のみ有効) |
directory | カレントディレクトリの指定 |
strip_ansi | true を指定すると、ログのエスケープシーケンスを削除する |
environment | 子プロセスに引き渡す環境変数を key1=value1,key2=value2 の形式で指定 |
identifier | supervisord プロセスの識別子 |
このセクションのほとんどは supervisord のコマンドラインオプションでも指定できるので、デフォルトを変更したいもののみ記述すればよいかと思います。
supervisorctl セクション
コマンドラインの管理インターフェースである supervisorctl の設定を行うセクションです。
設定項目 | 説明 |
---|---|
serverurl | 接続する supervisord の URL (デフォルトは http://localhost:9001) |
username | 認証用のユーザー名 |
password | 認証用のパスワード |
prompt | プロンプトとして表示する文字列(デフォルトは supervisor) |
history_file | readline ライブラリで使う履歴ファイルのパス名(デフォルトでは履歴ファイルを作成しない) |
serverurl の指定は unix_http_server セクションの file に指定したパスの前に「unix://」をつけたものを指定します(ローカルの supervisord に接続する場合)。 username と password にも unix_http_server の同名セクションと同じ物を指定しておかないと接続できないので注意してください。
program:〜
管理する外部プログラムの情報を記述するセクションです。実際のセクション名は「program:<任意の識別名>」となり、識別名を変えることで複数の外部プログラムの情報を記述できます。
設定項目 | 説明 |
---|---|
command | 起動する外部プログラムを指定します。必要に応じてオプションも指定可能 |
process_name | プロセスの名前を定義する(numprocsが1以上の場合に必須で、 %(process_num)s を含めること) |
numprocs | 起動するプロセス数(デフォルト 1) |
numprocs_start | プロセス番号のオフセット(デフォルト 0) |
priority | 起動・終了の優先順位(値が小さいほど先に開始し、後に終了する。デフォルト 999) |
autostart | false を指定すると supervisord の起動時に自動起動しなくなる |
autorestart | プロセス終了時の再起動の指定(true なら常に再起動、 false なら常に再起動しない、 unexpected なら 終了コードが exitcodes のあるもの以外なら再起動) |
startsecs | ここに指定した秒数より早く終了したら、終了コードにかかわらず起動失敗とみなす(デフォルト 1) |
startretries | 起動に失敗した場合にリトライする回数(デフォルト 3) |
exitcodes | 正常終了とみなす終了コード(デフォルト 0, 2) |
stopsignal | プロセスを終了させるために使うシグナル(デフォルト TERM) |
stopwaitsecs | この秒数が経ってもプロセスが終了しない場合、 SIGKILL を送信する(デフォルト 10) |
killasgroup | true なら、 SIGKILL を送信する際にプロセスグループ全体に送信する |
user | プロセスを実行するユーザー(supervisord が root で実行されているときのみ) |
redirect_stderr | true なら、標準エラー出力を supervisord の標準出力にリダイレクトする |
stdout_logfile | 標準出力ログのファイル名 |
stdout_logfile_maxbytes | 標準出力ログがこのサイズを超えたらローテートする(デフォルト 50MB) |
stdout_logfile_backups | ログローテートで残す世代数 |
stdout_capture_maxbytes | Capture Mode 時のバッファサイズ(デフォルト 0) |
stdout_events_enabled | true を指定すると、標準出力への出力時に PROCESS_LOG_STDOUT イベントを発生させる |
stderr_logfile | 標準エラー出力のファイル名 |
stderr_logfile_maxbytes | 標準エラー出力ログがこのサイズを超えたらローテートする(デフォルト 50MB) |
stderr_logfile_backups | ログローテートで残す世代数 |
stderr_capture_maxbytes | Capture Mode 時のバッファサイズ(デフォルト 0) |
stderr_events_enabled | true を指定すると、標準エラー出力への出力時に PROCESS_LOG_STDERR イベントを発生させる |
environment | 環境変数を key1=value1,key2=value2 の形式で指定 |
directory | 起動時のカレントディレクトリの指定 |
umask | umask の指定 |
serverurl | SUPERVISOR_SERVER_URL 環境変数に渡す値(デフォルトは supvervisord の UNIX ドメインソケットの URL) |
このセクションは最低限 command さえ記述すれば使えるはずです。あとは必要に応じて定義すればよいでしょう。
rpcinterface:〜
このセクションは supvervisord が使用する RPC インターフェースを拡張するためのものです。通常は以下の記述を含めておけば OK です。
[rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
Tornado アプリを起動する設定ファイルの例
設定ファイルの記述方法がわかったので、 Tornado アプリケーションを起動するための設定を書いてみます。 Tornado アプリケーションのエントリファイルは main.py で、 --port オプションで listen するポート番号が指定できると仮定しています。
[unix_http_server] file=/tmp/supervisor.sock [supervisord] logfile=%(here)s/log/supervisord.log pidfile=%(here)s/log/supervisord.pid [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] serverurl=unix:///tmp/supervisor.sock [program:tornado] command=python %(here)s/main.py --port=%(process_num)s --logging=info --log_file_prefix=%(here)s/log/%(program_name)s-%(process_num)s.log process_name=%(program_name)s-%(process_num)s directory=%(here)s numprocs=4 numprocs_start=8080 stdout_logfile=NONE stderr_logfile=NONE
何ヶ所か %(〜)s という記述を使っていますが、これは変数展開です。括弧の中に変数名を記述し、括弧に続けて Python のフォーマット文字列を指定します。変数名としては以下のものが使えます。
変数名 | 説明 |
---|---|
here | 設定ファイルがあるディレクトリのパス名 |
program_name | program:〜 セクションの「〜」部分 |
group_name | group:〜 セクションのグループ名(だと思う ^^;) |
process_num | プロセス番号 |
host_node_name | Python の platform.node() の値 |
ENV_環境変数名 | supervisord に与えられた環境変数の値 |
また、上記では Supervisord によるロギングを無効にして Tornado 側でログを出力していますが、そのほうがオーバーヘッドが少ないかなと思ってそうしただけで、あまり深い考えはありません。とくに開発中は supervisord のログ機能を使ったほうが supervisorctl でログの閲覧とかもできて便利かも。
supervisord の起動
以上で準備ができたので、 supervisord を起動してみましょう。 easy_install や setup.py install できちんとインストールしたなら supervisord コマンドで起動できるのですが、今回はビルドしたものをプロジェクトディレクトリにコピーするというイレギュラーな方法をとっているので、以下のコマンドで起動する必要があります。
cd /path/to/your/project PYTHONPATH=. python supervisor/supervisord.py
これで、 supervisord が起動し、同時に設定ファイルの指定に従って管理対象のプログラムも順次起動されます。起動オプションとしては以下のものが指定できます。ほとんどは設定ファイルの supervisord セクションの指定と同じです。
オプション | 説明 |
---|---|
-c | 設定ファイルのパス |
-n | フォアグラウンドで起動 |
-h | 使い方を表示 |
-u | 実効ユーザー |
-m | umask を指定 |
-d | カレントディレクトリの指定 |
-l | ログファイルのパス |
-y | ログファイルの最大サイズ |
-z | ログの世代数 |
-e | ログレベル |
-j | .pid ファイルのパス |
-i | supervisord の識別子 |
-q | 管理対象プログラムのログの出力先ディレクトリ |
-k | 管理対象プログラムのログの自動削除をしない |
-a | 起動に必要なファイルディスクリプタの数 |
-t | ログからエスケープシーケンスを削除する |
-v | supervisord のバージョン番号を表示 |
--profile_options | プロファイリングの指定 |
--minprocs | 起動に必要なプロセスデスクリプタの数 |
supervisord をマシンの起動時に自動起動させたいなら、単に /etc/rc.local などで上記のコマンドを実行すればよいでしょう。
supervisorctl による管理
supervisorctl を使うと、各プログラムの状態の確認や、起動・終了・再起動といった管理が可能です。 supervisord と同様に普通は supervisorctl コマンドとして起動できるのですが、ここでのインストール方法では以下の方法で起動します。
cd /path/to/your/project PYTHONPATH=. python supervisor/supervisorctl.py
すると、 supervisor> というプロンプトとともにコマンドの入力状態になり、インタラクティブに各プロセスの管理が行えます。例えば、 status コマンドを実行すると、各プロセスの状態が表示されます。
supervisor> status tornado:tornado-8080 RUNNING pid 880, uptime 0:00:21 tornado:tornado-8081 RUNNING pid 881, uptime 0:00:21 tornado:tornado-8082 RUNNING pid 882, uptime 0:00:21 tornado:tornado-8083 RUNNING pid 883, uptime 0:00:21
利用できる主なコマンドは以下のとおりです。
コマンド | 説明 |
---|---|
start <name> | プログラムを起動 |
stop <name> | プログラムを終了 |
restart <name> | プログラムを再起動 |
fg <process> | process をフォアグラウンドにする |
status | 各プログラムの状態を表示 |
reread | デーモンの設定ファイルを再読み込みする |
reload | supervisord をリスタート |
shutdown | supervisord を終了させる |
quit | シェルを終了 |
help | 使い方を表示 |
コマンドは supervisorctl.py のコマンドラインでも指定できます。例えば Tornado のすべてのプロセスを再起動するには以下のようにします。
PYTHONPATH=. python supervisor/supervisorctl.py restart 'tornado:*'
この方法ならインタラクティブシェルを起動せずにコマンドが実行できるので、シェルスクリプトなどを利用した自動化も簡単です。
以上で Supervisor を使って Tornado アプリケーションをデーモン化し、バックグラウンド動作させることができました。今回利用した Supervisor の機能はごく一部ですが、それでもたいへん便利です。今後、デーモン化や各種サーバー管理の手段として活用していこうと思っています。
詳しくはこちらの記事をどうぞ!
この記事にコメントする