Posts categorized "Perlbal" Feed

Feb 06, 2013

Perlbal の Reproxy URL Cache の統計を Ganglia に送りつけるやっつけスクリプト。

mgmt のポートにつないで、show service <service name> で Reproxy URL Cache のキャッシュ数、ヒット数、ヒット率なんかが取れる。

% echo "show service web" | nc localhost 5700
show service tp_web
SERVICE tp_web
     listening: --
          role: reverse_proxy
  pend clients: 0
  pend backend: 0
    cache size: 0/1024 (0.00%)
    cache hits: 0
cache hit rate: 0.00%
 connect-ahead: 0/0
          pool: web_pool
         nodes:
                127.0.0.1:6500              0
.

(空っぽの状態だけど)これを parse して gmetric コマンドで定期的に ganglia に送りつけるだけ。

#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long qw( :config posix_default no_ignore_case bundling auto_help );
use Pod::Usage;
use IO::Socket::INET;

GetOptions(\my %opt, qw( host=s service=s gmetric verbose ))
    or pod2usage(1);

my $sock = IO::Socket::INET->new(PeerAddr => $opt{host}, Timeout => 2, Proto => 'tcp')
    or die $!;
print $sock "show service $opt{service}\r\n";
my ($cached, $max, $hit, $rate);
while (my $line = <$sock>) {
    last if $line =~ m{^\.};
    chomp $line;
    if ($line =~ m{\s*cache size: (\d+)/(\d+) \((\d+(?:\.\d+)?)\%\)}) {
        $cached = $1;
        $max    = $2;
    }
    elsif ($line =~ m{\s*cache hits: (\d+)}) {
        $hit = $1;
    }
    elsif ($line =~ m{\s*cache hit rate: (\d+(?:\.\d+)?)\%}) {
        $rate = $1;
    }
}
die unless (defined $cached && defined $max && defined $hit && defined $rate);

if ($opt{verbose}) {
    print "$cached cached, $hit hits, $rate% hit\n";
}
if ($opt{gmetric}) {
    system 'gmetric', '--name', 'perlbal_reproxy_cache_urls', '--value', $cached, '--type', 'uint32', '--group', 'perlbal';
    system 'gmetric', '--name', 'perlbal_reproxy_cache_hits', '--value', $hit, '--type', 'uint32', '--group', 'perlbal';
    system 'gmetric', '--name', 'perlbal_reproxy_cache_hit_rate', '--value', $rate, '--type', 'float', '--group', 'perlbal';
}

__END__

=pod

=head1 NAME

reproxy_cache_stats.pl - get reproxy cache stats

=head1 SYNOPSIS

  % reproxy_cache_stats.pl --host=<host:port> --service=<name> [--gmetric] [--verbose]

=cut

というメモ。

Jan 17, 2013

Perlbal で Server::Starter(start_server command) によるホットデプロイをサポートするためのプラグイン、Perlbal::Plugin::ServerStarter というのを作った(作っている)。

https://github.com/ziguzagu/Perlbal-Plugin-ServerStarter

Perlbal の素敵な点の一つに、動的に設定変更できる(管理ポートに telnet, nc などでつないでコマンド送り込む)というのがあるけど、たぶんほとんどのプラグインでは設定の削除ができない。たとえば Vhosts プラグイン。

VHOST app.example.com   = app
VHOST admin.example.com = admin     ## これがいらなくなっても動的に削除するコマンドはない
VHOST www.example.com   = web

また、複雑な設定や開発環境などではテスト済みの大きめの設定変更なんかをちまちま動的に変更するのは、作業自体のテストもしづらいし、そもそもが面倒くさい。なので、ときには再起動で設定読み直しがどうしても必要になったりならなかったりする。ただ(一応)ロードバランサーという位置づけのソフトウェアでもあり、サーバーのエッジあたりで運用されることもなくはないので、できれば無停止でいきたい。

ということで Server::Starter 対応。以下使い方。

プラグインを読み込むと LISTEN というコマンドが使えるようになるので、SET listen = [ip:]port の代わりに使う。

LOAD ServerStarter

CREATE SERVICE web
  SET role    = web_server
  SET docroot = /path/to/htdocs
  LISTEN = 5000
ENABLE web

と設定ファイルに書き、start_server 経由で perlbal 起動。

$ start_server --port 5000 -- perlbal -c /etc/perlbal/perlbal.conf

ただ、ポート番号をコマンドラインと設定ファイルで重複して管理することになるのがアレなので、設定ファイルからポート番号抜き出して start_server に食わせてくれる start_perlbal というコマンドも用意した。

