Perl Hackers Hub

第24回 PSGI/Plack実践入門―Starman,Starlet,Twiggy,Plack::Middleware,Server::Starter(3)

この記事を読むのに必要な時間:およそ 4 分

(1)こちら⁠2)こちらから。

PSGIミドルウェア

PSGIミドルウェアとは

PSGIアプリケーションとPSGIサーバの間にあり,PSGIアプリケーションをラップすることでリクエストやレスポンスを書き換えるしくみのことをPSGIミドルウェアと呼びます。PlackではPlack::Middlewareという名前空間のもとに実装され,CPANにも数多くアップロードされています。

Plack::Builderモジュールを利用すると,DSLDomain Specific Languageドメイン特化言語)にてミドルウェアの読み込みと設定を行うことができます。

use Plack::Builder;
my $app = sub {…};
$app = builder {
  enable 'Plack::Middleware::Lint';
  enable 'Plack::Middleware::StackTrace';
  $app;
};

Plack::Builderが提供するbuilderブロックの中で,enable 'ミドルウェア名';として指定してミドルウェア追加します(⁠Plack::Middleware::」は省略できます)⁠builderはミドルウェアの読み込みとアプリケーションのラッピングを行い,新しくPSGIアプリケーションを生成します。

以降ではプロダクション環境で使用されるPlack::Middlewareをいくつか紹介します。

Static ─⁠─ 静的コンテンツの配信

Plack::Middleware::StaticはCSSやJavaScriptなどの静的コンテンツの配信を行うミドルウェアです。Plackのディストリビューションに含まれています。

builder {
  enable 'Static',
    path => qr!^/(css|js|img)/!,
    root => '/path/to/public';
  $app;
}

上記のような設定を行うと,/cssや/jsといったURIにアクセスした際に,rootで指定したディレクトリから対象のファイルを配信します。

ただし,プロダクション環境で静的コンテンツの配信をアプリケーションサーバから行ってしまうと,アプリケーションサーバが本来の処理に集中できなくなってしまい,レスポンス速度の低下につながります。ApacheやnginxといったWebサーバをリバースプロキシとして設置し,静的コンテンツはそこから配信するのが一般的です。

AccessLog ─⁠─ アクセスログの表示

AccessLogはその名のとおりアクセスログを出力するためのミドルウェアです。

builder {
  enable "AccessLog",
    format => "combined";
  $app;
};

ログのフォーマットにはcombinedcommonあるいはApacheのmod_log_configと同じフォーマット文字が使えます。

AccessLogミドルウェアでApacheでサポートされる,%D(レスポンスにかかった時間)を出力したい場合は,Plack::Middleware::AccessLog::Timedが必要となります。

enable "AccessLog::Timed",
  format => '%h %l %u %t "%r" %>s %b %D';

ログの出力先はデフォルトでは$env->{psgi.errors}が示すファイルハンドルになります。多くの場合は標準エラーです。ファイルに書き出すときはloggerオプションにコードリファレンスを渡します。

use File::RotateLogs;
my $logger = File::RotateLogs->new();

builder {
  enable "AccessLog",
    format => 'combined',
    logger => sub { $logger->print(@_) }
  $app;
}

File::RotateLogsはログファイルを指定した時間ごとに分割し,古いログファイルを自動で削除する機能を持っています。AccessLogミドルウェアと併せて使うことで,ログのローテーションやディスク溢れの心配なくアクセスログを記録できます。

ReverseProxy─⁠─ アクセス元IPアドレスの取得

PSGIアプリケーションをApacheやnginxなどのリバースプロキシのもとで動作させた場合,アクセス元のIPアドレスを示す$env->{REMOTE_ADDR}はリバースプロキシのIPアドレスとなります。同じホスト上にリバースプロキシがあれば127.0.0.1が格納されます。本来のクライアントのIPアドレスはどうなるかというと,X-Forwarded-Forというヘッダの末尾に追加されて送られてきます。X-Forwarded-ForヘッダはRFCなどで定義されているヘッダではありませんが,Apache,nginx,Squidなどでサポートされている標準的なヘッダです。

