Plack::Server::Standalone 系を使ってウェブアプリケーション開発と運用が楽になる話B!

こんばんわ。Advent Calendar 2回目の登場になります、kazuho です。前回は hacker トラックなのに標準添付モジュールの紹介でしたが、今回は Plack 関連の話です。

既存の環境に対する不満

Perl のウェブアプリケーションを構築するにあたっては、リバースプロキシと mod_perl を組み合わせるか、あるいは FastCGI (ExternalServer) を利用するのが一般的だと思います。しかし、どちらをとっても、環境を構築して設定するのが難しいというのが個人的な不満でした (mod_redirect を設定したり mod_fastcgi にパッチをあててインストールしたり startup.pl を書いたり...)。自分が Plack の開発 (主に Server::StandaloneServer::Standalone::Prefork) に関わるようになったのも、そのイライラを解消したかったからです。

その目的は、ほぼ達成することができ、先週、会社で運用しているサービス「パストラック」のバックエンドを Apache + FastCGI から Apache (mod_proxy) + Plack::Standalone::Server::Prefork::Server::Starter へ切り替えました。

そこで今日は、Plack::Server::Standalone 系のモジュールを利用したウェブアプリケーションの開発と運用の実際について、その楽さ加減を宣伝したいと思います。

.psgi と plackup

Standalone 系のサーバの特徴は、Plack の Middleware と組み合わせて、単独で動作するウェブサーバを構築できるところです。パストラックの .psgi ファイルは、概ね以下のようになっています。/static 以下のファイルを静的コンテンツとして配信し、動的コンテンツは昔ながらの CGI::Application::Dispatch を用いて生成しています。

builder {
    enable 'AccessLog', format => 'combined';
    enable 'ConditionalGET';
    enable_if { $_[0]->{PATH_INFO} =~ q{^/static/\d+/} } 'Header', set=> [ 'Expires' => 'Tue, 31 Dec 2019 23:59:59 GMT' ];
    enable 'Plack::Middleware::Static', path => qw{^/static/}, root => $ROOT;
    enable 'Plack::Middleware::Static', path => qw{^/(?:favicon\.(ico|png)|robots\.txt)$}, root => "$ROOT/static";
    CGI::Application::Emulate::PSGI->handler(
        sub {
            MyApp->dispatch();
        },
    );
};

開発環境では、この .psgi ファイルを以下のようにして Plack::Server::Standalone で動かします。httpd を起動する必要も設定ファイルを書く必要もありません。plackup 自身が、上で述べたように静的コンテンツも含む完全なウェブアプリケーションとして動作します。また、-r オプションによって、コードを書き換えると自動的に再起動するようになるので、修正→テストのサイクルを素早く回すことができます。

% plackup -r index.psgi

運用環境への投入

運用環境では、上の .psgi ファイルを Plack::Server::Standalone::Prefork::Server::Starter (以下 PSSPSS) を利用して動かしています。サービスの起動スクリプト (daemontoolsで管理) は、以下のとおり。.psgi は開発用のものと全く同一。他に設定ファイルはありません。

#! /bin/sh

exec 2>&1
cd /var/webapp || exit 1
exec /usr/bin/start_server --port=80 -- /usr/local/bin/setuidgid www \
  /usr/bin/plackup -E production -s Standalone::Prefork::Server::Starter --max-workers=40 --max-keepalive-reqs=1 index.psgi

PSSPSS は、Plack::Server::Standalone ベースのマルチプロセス httpd である Plack::Server::Standalone::Prefork に、完全無停止での更新機能を追加した httpd です。ウェブアプリケーションのプログラムを書き換えた後に SIGHUP を送ることで、サービスを一切停止しないホットデプロイが可能です。この仕組みについては、詳しくは「Kazuho@Cybozu Labs: Writing Hot-deployable servers (introduction of Server::Starter)」をご覧ください。

自分は daemontools で管理しているので、以下のようにして SIGHUP を送っています。

# svc -h /servire/webapp

Plack::Server::Standalone::Prefork (とその子クラスであるPSSPSS) では、デフォルトで keep-alive がオンになるのですが、ここではリバースプロキシと組み合わせるために、keep-alive をオフにしています(--max-keepalive-reqs=1)。

ちなみにパストラックでは、この設定で 20〜40 リクエスト/秒ほどの動的コンテンツを XenServer 上で生成しています。

リバースプロキシとの組み合わせ

PSSPSS (あるいは Plack::Server::Standalone::Prefork) は単独でのウェブサービス提供も十分可能なポテンシャルを持っていますが、パストラックでは Apache (mpm_worker) ベースのリバースプロキシと組み合わせて運用しています。mpm_worker で同時接続数の上限を稼ぐ。それによって keep-alive オンでの運用が可能となり、ユーザーのレスポンスが向上するから、というのが理由です。

この場合の設定も簡単。plackup が単独で全コンテンツをサーブできるので、必要なのはリバースプロキシとキャッシュだけです。だいたい、以下のような設定で運用しています。

<VirtualHost _default_:80>
  ServerName webapp.example.com
  ErrorLog /var/log/httpd/webapp.example.com/error_log
  CustomLog /var/log/httpd/webapp.example.com/access_log combined
  CacheEnable disk /static
  <Location />
    Order allow,deny
    Allow from all
    ProxyPass http://webapp.local/
    ProxyPassReverse http://webapp.local/
    ProxyPreserveHost On
  </Location>
</VirtualHost>

まとめ

このように、Plack::Server::Standalone 系のモジュールを使うことで、開発から運用までのコストを低く抑えることが可能です。具体的には、

  • 開発環境の構築が容易
  • 開発環境と運用環境の差異が少ない (ので問題が発生しにくい)
  • 設定項目が少ない
  • 運用環境でのホットデプロイが簡単
  • 全て HTTP ベースなので、問題の切り分けが容易

といったあたりになるでしょうか。大規模な案件で使うメリットがどれだけあるかは不明ですが、小〜中規模な開発では便利だと思います。一度お試しあれ。

PS. 明日は、ななしさんです。お楽しみに!