2011年3月アーカイブ

バージョン0.13以下の Plack::Middleware::Session の Plack::Session::Store::DBI は fork を伴うサーバで利用するとデータベースとの接続でエラーになるなどの問題がありました。バージョン 0.14 でエラーを回避できるオプションが加わっています。

http://search.cpan.org/~miyagawa/Plack-Middleware-Session-0.14/ SYNOPSISにある

builder {
    enable 'Session',
        store => Plack::Session::Store::DBI->new(
            dbh => DBI->connect( @connect_args )
        );
    $app;
};

このコードでは、builder実行時に DBI->connect も実行されデータベースに接続します。Plack::Loader::Delayedを用いずに、Starman(—preload-app指定)やStarletといったサーバを使用した場合、このデータベース接続後に fork により子プロセスが作成されるので、socket を子プロセス間でシェアしてしまい、エラーとなる危険性がありました。

/* Starmanはデフォルト Plack::Loader::Delayed を使用する件を miyagawa さんより指摘されたので追記 */

そこで、必要になったとき(子プロセスが初回のアクセスを処理する際)に、データベースとの接続を行うようcallbackを指定するオプションが 0.14 で追加されています。

builder {
    enable 'Session',
        store => Plack::Session::Store::DBI->new(
            get_dbh => sub { DBI->connect( @connect_args ) }
        );
    $app;
};

こっちを使うことで、問題が解決できると思われます。

DBとの永続接続を実現したい場合は、connect_cachedを使うのがよさげです

builder {
    enable 'Session',
        store => Plack::Session::Store::DBI->new(
            get_dbh => sub { DBI->connect_cached( @connect_args ) }
        );
    $app;
};

また、Scope::Container::DBI を使うと1リクエスト中での永続接続ができます。

builder {
    enable 'Scope::Container';
    enable 'Session',
        store => Plack::Session::Store::DBI->new(
            get_dbh => sub { Scope::Container::DBI->connect( @connect_args ) }
        );
    $app;
};

お試しくださいませ

技術評論社様から「Nagios 統合監視 [実践] リファレンス」という本を献本頂いたので紹介。

自分とNagiosの関わりは古く、このblogでも2004年にNagiosという文字が出てきている。Nagiosは設定が煩雑であることで有名で当時からテンプレートを参考にしつつ我流で利用をしていました。前職でも監視にNagiosを使っていて、プラグインを書いたり、大量のサーバを監視するために設定の自動生成ツールなんかも作りました。残念ながら現職ではNagiosは利用していませんが、、



この「Nagios 統合監視 [実践] リファレンス」はその名の通りリファレンス中心の書籍です。著者はNagiosドキュメントの翻訳プロジェクトをやっておられる株式会社エクストランスの方々。内容は1章ではNagiosの概要を説明し、2章でプラグイン、3章から5章で設定ファイル、ホストやサービスの設定項目が1つ1つ解説されています。今まではこのような説明はNagios付属のドキュメントあるいは公式サイトの英語のドキュメントでしか得られませんでしたが、日本語でまとまって情報が入手できる価値はあるのではないでしょうか。テンプレートそのままに設定してきた項目などを見直してみるとNagiosの便利な機能が発掘できるかもしれません。個人的には実践的な監視の話がもっと知りたいので、リファレンスの合間合間にあるコラムが興味深かったです

前職でのNagiosの話は、WEB+DB PRESS Vol.55に書きました。そのときの紹介記事



また、「[24時間365日]サーバ/インフラを支える技術」にも設定方法などが紹介されていましたね。こちらもおすすめです。



2010 Perl Advent Calendar などでも説明していた Scope::Container でDBの接続管理を行うモジュールを書いた。

CPAN: http://search.cpan.org/dist/Scope-Container-DBI/ github: https://github.com/kazeburo/Scope-Container-DBI

機能的には、Scope::Container に接続情報をキャッシュして、同じDSN・ユーザ名で接続の場合、キャッシュからdbhを返します。Scope::Containerなので任意のスコープで接続の維持と切断ができます。

Scope::Container::DBIには、connectメソッドがあるだけ。DBIと同じくdsn、ユーザ名、パスワード、オプションを渡す。

use Scope::Container::DBI;
use Scope::Container;