$ start_perlbal -c /path/to/perlbal.conf

これでよしなにしてくれる。知らないオプションは start_server にパススルーするので、

$ start_perlbal -c /path/to/perlbal.conf --pid-file perlbal.pid

というのも機能するように。

1つ注意点があるとすればログについてはちょっと見直しが必要。Perlbal(1.80現在) のログ出力は、

  • foreground で実行したら STDOUT
  • --daemon オプションで daemonize したら syslog

という実装になっていて、foreground で実行しつつもログは syslog に、というのができない。Server::Starter は仕様上 foreground で実行する必要がある(daemonize すると pid が変わってしまうので(fork して exit するから)、start_server で起動失敗扱いになる。はず。試した限り。)ので、perlbal --daemon でいままで運用していた場合は、daemontools (multilog) 使ったりするか、

$ sudo start_perlbal 2>&1 | /usr/bin/logger -p local7.warn -t perlbal &

とかして syslog やらファイルにログ出すようにする必要がある。

あと、start_server を経由せずに perlbal で起動しようとすると Server::Starter#server_ports よんだところで死んでそのまま起動できなんだけど、警告だけだして SET listen = [ip:]port の指定に読み替えてとにかく起動できたほうがいいかどうかは迷いどころ。そのフォールバックのせいで気づかずに start_server なしで起動してた、なんてことになるのはそれはそれで事故のもとかなぁとも思うけどはたして...。

実装自体は Perlbal 本体に取り込んだほうが美しいので安定したら考えたいところ。

とはいえ、これで Perlbal 無停止運用が可能になったよ。たぶん。 happy01

Jul 20, 2011

オレオレじゃない本物の SSL 証明書を Perlbal で使うときの中間証明書の設定方法メモ。

SSL を使いたい reverse_proxy などで、

SET enable_ssl      = on
SET ssl_key_file    = /etc/perlbal/cert/cbapp-key.pem
SET ssl_cert_file   = /etc/perlbal/cert/cbapp-crt.pem
SET ssl_ca_path     = /etc/perlbal/ca
SET ssl_verify_mode = 1
SET ssl_cipher_list = ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+EXP:+eNULL

という感じで設定する。

オレオレ証明書使うときと違うのは、ssl_ca_pathssl_verify_mode を設定する、というところ。ssl_ca_path には中間証明書をおくディレクトリを指定しておく。ssl_verify_mode の設定値は IO::Socket::SSL 嫁と書かれているけれど、よくわからんので 1 (verify peer) を設定した。

で、ssl_cal_path のディレクトリには <中間証明書の hash value>.0 というファイル名で中間証明書を設置する(同じ hash value の場合は .1, .2 のようにするらしい)。hash value というのは以下のコマンドで取得できる。

% openssl x509 -noout -hash -in <中間証明書ファイル>

この <hash value>.0 ファイルそのままおいておいても良かったけど、なんとなく不気味なので、

% ln -s ca-201107.pem $(openssl x509 -noout -hash -in ca-201107.pem).0

として symlink にしておいた。

中間証明書ディレクトリを ssl_ca_path で指定する、というのは IO::Socket::SSL 由来で、IO::Socket::SSL を直接使うときにも同じようにする。実際に中間証明書の処理をやるのは Net::SSLeay の CTX_load_verify_locations という関数でこいつは openssl の同名の API をよびだしているようなので、この、指定されたディレクトリの中にある hash value のファイル名をどうにかする、というのは openssl の仕様であった。

というのを学んだメモ。

参考:

Jun 25, 2011

Perlbal の web server に Apache の mod_expires と同じ代替期間指定構文ってやつで Expires を指定できるプラグイン、Perlball::Plugin::Expires というのを書いた。

使い方はこんなん。

LOAD Expires
CREATE SERVICE web
    SET role    = web_server
    SET listen  = 127.0.0.1:8000
    SET docroot = /path/to/docs
    SET plugins = Expires
    Expires default   = access plus 1 day 12 hours
    Expires image/gif = access plus 10 years
ENABLE web

Content-Type 別に指定できるのも同じ。ExpiresDefault 的なのは default を使う。mod_expires には A<時間>, M<時間> という形式もあるけどそっちはとりあえず対応してない(使ったことないから...)。

Perlbal には汎用的にレスポンスヘッダを追加できる Perlbal::Plugin::Addheader というのがあるのだけれど、Expires を指定するのに、

ADDHEADER static Expires [% {use HTTP::Date;HTTP::Date::time2str(time() + 2592000)} %]

こんなふうに書かないといけなくてあんまりだなぁと思ったので書いてみた次第。

どうぞご利用ください。