2011年7月アーカイブ

以前、ここに書いていた、memcachedのincrがスレッドセーフではなく、番号がズレることがある件が 1.4.6 で修正されたようなので検証してみた。

memcached-1.4.6のダウンロードは、memcached.org から
リリースノート http://code.google.com/p/memcached/wiki/ReleaseNotes146

検証に使うscriptは以下。

use Cache::Memcached::Fast;
use Parallel::ForkManager;
my $i=0;
while(1) {
$i++;
my $memcached = Cache::Memcached::Fast->new({
    servers            => [ { address => "localhost:11211", noreply => 0 }  ] ,
    compress_threshold => 4_000,
    ketama_points      => 250,
    max_failures       => 3,
    failure_timeout    => 2,
});
$memcached->set("hoge",1);
print $i;

my $pm =Parallel::ForkManager->new(30);
foreach my $id ( 1..1000 ) {
    $pm->start and next;

    my $childmemcached = Cache::Memcached::Fast->new({
        servers            => [ { address => "localhost:11211", noreply => 0 }  ] ,
        compress_threshold => 4_000,
        ketama_points      => 250,
        max_failures       => 3,
        failure_timeout    => 2,
    });
    for my $loop ( 1..100 ) {
        my $ret = $childmemcached->incr("hoge");
        warn "[$id] failed?" unless $ret;
    }
    $pm->finish;
}
$pm->wait_all_children;

sleep 1;
my $ret = $memcached->get("hoge");
print $ret;
die if $ret != 100001;
sleep 30;
}

まず、memcached-1.4.5に対して実行してみると

$ perl -l mem.pl
1
100001
2
100001
3
100001  
4
100001
5
100001
6
99995
Died at mem.pl line 37.

意外と簡単に、6ループ目で発症。

次に、memcached-1.4.6で検証

$ perl -l mem.pl
1
100001
2
100001
3
...
100
100001
101
100001
...

angry birdsをやりながらしばらく動かしっぱなしにしてたけど、途中で止まったりはしない模様。
ということで、memcached-1.4.6でincr/decrがスレッドセーフではないバグは修正されたと言っても問題ないでしょう

ちなみに、1.4.6では、mixiの障害の原因にもなった接続時の競合も解決されているようなので、要チェックです

新しいWebサービスを開始する際や、既存サービスに変更を加える際に、サーバを何台確保するか、ストレージやAPIといった共有リソースを使用して良いか、ディレクターやアプリケーションエンジニアの方に訪ねられることがありますが(というかそれが仕事ですね)、その際相談のためにどんな情報を持って来て欲しいか書いてみます。人間同様にサーバやネットワークリソースも有限なので、無駄にならない最適なサーバ台数を割り出したり、増強が必要かどうかを判断して、会社のビジネスを効率よく進めていくことが重要です。

人によっては以下に書いてあることが、非常に緩く感じでしまうこともあるかもしれません。これはWebサービスを早く立ち上げて、柔軟に運用していくことができる環境ならではだと思います。それでも出して欲しいモノはいくつかあります

企画書

どんなサービスであるか説明できる企画書があるといいでしょう。ないわけはないと信じてます。既存のサービスの変更であればその変更分の資料になります。Power Pointや方眼紙Excelの立派な資料である必要はありません。1ページの図、wikiの1ページ、手書きでもかまいません。

オペレーションエンジニアはどんなサービスであるかの説明をうけ、どのようにサービスが実装されようとしているのか把握しようとします。実装がどのようになっているかは負荷やパフォーマンスの見積もりに重要な情報となります。

予測・目標PV、UU、登録者数

ほぼ企画書に含まれるであろう情報ですが、新しいサービス、機能にどの程度のアクセスがあるかの予測や目標です。誘導施策がありサービス開始直後のアクセスが多くなると考えられるならその予測と、2ヶ月3ヶ月経過した後の想定があるといいですね。PV予測は上の負荷・パフォーマンスの想定とあわせて、サーバ台数の見積もりに使います。数ヶ月先までの予想があるとサーバ購入のスケジュールが立てられるので、増強の必要が出た場合、スムーズに対応できそうです。

まったくの新規サービスで誘導も目立って行わない場合、PVの予測がないこともあるかもしれません。その際は最小の台数から開始になり、急激な負荷上昇があった場合でも対応するスピードは遅くなるかもしれません

蓄積するデータがある場合、その種類と量

Webサービスにおいて画像やブログ記事などコンテンツをユーザがアップロードする場合、データがサーバに蓄積されていくことになります。多くの場合、サーバに保存されるデータが一定の容量(メモリの容量)に収まっている間はアクセスが高速に行われますが、それを超えると劇的に遅くなります。大量のデータを保存する場合は値段の高い高速なディスクやメモリを多く積んだサーバを採用するか、データの分散ができるようアプリケーションを設計する必要が出てきます。またアップロードされたデータの冗長性を持たせる方法、バックアップについても考えなければなりません。ストレージは意外とコストに繋がります