sub work {
    my $contaier = start_scope_container();      
    for (1..n) {
        # 最初の1回だけ接続。あとはキャッシュされる  
        my $dbh = Scope::Container::DBI->connect(
            'dbi:mysql:mydb;host=myhost', 'myuser', 'mypasswd',
            { RaiseError => 1, mysql_connect_timeout => 4, mysql_enable_utf8 => 1 }
        );
    }
    #$contaierが破棄されるまで接続はキャッシュ
}

Plack::Middleware::ScopeContaierと組み合わせると、1リクエスト中だけ永続接続が簡単に実現できます。

あと、複数のdsnをarrayrefで渡すと、その中からランダムで選び出して接続をすることもできます。slaveを並べる場合などに使えます。

use Scope::Container::DBI;
use Scope::Container;

my $dbh = Scope::Container::DBI->connect(
    ['dbi:mysql:mydb;host=myslave01', 'myuser', 'mypasswd', {..}],
    ['dbi:mysql:mydb;host=myslave02', 'myuser', 'mypasswd', {..}],
    ['dbi:mysql:mydb;host=myslave03', 'myuser', 'mypasswd', {..}],
);

これも接続をキャッシュして1回だけ接続が行われます。

あと、Scope::Container::DBIでキャッシュのdbhを再利用する場合にはDBIx::Connectorのように、プロセスID/スレッドIDを確認しているので、fork safeです。

MySQLの文字コードの設定など接続が完了した時点でなんらかのアクションをしたいことがあると思います。Scope::Container::DBIにはその機能はありませんが、DBI標準のCallbackが使えます。

my $dbh = Scope::Container::DBI->connect($dsn, $username, $password, {
    RaiseError => 1,
    Callbacks  => {
        connected => sub {
            shift->do(q{SET NAMES utf8});
        },
    },
});

ちょっと長くなるけど使えるはず

おためしくださいませませ

少し時間が経ってしまったけど。

地震のあと、息子の保育園お迎えに少しでも早く行ってあげたいと思っていたのですが、オフィスからみえる新宿駅西口、東口両方とも人で溢れかえっていて、もし電車が復旧したとしてもとても乗れないだろうなということで歩いて帰ることにした。もし途中で東横線が走り出せばいいなということも考えていたりもしたけど、それは甘すぎました

実際に歩いた場所をGoogle mapで表示してみると、30kmぐらいあるそうです。フルマラソンより短いです。かかった時間はGoogleの予測の時間通り、6時間強ぐらいでした。


大きな地図で見る

twitterの投稿を見返すと、

  • 歩き始めたときのtwitterへの投稿が16:12
  • 池尻のあたりで 17:23。ここまで田園都市線沿いの同僚と一緒にあるいていました
  • 祐天寺のあたり 17:52
  • 学芸大のあたり 18:01
  • 環七通り 18:20
  • 大岡山 18:26
  • 雪が谷大塚 18:45
  • 多摩川を渡る 18:57
  • 武蔵小杉 19:12
  • 日吉 19:42。武蔵小杉からずっと停電で真っ暗でした。東横が動く夢はこの辺りで捨てた
  • 綱島 20:07。電気が付き始めるとともにパチンコ屋が営業していることに腹が立つ
  • 大倉山あたりでiPhoneバッテリ切れる。菊名からの道がわからなくて人に道を聞きつつ歩いた。
  • 保育園に息子を迎えに行って家到着して、ベッドに寝かしつけたのが 23:06

今思うと、若干遠回りしているなぁとか、中目黒のドンキで自転車買えばよかったとか思うんだけど、もし次に大きな災害があったときの練習にもなったと思うことにする。ほんとtwitterがあるおかげで孤独ではなかったし、iPhoneの地図があったから自信をもって歩けました。途中で電池切れにならないようiPhoneを充電できるバッテリを会社に置いておくようにしようと思う。

保育園に連絡をしてくれていた奥さんは次の日の朝に動き出した電車で帰ってきて家族3人とも無事に過ごしています。あとは被災地が一刻も早く復興することを願うばかりです。

JavaScriptが使いにくい携帯などの環境でGoogle Anlyticsを利用する場合は、通常このサイトで配布される1x1の画像を吐き出すサーバサイドのビーコンスクリプトを設置し、 Webサイドからリンクを張りますが、このサーバサイドのスクリプトのうちPerlのコードはCGI以外では実行しづらいモノなので、PSGIにアプリケーションにしてCPANにリリースしました

加えて、タイムアウトの制御が細かくできるようLWPをFurlにし、勝手にビーコンが他人に利用されないようchecksumを追加しています。また、PSGIアプリケーションで利用できるURL生成用のクライアントライブラリもつくりました。

