9月 10

Flareのフェイルオーバー機能をテストしてみました。

ノード監視 + フェイルオーバー (インデックスサーバにより各サーバを監視し、ダウンした場合にはサービスアウトします。また、masterサーバがダウンした場合はslaveサーバのうち1台がmasterサーバに昇格します)
Flare – GREE Labsより

今回は後半のmasterサーバーがダウンした場合のテストです。
実際にダウンしてしまうというよりも、メンテナンスのためにを故意にダウンさせるケースを想定しています。実際の挙動を確認しておくことで、本番環境で自信を持ってmasterサーバーを落とすことができます。

設定ファイル

インストールは公式ドキュメントをご覧いただくとして、各サーバーの設定から。
1台のサーバーにインデックスサーバー*1、各ノードサーバー*3を立ち上げるため、ポートを変えて起動します。

インデックスサーバー

/etc/flare/flarei.conf

data-dir = /opt/flare/flarei
log-facility = local0
max-connection = 256
monitor-threshold = 3
monitor-interval = 1
server-name = localhost
server-port = 12120
thread-pool-size = 8

マスターノードサーバー

/etc/flare/flared_master.conf

data-dir = /opt/flare/flared_master
index-server-name = localhost
index-server-port = 12120
log-facility = local1
max-connection = 1024
mutex-slot = 64
proxy-concurrency = 2
server-name = localhost
server-port = 11211
storage-type = tch
thread-pool-size = 16
storage-ap = 4
storage-bucket-size = 16777216

スレーブノードサーバー

/etc/flare/flared_slave.conf

data-dir = /opt/flare/flared_slave
index-server-name = localhost
index-server-port = 12120
log-facility = local2
max-connection = 1024
mutex-slot = 64
proxy-concurrency = 2
server-name = localhost
server-port = 11212
storage-type = tch
thread-pool-size = 16
storage-ap = 4
storage-bucket-size = 16777216

プロキシノードサーバー

/etc/flare/flared_proxy.conf

data-dir = /opt/flare/flared_proxy
index-server-name = localhost
index-server-port = 12120
log-facility = local3
max-connection = 1024
mutex-slot = 64
proxy-concurrency = 2
server-name = localhost
server-port = 11213
storage-type = tch
thread-pool-size = 16
storage-ap = 4
storage-bucket-size = 16777216

syslog設定

/etc/syslog.conf

# Log flare
local0.*                                                /var/log/flarei.log
local1.*                                                /var/log/flared_master.log
local2.*                                                /var/log/flared_slave.log
local3.*                                                /var/log/flared_proxy.log

通常運用状態を再現

設定が終わったら、各サーバーを起動します。

sudo /usr/local/flare/bin/flarei -f /etc/flare/flarei.conf --daemonize
sudo /usr/local/flare/bin/flared -f /etc/flare/flared_master.conf --daemonize
sudo /usr/local/flare/bin/flared -f /etc/flare/flareid_slave.conf --daemonize
sudo /usr/local/flare/bin/flared -f /etc/flare/flared_proxy.conf --daemonize

起動が終わったらインデックスサーバーへ接続し、node roleコマンドで以下の状態に設定します。

stats nodes
STAT localhost:11211:role master
STAT localhost:11211:state active
STAT localhost:11211:partition 0
STAT localhost:11211:balance 1
STAT localhost:11211:thread_type 16
STAT localhost:11212:role slave
STAT localhost:11212:state active
STAT localhost:11212:partition 0
STAT localhost:11212:balance 2
STAT localhost:11212:thread_type 17
STAT localhost:11213:role proxy
STAT localhost:11213:state active
STAT localhost:11213:partition -1
STAT localhost:11213:balance 0
STAT localhost:11213:thread_type 18

通常運用では、クライアントはproxyサーバーへ接続します。その後proxyサーバーが、get時にはmaster:slave=1:2の比率(設定で変えられます)で分散してアクセスし、set時にはmasterサーバーへアクセスします。書き込まれたmasterサーバーはslaveサーバーへデータをレプリケートします。
この状態で、masterサーバーを落としてみます。

マスターダウンからスレーブの昇格

killコマンドでmasterサーバーを落とすと、インデックスサーバーがダウンを検知し、slaveサーバーを昇格させます。
状態を確認すると、