貯めて行くデータがある場合、それが画像なのか、テキストデータなのかの種類と大体のファイルサイズ、また追加されていくペースが情報としてあると参考になります。

効果測定の方法

KPIの測定や経営陣への報告のため、アクセス数の集計やユーザの統計などを行うと思います。その際にあらかじめどのような測定が必要なのか相談をしておくといいでしょう。PV集計やデータ量の予測にも関係しますが、集計や統計を行う対象となるデータが大きくなればなるほどその作業に時間がかかるようになります。場合によってはサービスに影響がでてしまうほどです。

あらかじめ測定の項目について相談があれば、サービスに影響がでないように集計を行うにはどうしたらいいかを設計段階から考慮にいれることができます。

熱意

最後はネタに思われるかもしれないけど熱意。上にあげて来た項目が多少不足しても、そのサービスがどんなに面白いのか、ユーザがたくさん付くのかを説明する熱意が伝わると、運用する側としても独自に予測を行ったり、負荷が上ぶれても問題のない体制をとるモチベーションとなると思います。

会議に呼ばれても担当者がスマフォソをずっといじっていてサービスの説明すらないのは、協力とかそれ以前の問題です。会議の場では聞いてもないのに新しいサービスの「ほげほげがいいんですよ〜」と言ってくるぐらいのうざい熱意のほうがいい、、、、はず。

一つ前の記事、「今こそ見直すApacheの設定」にはたくさんのアクセスを頂きました。はてなブックマークでホットエントリになったのが大きかったようです。

7/6、7/7のこの記事ページへのアクセスを集計すると

grep 'GET /2011/07/apache.html' access.log|grep ' 200 '|perl -nE 'm!0[67]/Jul/2011! and say $&'|uniq -c
   4966 06/Jul/2011
   6220 07/Jul/2011

となり、合わせて、11,000回のリクエストをサーバが処理したことになります。その際のサーバの負荷ですが、MovableTypeで静的書き出しをしているためまったく負荷らしい負荷はありませんでした。

cloudforecastでサーバのリソースグラフをとっていたので紹介します。環境はさくらのVPS(1GB)に、Ubuntu 10.04、ApacheはUbuntuのパッケージにあった2.2.14を使っています

通信量

まずこれが、2011-07-06 00:00:00 から 2011-07-07 24:00:00 までのトラフィックのグラフ。

traffic.png

青いラインがサーバが出力されたトラフィック、緑がリクエストなどサーバに届くトラフィック

画像が一つもない記事だったのでトラックはどちらにしろ少ないですが、記事公開まで、せいぜい数十kbpsだったのが、16時の記事公開とともに急上昇しています。2時間程度で一度下降して、またそのあと第二のピークを迎えます。おそらくこれがはてなブックマークのホットエントリに上がった瞬間でしょう。

そして夜間はアクセスが落ち、7日のお昼までを頂点に山の形を描いてます。

CPU負荷

同じ時間帯のCPUの使用率のグラフです。

cpu.png

紫色はIDLEです。CPUはほぼ使われていません。MovableTypeの静的書き出しなので動的生成のWordPressなどとは異なりCPUはほぼ使わないで配信が可能です。

Apache mod_status

Apacheのプロセスがどの程度利用されているかのグラフです。緑色がなんらかの処理中を示し、青がIDLEしているスレッド(Preforkならプロセス)を表します。

apache.png

CPU使ってないのに、Apacheがよく使われているねと思われるかもしれませんが、これはほぼ、KeepAliveによるものです。何も送受信してなくても永続接続中のスレッドはビジーとしてカウントされます。

おそらくKeepAliveをOffにすれば、緑色はほぼ0に近づくと思われます

これからも役に立ちそうな記事を書いて行きたいと思うところでございます

nginxやvarnishなどがアツいですが、Apacheもまだまだ実績や安定性から採用されていると思います。ここではデフォルトとは異なる値に変更するサーバ設定を中心に、パフォーマンス改善、安全性向上のためのApacheの設定を紹介します。

mpmの確認

> /path/to/bin/httpd -V
Server version: Apache/2.2.19 (Unix)
Server built:   Jun 23 2011 17:13:13
Server's Module Magic Number: 20051115:28
Server loaded:  APR 1.4.5, APR-Util 1.3.12
Compiled using: APR 1.4.5, APR-Util 1.3.12
Architecture:   64-bit
Server MPM:     Worker

PreforkやWorkerが使われます。Workerはマルチスレッドとマルチプロセスのハイブリッド型で、Preforkより少ないリソースで多くのレスポンスに応答することができます

プロセス/スレッドの設定

Timeout 300

Timeoutはリクエスト/レスポンスの送受信、CGIやProxy(ProxyTimeoutが指定されていない場合)のタイムアウトとして使われます。デフォルト300秒でとくに変更する理由はないでしょう

KeepAlive Off
MaxKeepAliveRequests 20
KeepAliveTimeout 1

