Server::Starter、Starman、Starletを使ったオレオレ運用方法

インターネットからリクエストを直接受け付けるのはApacheやnginxで、そこからリバースプロキシでPSGIアプリを使ってレスポンスを返しているというケースを想定して書いてます。 やり方は色々とあるんだろうけれど、apachectlライクにstart,stop,restartでお手軽に運用したいのでstart_serverスクリプト(Server::Starter)をラッピングしたものを書きました。

使い方は下記の通りです。

 usage: /path/to/script/appctl --stage=[development|production] [--start|--stop|--restart]

githubのソースはこちらで、以下はappctlスクリプトのソースです。ログのリダイレクトや、starman, starletの個別のオプションは直に変えてください。

#!/home/kw/perl5/perlbrew/perls/perl-5.14.3/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Data::Dumper;

my $stage = 'development';
my $start;
my $stop;
my $restart;
GetOptions(
    'stage=s' => \$stage,
    'start'   => \$start,
    'stop'    => \$stop,
    'restart' => \$restart
) or usage();

unless ( $stage =~ /development|production/ ) {
    usage();
    exit;
}

my $nohup        = "/usr/bin/nohup";
my $start_server = "/home/kw/perl5/perlbrew/perls/perl-5.14.3/bin/start_server";
my $plackup      = "/home/kw/perl5/perlbrew/perls/perl-5.14.3/bin/plackup";
my $server       = 'Starlet';
my $app          = "/home/kw/git_works/unko/server_starter/myapp.pl";
my $port         = 3000;
my $pid_file     = "/home/kw/tmp_app/app01.pid";
my $status_file  = "/home/kw/tmp_app/app01.status";

# print Dumper $stage
# print Dumper $start;
# print Dumper $stop;
# print Dumper $restart;

if ($start) {
    unless ( -e $pid_file && -e $status_file ) {
        my $cmd =
            "$nohup $start_server "
          . "--pid-file=$pid_file "
          . "--status-file=$status_file "
          . "--port=$port "
          . " -- $plackup -E $stage -s $server "
          . "--max-workers=5 "
          . "--max-reqs-per-child=1000 "
          . " $app & ";
        warn $cmd;
        system($cmd);
    }
    else {
        warn "server is already running..";
        exit;
    }
}
elsif ($stop) {
    if ( -e $pid_file && -e $status_file ) {
        my $cmd = "/bin/cat $pid_file | xargs kill -TERM ";
        warn $cmd;
        system($cmd);
    }
    else {
        warn "pid_file and status_file is NOT exists..";
        exit;
    }
}
elsif ($restart) {
    if ( -e $pid_file && -e $status_file ) {
        my $cmd =
            "$nohup $start_server "
          . "--pid-file=$pid_file "
          . "--status-file=$status_file "
          . "--restart ";
        warn $cmd;
        system($cmd);
    }
    else {
        warn "server is not running..";
        exit;
    }
}
else {
    usage();
}
exit;

##############
## functions
sub usage {
    print << "    EOT";
      usage: /path/to/script/appctl --stage=[development|production] [--start|--stop|--restart]
    EOT
    exit;
}

以下は調査したメモです。

===================================================================
Server::Starter(0.12)に関して
・hot deployを簡単に実行できるようにしたツール
・apachectlのrestartはstop, startの実行。gracefulは、今現在処理中の
 リクエストを処理しきってからstop, start。
・hot deployは、stopとstartの間の切れ目がないように継続してリクエストを
 処理できるような仕組み。(通常、サーバーをrestartする時はstopしてから
 startとなるので、stopしてからstartする間のリクエストは処理できない
 期間が発生する。)
・メモリ上に大きなデータを乗せてサーバを動かしたい場合など、再起動に
 大きな時間が必要となる時にhot deployが役にたつ。
・Server::Starterをインストールするとstart_serverというデーモンを起動する
 プログラムがインストールされるので、これを使ってサーバアプリを起動する。
・「Server::Starterから学ぶhot deployの仕組み」が比較的わかりやすい。
  http://shibayu36.hatenablog.com/entry/2012/05/07/201556


使い方
start_server --port=80 -- plackup -s Starlet your-app.psgi

オプション
--port=(port|host:port)
  ポートの指定

--path=path
  unix socketを使った場合のパス

--interval=seconds
  サーバーを再起動する時の最小インターバル(default:1)

--signal-on-term=Signal
  start_serverがSIGTERMを受け取った時に起動しているサーバープログラムに
  送られるシグナル。(default: SIGTERM)

--pid-file=filename
  このオプションがセットされた場合、start_serverのプロセスIDが
  ファイルに記述される

--status-file=filename
  このオプションがセットされた場合、サーバープロセスのステータスを
  記述します。

--restart
  サーバをhot deployするためのコマンド。
  start_serverのプロセスIDを--pid-fileから読み込んでSIGHUPを送ります。
  --status-fileをチェックしてdeploy前のサーバがdieするまでwaitします。

--help

--version

===================================================================
Starlet(0.18)に関して
・HTTP/1.0 serverでkeep-aliveをサポート
・リバースプロキシの裏で動くHTTPサーバに適している(DESCRIPTIONより)
・Parallel::Preforkを使ったPreForkサーバ, graceful shutdownが可能
・Server::Starterを使ってhot deployが可能
・HTTP::Parser::XSを使って高速化

オプション
--max-workers=#
  ワーカープロセスの数(default:10)

--timeout=#
  タイムアウト設定(default:10)

--keepalive-timeout=#
  keepaliveの継続秒数(deafult:2)

--max-keepalive-reqs=#
  1つのkeepaliveコネクションで返すことのできるリクエストの数。
  1に設定された場合、keepaliveはoffとなる(default:1)

--max-reqs-per-child=#
  worker processごとの処理するリクエスト数(default:100)

--min-reqs-per-childe=#
  設定するとmax-reqs-per-childeで設定された数との間で、worker process
  ごとの処理するリクエスト数がランダムかされる。(default:none)
  (ワーカープロセスの終了時期をずらしてforkの負荷を集中させない)
  http://blog.kazuhooku.com/2011/04/web-serverstarter-parallelprefork.html

--spawn-interval=#
  間隔をあけてワーカープロセスの再起動をするにための設定。(default:none)
  slow-restartのための仕掛け