使い方。まずサーバサイド。

use WWW::GoogleAnalytics::Mobile::PSGI;
use Plack::Builder;

builder {
    mount "/ga" => WWW::GoogleAnalytics::Mobile::PSGI->new(
        secret => 'my very secret key',
        timeout => 4,
    );
    ...

};

サーバサイドは、単独のアプリケーションでも他のアプリケーションと同居でもどちらでもかまいません。Google Analyticsが障害を起こした際の影響を考えると分けたいとか思うかもしれません。実際の稼働率はわかりません。

secretはチェックサムのための秘密の文字列。クライアントライブラリと共通にします。timeoutはGoogleへの問い合わせの制限時間です。

クライアントサイドは、

use WWW::GoogleAnalytics::Mobile;

my $gam = WWW::GoogleAnalytics::Mobile->new(
    secret => 'my very secret key',
    base_url => '/ga',
    account => '...',
);

my $image_url = $gam->image_url($env);

image_urlメソッドに渡すenvはPSGIのenvです。これで画像のurlが得られるのでテンプレート中に埋め込んで利用します。

いくつかTips。

WWW::GoogleAnalytics::Mobile::PSGIでGoogleにアクセス際にProxyを通すには、環境変数を利用します。

my $app = WWW::GoogleAnalytics::Mobile::PSGI->new()->to_app;
$app = sub {
    local $ENV{http_proxy} = 'http://192.168.32.1;
    $app->(shift);
};

な感じ。

また、Net::DNS::Liteを使っているので、googleにアクセスする際の名前解決の結果をCache::LRUなどを使ってキャッシュできます。

use WWW::GoogleAnalytics::Mobile::PSGI;
use Cache::LRU;

# setup cache for Net::DNS::Lite
$Net::DNS::Lite::CACHE = Cache::LRU->new(
    size => 256,
);

おためしくださいませませ。

20年弱ぶりぐらいにレゴ買ったよ!

まずは、箱

DSC02004.jpg

裏側には説明!

DSC02005.jpg

箱を開けるとブロックがいっぱい!

DSC02006.jpg

作る順に袋をわけてくれたらいいのになと思う。

DSC02007.jpg

作り方の説明書にもしんかい6500の詳しい説明がある。おんなじ図がこのページにもあった。

DSC02008.jpg

さっそく組み立てる。まずは台座となる板。周りを黒で囲むとかニクい演出

DSC02009.jpg

海底の岩山。あやしい生物がおります。この小さい1x1の部品を重ねるのがちょっと大変。不器用になったなぁ

DSC02010.jpg

海底の生物2。ちょっとかわいい

DSC02016.jpg

本体の作成開始。まずは後部から。形がいいですねー

DSC02018.jpg

次は中央部分。大きく分けて4つのパーツを作って合体していく感じ。この辺で子供が泣いたので対応してた

DSC02020.jpg

次は先頭部分。オレンジ色の固まりの中にはソナーとかが入っているらしい

DSC02022.jpg

このディテールがたまらん

DSC02024.jpg

最後にマニピュレータ。作って合体。

DSC02025.jpg

完成!

DSC02030.jpg

別の角度から

息子の昼寝中に1時間ぐらいで組み立てて、玄関に飾りました。まだ小さいレゴを触らせるのは怖い。あと1年ぐらいしたら息子も一緒に遊べるだろうから、せっかく実家に衣装ケースいっぱいのレゴがあるんだし、もっといろいろ組み立てたいなぁー。

過去に何回か、Webアプリケーションエンジニア向けのインフラ勉強会があったらいいなぁとtwitterにつぶやいたことがありますが、じゃぁ実際どんな内容が良いのか、あまりまとまっていませんでしたので、整理してみました。

まぁ「Webアプリケーションエンジニアに知っていて欲しいインフラの知識」と言いながらWebアプリケーションの運用の仕事をしている自分でも専門にやっている方からみて完璧に答えられる自信はありません。ただ今の世の中ググれば答えは見つかるので「概要は知っている」そして「詳細を調べる方法を知っている」ぐらいで問題ないと思っています。

  1. ネットワークにおけるレイヤ2,3,4,7の概要

  2. TCP/IPの通信開始、通信終了時の状態遷移の把握

  3. IPアドレス、セグメント、スタティックルーティング、NAT

  4. CPUのトレンド

  5. HDDの構造

  6. RAIDレベル、RAIDカードのBBUの役割

  7. SSDの特徴

  8. ハードウェアの価格・消費電力

  9. Linuxのページキャッシュの動作

  10. CPU使用率のuser,system,id,waitの各項目について