クライアントとサーバ間のKeepAliveの設定です。画像配信など特定の目的専用のサーバでない限り、Offのほうが問題が少ないと考えています。Onにする場合は、MaxKeepAliveRequests(1つの接続で何回のリクエストに応えるか)、KeepAliveTimeout(送受信が終わった後何秒で接続を切るかの時間)の数値を大きくしすぎないようにしましょう。アクセスの多いサイトの場合、送受信が終わった後のidle接続が溜まり、MaxClientsに達してしまうなどパフォーマンスに影響がでる可能性があります

ServerLimit          64
ThreadLimit          32
StartServers         16
MaxClients           512
MinSpareThreads      512
MaxSpareThreads      512
ThreadsPerChild      32

Workerの場合は、MaxClientsとThreadPerChildによってプロセス数を制御します。上記の例では、MaxClientsが512、1プロセスあたりのスレッド数(ThreadsPerChild)が32なので、16プロセス立ち上がります。より大きなMaxClientsを指定したい場合は、ServerLimit(プロセス数の上限値)、ThreadLimit(スレッド数の上限値)を変更するといいでしょう。StartServersはプロセス数なので注意

もちろん、プロのサーバ管理者は、StartServers、MaxClients、(Min|Max)SpareThreadsが等しくなるように設定します

でも紹介しています。

MaxRequestsPerChild  5000

1スレッド(Preforkならプロセス)が受け付けるリクエスト数です。指定した値に達するとスレッド(プロセス)は終了し、新しいスレッド(プロセス)と入れ替わります。KeepAliveがOnの場合は、1つの接続が1リクエストとカウントされるので1スレッド(プロセス)が扱う最大接続数と同じ意味になります

hirose31さんのmod_bumpy_lifeを導入するとこのMaxRequestsPerChildにランダム性を取り入れることができます

MaxRequestsPerChildMin 4000
MaxRequestsPerChildMax 5000

このように指定することで、4000から5000の間でランダムにスレッドが終了します。これにより一斉にスレッドが終了し、一時的に負荷が高くなるような問題(Thundering Herd)を避けることができます。

サーバ動作の設定

ServerTokens Prod
ServerSignature Off

レスポンスのヘッダや、エラーページのフッタに Apache のバージョン等を表示するかの設定です。バージョンを隠すことにどれほどの意味があるのかは疑問ですが、表示しないようにするには、ServerTokensにProd(Apacheとだけ表示)、ServerSignature(フッタ)はOffにします。

TraceEnable off

TRACEメソッドは送ったリクエストの内容をそのまま返すHTTP/1.1で定義されているメソッドです。サイトにXSS脆弱性があった場合、XHRでTRACEメソッドを送り、Cookieなどの内容を読み出せる可能性があります。通常必要ないので、Offがお勧めです

RequestReadTimeout header=10 body=30

slowloris対策として導入されたmod_reqtimeoutの設定です。上の設定では、headerをすべて受信するのに10秒、bodyを受信するのに30秒の制限を設けています。

SetEnv proxy-nokeepalive 1

proxyの動作を制御する環境変数です。Apacheのmod_proxyはbackendとの接続を永続化することができますが、それを無効にします。以前からReverseProxyとして使っているApacheがデータの受け渡しのあたりでCPU100%使うbusy loopを起こすことがたびたびあり、これをいれてます。最新版のApacheでそもそも問題が解決しているかは検証していません。

FileETag None

多くの場合Cache-Contorl/Expiresを使うのでETagは必要ないと思います。デフォルトのFileETagは値の計算にinode番号を使うので複数のサーバから同じファイルを送信している場合、サーバ毎に異なるETagが生成されてしまいます。その際には、「Size」とだけ指定するのがお勧めです

NameVirtualHost *:80
<VirtualHost *:80>
    ServerName dummy.example.com

    ...
</VirtualHost>

<VirtualHost *:80>
    ServerName yourservice.com
    ...
</VirtualHost>

IPアドレスや意図しない逆引きアドレスからアクセスされないように、最初にダミーのVirtualHostを設定します。IPアドレスや逆引きアドレス、また意図しない他のアドレスでアクセスされた場合、最初のVirtualHostが選ばれます。エラーページを出してもいいですし、正しいURLへの誘導を行うのもいいでしょう。

合わせて参考にしたい

まぁ、このblogのサーバはほぼデフォルトで動いているんですけどね

自宅サーバから、さくらのVPSに引っ越しました。ちょっと奮発してメモリ1GBを選択。意外と快適ですね

やったこと

  • zsh screen apache2 mysqlなどをいれる
  • cloudforecastをいれた
  • supervisor をいれてcloudforecastとtiarraを動かした
  • ufwを使ってiptablesの設定

あとで詳細を書くかもしれない。

cloudforecastはここで動いています。負荷や通信量が見れます。MySQLの設定がデフォルトのままなのは許して!

今の自宅サーバはPE T105で2年間ほど動いていましたが、しばらくお休みです。夏が終わって電力不足にめどがついたら実験用にまた使うかもしれない。