stats nodes
STAT localhost:11211:role proxy
STAT localhost:11211:state down
STAT localhost:11211:partition -1
STAT localhost:11211:balance 0
STAT localhost:11211:thread_type 16
STAT localhost:11212:role master
STAT localhost:11212:state active
STAT localhost:11212:partition 0
STAT localhost:11212:balance 2
STAT localhost:11212:thread_type 17
STAT localhost:11213:role proxy
STAT localhost:11213:state active
STAT localhost:11213:partition -1
STAT localhost:11213:balance 0
STAT localhost:11213:thread_type 18
END

元masterサーバがダウンしてproxyサーバーに、元slaveサーバーがmasterサーバーに昇格しています。
無事成功しました。

昇格時のログ

ログを確認すると、挙動がよく理解できます。

インデックスサーバーのログ

/var/log/flarei.log

Sep  1 13:55:20 flare-test flarei[10772]: [1152215360][WRN][connection.cc:448-write] connection seems to be already closed (sock=-1)
Sep  1 13:55:21 flare-test flarei[10772]: [1152215360][WRN][connection.cc:122-open] connect() failed: Connection refused (111) -> wait for 500000 usec
Sep  1 13:55:24 flare-test last message repeated 7 times
Sep  1 13:55:25 flare-test flarei[10772]: [1152215360][ERR][connection.cc:129-open] connect() failed
Sep  1 13:55:26 flare-test flarei[10772]: [1152215360][WRN][connection.cc:122-open] connect() failed: Connection refused (111) -> wait for 500000 usec
Sep  1 13:55:29 flare-test last message repeated 7 times
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][ERR][connection.cc:129-open] connect() failed
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:338-down_node] handling node down event [node_key=localhost:11211]
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][ERR][cluster.cc:357-down_node] master node down -> finding an active slave and shift its role to master
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:380-down_node] found new master node (node_key=localhost:11212, partition=0, balance=2)
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:396-down_node] setting node state to down (and clearing role, etc)
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:1308-_reconstruct_node_partition] reconstructing node partition map... (from 3 entries in node map)
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:1416-_reconstruct_node_partition] node partition map:
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:1421-_reconstruct_node_partition] 0: master (node_key=localhost:11212, node_balance=2)
Sep  1 13:55:30 flare-test flarei[10772]: [1152215360][NTC][cluster.cc:1435-_reconstruct_node_partition] node partition map (prepare):
Sep  1 13:55:31 flare-test flarei[10772]: [1152215360][WRN][connection.cc:122-open] connect() failed: Connection refused (111) -> wait for 500000 usec
Sep  1 13:55:34 flare-test last message repeated 7 times

インデックスサーバーは設定ファイルのmonitor-intervalの秒数ごとに各ノードへ死活チェックを行っています。
今回は1(秒)に設定しているので、マスターがダウンしてから最長1秒でダウンを検知します。
検知した後monitor-thresholdの回数分(3回)、死活チェックをリトライします。リトライの間は0.5秒のインターバルがあり、最後の死活チェックが終わると、ノードのダウンを決定し、新しいマスターを探します。
新しいマスター候補が見つかると昇格させ、晴れてマスターになります。

ログでは10秒で昇格を果たしていますが、環境やデータ量によって変わる可能性はありそうです。

スレーブサーバーのログ

/var/log/flared_slave.log

Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:759-reconstruct_node] reconstructing node map... (3 entries)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:785-reconstruct_node] update: node_balance (1 -> 0)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1308-_reconstruct_node_partition] reconstructing node partition map... (from 3 entries in node map)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1416-_reconstruct_node_partition] node partition map:
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1421-_reconstruct_node_partition] 0: master (node_key=localhost:11212, node_balance=2)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1435-_reconstruct_node_partition] node partition map (prepare):
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1061-_shift_node_state] shifting node_state (node_key=localhost:11211, old_state=0, new_state=2)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1078-_shift_node_role] shifting node_role (node_key=localhost:11212, old_role=1, old_partition=0, new_role=0, new_partition=0)
Sep  1 13:55:30 flare-test flared[11476]: [1036380480][NTC][cluster.cc:1078-_shift_node_role] shifting node_role (node_key=localhost:11211, old_role=0, old_partition=0, new_role=2, new_partition=-1)

こちらにもインデックスサーバーの指令を受けて、マスターになるログが残っていました。

クライアントへの影響