PSGIアプリケーションでは次のようなコードで,クライアントのIPアドレスをX-Forwarded-Forから取得できます。

my $xff = $env->{HTTP_X_FORWARDED_FOR};
my ($ip) = $xff =~ /([^,\s]+)$/;

Plack::Middleware::ReverseProxyはX-Forwarded-ForからIPアドレスを取得し,$env->{REMOTE_ADDR}の上書きのほか,psgi.url_schemeの調整を自動で行うミドルウェアになります。リスト4ではPlack::Builderの提供するenable_ifを使って,リモートアドレスを確認したうえでReverseProxyを有効にしています。

リスト4 reverse_proxy.psgi

use Plack::Builder;
builder {
  enable_if { $_[0]->{REMOTE\_ADDR} eq '127.0.0.1' }
    'ReverseProxy';
  $app;
};

X-Forwarded-Forヘッダはクライアント側で簡単に詐称できてしまうので,送られてきた値をそのまま信用するのは危険です。場合によってはIPアドレスでアクセス制限してあるコンテンツに対して不正アクセスが可能な状態になります。リモートIPアドレスがリバースプロキシのIPアドレスかどうか判断し,リバースプロキシからのアクセスの場合のみ,X-Forwarded-Forを信用するようにしてください。

ServerStatus::Lite─⁠─ サーバ状態の可視化

Starman,StarletといったPrefork型のPSGIサーバにおいて,ワーカの可視化に使われるのがPlack::Middleware::ServerStatus::Liteです。

ServerStatus::Liteにはいくつかオプションがあります。

builder {
  enable "Lite",
    path => '/server-status',
    allow => ['127.0.0.1','192.168.0.0/16'],
    counter_file => '/path/to/counter_file',
    scoreboard => '/path/to/scoreboard';
  $app;
};

pathにサーバの状態を表示するためのURIを指定し,allowにそのURIに対してアクセス許可されるIPアドレスを指定します。もしIPアドレスが指定されていない場合,一切アクセスができません。counter_fileはアクセス数と総転送量を記録するためのファイルです。そしてscoreboardにワーカプロセスの状態を記録するためのディレクトリを指定します。

HTTPクライアントでpathに指定したURIにアクセスすると,図4のようなページが表示されます。上からサーバが起動してからの秒数,処理したアクセス数と転送量(KB)⁠現在リクエストを処理しているビジー状態のワーカ数とアイドル中のワーカ数になります。以降はプロセスの状態でpidごとにリクエスト処理中かどうか,処理中であれば現在処理しているリクエストについての情報,アイドル状態であれば1つ前のリクエストに関する情報がまとめられています。

図4 server-status

$ curl http://localhost:5000/server-status
Uptime: 1381942535 (23 seconds)
Total Accesses: 3
Total Kbytes: 0
BusyWorkers: 1
IdleWorkers: 9
--
pid status remote_addr host method uri protocol ss
80060 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 14
80061 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 13
80062 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 13
80063 A 127.0.0.1 localhost:5000 GET /server-status HTTP/1.1 0
80064 .

図5は,ServerStatus::Liteを使って得られた情報をリソースモニタリングツールのCloudForecastでグラフ化したものです。このように可視化することで,適切なワーカ数に調整でき,効率的な運用が可能となります。

図5 server-statusグラフ

図5 server-statusグラフ

著者プロフィール

長野雅広(ながの まさひろ)

株式会社ミクシィ 開発部システム運用グループ アプリケーション運用チーム所属。mixiのアプリケーション運用に携わっています。Perlのカンファレンス,YAPC::Asia 2008でもmemcachedに関する発表を行いました。

URLhttp://blog.nomadscafe.jp/

コメント

コメントの記入