すごく曖昧な内容ですが、Webアプリケーションの構築、またチューニングをしていく上で調査のきっかけとなる項目になっているかなぁと思います。そして自分の仕事がなくなr

上記のことを知りたいときのおすすめの本は


その昔、第2(3?)版で勉強しました


もはや説明不要かと。LinuxとMySQLの本ですがハードウェアからチューニングまで網羅的に解説されています。

ご意見お待ちしています。

3日間で去年一年間分の花粉が悲惨したようです。元気です。

HTTPコンテンツ圧縮はどのレイヤーで行うのがいいか」で書いたあたりと問題は共通しているのですが、大規模サイトの運用で最近割とボトルネックとなりやすいのはラック-集約スチッチ間のトラフィックです。1台あたりの性能が飛躍的に向上し、画像転送では100Mbps〜300Mbps、それ以上ぐらいは楽に吐き出すようになっているので、ラックスイッチの1Gbpsのuplinkではすぐに詰まってしまいます。この対策として、高トラフィックのサーバを分散配置したり、link aggregationにて2Gbps-4Gbpsに増速したり、あるいは10Gの導入を検討すると思いますが、それには手間もお金もかかるので、まずはトラフィックを減らせないか考えるわけです。

そこで最近、2カ所ほどでとった方法が、フロントのReverse Proxyで短時間、キャッシュする方法です。

edge-cache.jpg

上の図は、画像を配信するようなシステムの構成図です。フロントにNginxがあり、consistent-hashingでリクエストを分散してSquidにアクセスしています。キャッシュの空間効率から言えば、Squid にだけキャッシュを保存するのが良いのですが、そのために、Nginx-Squid間の通信が必ず発生してしまいます、そこで空間効率を多少あきらめ、Nginx側でもキャッシュを有効にします。NginxはロードバランサーやDNS-RRなどで分散されるので同じ画像のキャッシュを複数台でもってしまうことになりますが、その分お互いにやりとりを行う通信をなくせるのでトラフィックは減らすことができます。

これでトラフィックの問題は解消されますが、キャッシュの場所を増やすことで別の問題が現れます。それはキャッシュデータの削除の問題です。CGMデータの場合理由は様々ですが、オリジナルデータの変更/削除が頻繁に行われます。そしてオリジナルデータが変更された場合、速やかにキャッシュも更新される必要があります。Consistent-Hashingの場合、キャッシュデータの削除時にもConsistent-Hashingを使えます。キャッシュの削除の方法にはこんな方法があります

しかし、Reverse Proxyのキャッシュはすべてのサーバにデータが存在する可能性があるので多少面倒です。もっとも簡単で確実なのは、Reverse Proxyのリストを管理し、全台に対して削除のためのリクエストを発行することですが、そこまで厳密性を求められない場合、キャッシュの有効期限を短く設定し、Expiresされるのを待つという手段も考えられます。これを勝手にShort-term Edge Cacheと呼んでみました。

NignxでShort-term Edge cacheを行うには

proxy_buffering on;
proxy_cache_path /dev/shm/nginx/cache levels=1:2 keys_zone=cache-space:20m max_size=300m inactive=10m;

proxy_cache_key "$scheme$host$request_uri";
proxy_cache cache-space;
proxy_cache_valid  200 302 5m;

こんな感じ。正常なレスポンスを5分キャッシュしています。

Apacheでは、moddiskcacheとcronを使う手があります。httpd.confにてdisk_cacheを有効にし、

CacheEnable disk /
CacheMaxExpire 300
CacheRoot /dev/shm/disk_cache
CacheDirLength 4

cronでcacheのcleanupを行います。

/usr/bin/find /dev/shm/disk_cache -type f -mmin +6 | /usr/bin/xargs rm -f >/dev/null 2>&1

これを毎分〜5分間隔で動くようセットします。

上の設定は、キャッシュ時間を5分にしていますが、コンテンツの性格やキャッシュ削除に求められる厳密性によって数分から数十分までの間で設定ができる思われます。また、同じコンテンツに対するアクセスの集中度合いによってShort-term Edge Cacheの有効性も変わってきます。実際に導入する際にはコストの削減メリットとサポートのリスクとを実際に業務に関わる人と調整することが必要となるかもしれません。