マスターをダウンさせた時に接続中だったクライアントがどうなるのかを調べてみました。

set

以下は”key_” + 1〜100万をキーに20バイトの文字列をsetするプログラムで、keyの後ろの数字は処理時間を表しています。

set key_500261:0
set key_500262:0
set key_500263:0
set key_500264:0
set key_500265:16.000910997391
set key_500266:0
set key_500267:0
set key_500268:0

5行目の処理時にマスターがダウンし処理が止まりますが、マスター昇格後、無事書き込みが完了しています。
時間はかかるものの、書き込みは失敗しませんでした。

get

以下は上のプログラムでsetした値をgetするプログラムで、key:value:処理時間を表しています。

get key_245:01234567890123456789:0
get key_246:01234567890123456789:0
get key_247:01234567890123456789:0
get key_248:01234567890123456789:0
get key_249:01234567890123456789:0
get key_250::31.737808942795
get key_251:01234567890123456789:0
get key_252:01234567890123456789:0
get key_253:01234567890123456789:0
get key_254:01234567890123456789:0
get key_255:01234567890123456789:0

6行目の処理時にマスターがダウンし処理が止まり、そのままデータが取得できず、タイムアウトしてしまいました。
スレーブの台数が多ければ止まらなかったかもしれませんが、この構成では5回中5回、全て同じ結果でした。テスト不足ではありますが、getの処理は失敗することがありそうです。

Tagged with:
8月 19

Twitter Client Ranking by Streaming APIの構成は、
PHP5.3.3(with APC) + MongoDB1.6.1 + Apache2.2.3で、フレームワークにsyfmony1.4.6を利用しています。この構成が動く最低限の環境を構築した際のメモです。
さくらのVPSはサービスはほとんど入っていませんでしたが、gccやautoconfは既に入っていました。

#Apache
sudo yum install httpd.x86_64

#PHPのコンパイルに必要(パラメータによる)
sudo yum install httpd-devel.x86_64
sudo yum install libxml2-devel.x86_64
sudo yum install openssl-devel.x86_64
sudo yum install curl-devel.x86_64

#最新版PHP取得→コンパイル→インストール
wget http://ve2.php.net/get/php-5.3.3.tar.gz/from/jp2.php.net/mirror
tar vfxz php-5.3.3.tar.gz
cd php-5.3.3
./configure \
--enable-mbstring \
--with-apxs2=/usr/sbin/apxs \
--with-curl \
--with-openssl \
--with-config-file-path=/etc  \
--enable-dom \
--with-libdir=lib64
meke
sudo make install

#symfonyのインストール
sudo pear channel-discover pear.symfony-project.com
sudo pear install symfony/symfony

#PHP拡張のインストール
sudo pecl install apc
sudo pecl install mongo

#PHP拡張の反映
sudo vi /etc/php.ini

extension = mongo.so
extension = apc.so
apc.enabled=1

PHPもRemiのリポジトリを利用すると簡単に最新版がインストールできますが、cli版がバックグラウンドで実行できない不具合があるのでソース版を利用しました。

参考

MongoDBをインストール

下記URLを参考にMongoDBのリポジトリを設定してインストールします。
参考:CentOS and Fedora Packages

sudo vi /etc/yum.repos.d/10gen.repo 

[10gen]
name=10gen Repository
baseurl=http://downloads.mongodb.org/distros/centos/5.4/os/x86_64/
gpgcheck=0

name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0

sudo yum install mongo-stable* --enablerepo=10gen
sudo yum install  mongo-10gen* --enablerepo=10gen

最新のstableバージョン1.6.1がインストールされます(2010年8月19日現在)最新のstableバージョン1.8.2がインストールされます(2011年7月21日現在)。デフォルトの設定は以下の通りです。
設定ファイル:/etc/mongod.conf
ログファイル:/var/log/mongo/mongod.log
データディレクトリ:/var/lib/mongo/

起動は起動スクリプトでOK

sudo /etc/init.d/mongod start

2011-7-21修正 yumリポジトリの修正。最新バージョンの修正。

Tagged with:
8月 17

はじめに

さくらのVPSをベータ*1として使わせてもらっているのに何もしないのはもったいない&申し訳ないので、Twitter Client Ranking by Streaming APIというのを、さくらのVPSのパフォーマンスとMongoDBのパフォーマンス・使用感をテーマに実験的に作ってみました。

