2010年9月アーカイブ

株式会社gumiさん主催の技術勉強会 gumiStudyの第二回で、memcachedの運用まわりについて発表をさせて頂きました。起動オプションと監視、あとおまけでサーバ構成についての内容でしたが、実際に高負荷なWebサービスでmemcachedを運用する際に注意するところを紹介できたと思います。

例の件以来、memcachedについて書いたり話したりする機会が多く頂いています。次はShibuya.pm で再び監視について発表する予定です。また、今回の発表でも紹介したリソースモニタリングツール cloudforecast はYAPC::Asiaで詳しく説明します。Shibuya.pmは参加のキャンセル待ちがかなり多い状態ですが、YAPC::Asiaはまだまだチケット絶賛発売中です。ぜひいらしてくださいませー。

第三回の紹介をしない間に、連載「memcachedの活用と運用 実践編」の最終回が公開されました。最終回は、memcachedを多く運用してきた経験から得た小さめのTIPSをいくつか紹介する回となっています。

言語依存の話になるので詳しくは書きませんでしたが、記事中のincrementのサンプルで

my $result = $memd->incr($key, @_);
if ( defined $result && ! $result ) {
    ...
}

このように冗長にみえる条件文になっていますが、このdefinedと!$resultの意味はCache::Memcached::Fastのドキュメントを読むと解ります。

Return: unsigned integer, new value for the $key, or false for negative server reply, or undef in case of some error.

undefの場合はエラー、定義済みだけど空文字の場合はキーが見つからなかった場合。

if ( ! $result ) { ..

とだけしてしまうと、何らかの理由で一時的にmemcachedに接続ができなかった場合に、初期値を入れてしまう危険性があります。(ここではaddを利用しているので危険性はないとおもいますが)高負荷時にmemcachedにつながらないことがどれくらいあるのか、把握できてませんが、そういうこともあるよということで。

memcached/docs/protocol.txtとかドキュメントはちゃんと読むのがおぬぬめです

kazuhoさんが「プロのサーバ管理者の間では存在価値が疑問視されて久しい (Min|Max)SpareServers だと思う」と書いたり、hirose31さんが去年のYAPC::Asiaで{Start,{Min,Max}Spare}Servers,MaxClientsは同じにしているよと発表したり、実際前職のサーバはそのように設定されていたのですが、自分でうまく説明ができてなかったので、調べながら書いてみた。
本当はイントラブログ用に書いていたものですが、がんばったので転載。

前提として、CPUの使用率におけるsystemとfork Re: クラウドがネットワークゲーム開発者にもたらしてくれたもの - blog.nomadscafe.jpでも書いている通りforkってのはサーバにとって重い部類の処理になります。つまり負荷の高いときにforkを大量に行うのはしてはならないことの1つです。

StartServers 60
MinSpareServers 50
MaxSpareServers 100
MaxClients 300

上記のように設定があると、サーバ起動時にまず、StartServersの60個の子プロセスが作られます。子プロセスがリクエストを処理し、MaxRequestsPerChildに達するとその子プロセスは終了します。
子プロセスが終了した際に、親プロセスは次の条件により新規に子プロセスをforkするか決定します。

  • IDLEしているプロセスの数がMinSpareServersよりも少なくて、かつ全てのプロセス数がMaxClientsより少なければ新しいプロセスを作成
  • IDLEプロセスがMaxSpareServersよりも多ければプロセスを1つKILLする

子プロセスの数は、サーバが暇な時はStartServers(60)からMinSpareServers(50)まで徐々に減っていく(プロセスが死んでも何もしないので)。忙しくなりBusyのプロセスが増えるとIDLEのプロセスがMinSpareServers(50)より少なくなるのでforkする。さらにIDLEがMinSpareServers(50)より少なければBusyとIdleの合計がMaxClients(300)を上限に子プロセスをforkしていく。やがてピークタイムを過ぎ、Busyのプロセスが減りIDLEのプロセスが増える、その数がMaxSpareServers(100)よりも多ければプロセスをKILLする。MaxSpareServers(300)以下になると初期状態と同じく徐々にプロセスが減っていきます。

spareservers-3.png

StartServers、(Min|Max)SpareServers, MaxClientsが同じ値の場合、プロセス数が固定されるので忙しいからといってMinSpareServersを確保するためだけに必要以上にforkを行うことはないし、余ったプロセスがKILLされることもない。IDLEのプロセスがCPUリソースを消費することはほぼないので余計なCPUは使いません。

StartServers 60
MinSpareServers 60
MaxSpareServers 60
MaxClients 60

このような設定がおすすめです

CPUは使わなくてもIDLEのプロセスが多くなると、メモリがもったいないとか思うかもしれないけれど、CoW(Copy On Write)があるのでそこまでメモリは喰わないはずです。それでもメモリを多く使うならプロセスが不足しない範囲でStartServers, (Min|Max)SpareServers, MaxClientsを全て下げればいいと思います。そもそもそんな状態ではピークタイムにSWAPする可能性があります。経験上、MaxClientsは一日の最大のBUSYプロセス数の倍あればいいと思います。CloudForecastやCactiなどを使って利用しているプロセス数をモニタリングするのはもはや必須課題です。

あと、memcachedとアプリケーションサーバが同居している場合、アプリケーションサーバの負荷が高くなって、MaxClientsまでジリジリと上がりメモリ使用量が増えるとmemcachedが確保するメモリもswapしてしまう危険もあります。メモリ使用の上限値を確認しておく上でもStartServers, (Min|Max)SpareServers, MaxClientsを同じにしておいた方がいいですね