TwitterのStreaming APIからツイートを取得、保存、解析してどんなクライアントが使われているかをランキング形式に表示するという単純なものです。

解析結果をブログで公開している例があるものの、なかなか直近で連続的なデータを公開しているところがなかったので、勢いで作りました。

さくらのVPS

さくらのVPSの仕様は以下の通りです。

メモリ 512MB
HDD 20GB
回線 100Mbps
OS CentOS 5 x86_64
グローバル IP アドレス IPv4 アドレス×1 個
データ転送量 無制限
管理者権限 root 権限付与

データを継続的に保存するのにHDDが20GBと心許ないので古くなったデータはどんどん削除していく必要がありそうです。一方でデータ転送量が無制限となっているので、APIからの取得に(料金的な意味で)気を使う必要はなさそうです。
OSは最新のCentOS5.5で、サービスはsendmailとssh以外は何も入っていないので、httpdなどを自由に追加して利用します。また、ポートはすべて公開状態なのでiptablesを設定、有効にすることも必要です。

Twitter Streaming API

Twitter Streaming APIのstatuses/sampleメソッドを利用します。sampleメソッドは公開タイムラインの1%未満がサンプリングされたデータとなります。実際にアクセスをしてみると1分間に350〜650ツイート取得でき、量にかなりムラがあることがわかりました。
HDDの容量を考えて、データはcronで6分毎に1分間取得することにしたので、サンプルは1/6%、全ツイートの約0.167%未満になりそうです。数日間取得してみたところ、1日で約10万〜12万ツイートをサンプリングできています。

MongoDB

MongoDBはドキュメント指向DBと呼ばれる仲間で、Key-Value StoreにRDBMSの色を少し足したようなイメージです。スキーマはダイナミックに変更できる一方で、SQLこそ使えないものの、where, sort, limit, group(by), max, min,などが用意されていて、RDBMSに慣れている人にもとっつきやすいと思います。
さらに、スケールアウトを容易にするSharding機能を標準装備していて、分散したデータベースから複雑な集計ができるMap/Reduceが利用できます。

RDBMSでDBやテーブルにデータを入れようと思った時はあらかじめCREATEしておく必要がありますが、MongoDBは必要ないので存在しないDBやコレクション(RDBMSでいうテーブル)にいきなりデータを投入してもエラーは出ず、コレクションやDBすらダイナミックに作られます。この辺りの挙動はファイル操作に近いので、日付ごとにダイナミックにコレクションを作ることもでき、今回のログの保管などの用途にも適しています。

日付ごとにコレクションを作り、値にはtimestamp, date, source, source_url, time_zone, utc_offset, langを保存することにしました。Sharding環境ではないのでMap/Reduceの力を発揮することもできないのですが、実験的な意味で、データの集計にはMap/Reduce*2を利用しています。

使い方

デフォルトは1日前のサンプリングしたすべてのデータが表示されます。そこから、日付、Lang、Time zone、UTC offsetで絞り込みすることで様々な形で抽出することができます。
例えば、「8月12日のツイートの中から東京(time_zone=Tokyo)で日本語(lang=ja)のツイートをするのに利用されているクライアント」などを抽出・集計することができます。

ランキングだけでは味気ないので、TOP10を円グラフで表すのと、各クライアントが時間ごとにどのくらい利用されているのか3つまで比較出来る機能を用意してみました。住んでいるところやクライアントの種類で利用されている時間外が全然違うのがわかります。

現状、なかなかもっさりとした感じなので、イライラするかもしれません。
実験なので、負荷の問題でサービスを終了するかもしれませんし、逆に、作っただけではなくMongoDBのTipsやチューニングなどで改善し、それをさらにネタにして行きたいとも思っています。

*1 7月15日〜8月31日までクローズドベータとして会員限定でプレリリースされたホスティングサービス。2010年9月1日から初期費用無料、月額980円の「さくらのVPS 980」として正式リリースされる。

*2 今回の実験ではmapReduce()よりgroup()を使った集計の方が速度が速いことが多かったので、当日分のリアルタイム集計ではgroup()を利用しています。mapReduce()の結果は新しくコレクションとして保存することができるので、過去分に関してはmapReduce()で抽出→コレクションとして保存→そこから再集計という実装にしています。

Tagged with:
preload preload preload