Varnish を CouchDB 用のReverse Proxyに活用する。
ご無沙汰しております、生きています。
さて、CouchDB 1.0 がでて、incremental replication もだいぶ安定した感があるので、自宅の CouchDB でロードバランスしたくなりました。
Apache mod_proxy_balancer という手もあるのですが、Incremental (MapReduce + Replication) という環境では、High Performance な Caching Proxy になる Varnish という選択肢もおもしろそうなので試しています。
構成
- CPU: Opteron Dual Core 1.8 GHz
- Memory: 8GB
- Servers: http://relax:5984/, slave: http://relax:15984/ (master と slave は同じサーバー上。ただし、ディスクは別)
で twitter のタイムラインをバックアップしている http://www.yssk22.info/relax/_design/mytweets/_list/timeline/by_date?descending=true のデータを対象にします。1つのドキュメントは 1.5KB ぐらいです。
CouchDB の設定
init.d の起動スクリプトに Incremental Replication を仕込んでおきます。これ忘れると、うっかり再起動したときにレプリケーションが止まっている、とかそういう感じになるので注意。
#!/bin/sh /opt/relax/master/couchdb/pro-1.0.0/etc/init.d/couchdb $@ /opt/relax/slave/couchdb/pro-1.0.0/etc/init.d/couchdb $@ start_replication () { sleep 1 # wait for slave waking up curl -X POST --data '{"source": "http://127.0.0.1:5984/relax", "target": "http://127.0.0.1:15984/relax", "continuous" : true }' http://localhost:5984/_replicate } case "$1" in start) start_replication ;; restart) start_replication ;; *) ;; esac
Varnish の設定
初心者なので、 vcl にこんな感じで とりあえず GETとHEAD をロードバランスしてくれればいいや、と。
まずは、効果測定のために、Varnish キャッシュを無効にしておきました。
backend default { .host = "127.0.0.1"; .port = "5984"; } backend slave { .host = "127.0.0.1"; .port = "15984"; } director couchdb round-robin { { .backend = default; } { .backend = slave; } } sub vcl_recv { if (req.request != "GET" && req.request != "HEAD") { return (pass); } set req.backend = couchdb; return (pass); }
ab してみる
クライアントには iMac (Core2Duo 3.02GHz, 4GB Memory) を使います。
まずは、直接CouchDBにある 1 tweet を直接たたく感じで。
$ ab -n 1000 -c 30 "http://relax:5984/relax/fffdcaf2b6bafa2f6a39a513d48d5e45" Concurrency Level: 30 Time taken for tests: 2.431 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 1687000 bytes HTML transferred: 1452000 bytes Requests per second: 411.42 [#/sec] (mean) Time per request: 72.918 [ms] (mean) Time per request: 2.431 [ms] (mean, across all concurrent requests) Transfer rate: 677.80 [Kbytes/sec] received
- c 30 が正しいかはともかく(もうちょっと多くてもいいかな...)、こんな感じです。
Varnish 経由でたたいてみます。
$ ab -n 1000 -c 30 "http://relax:6081/relax/fffdcaf2b6bafa2f6a39a513d48d5e45" Concurrency Level: 30 Time taken for tests: 1.671 seconds Complete requests: 1000 Failed requests: 7 (Connect: 0, Receive: 0, Length: 7, Exceptions: 0) Write errors: 0 Non-2xx responses: 7 Total transferred: 1747622 bytes HTML transferred: 1445098 bytes Requests per second: 598.52 [#/sec] (mean) Time per request: 50.123 [ms] (mean) Time per request: 1.671 [ms] (mean, across all concurrent requests) Transfer rate: 1021.48 [Kbytes/sec] received
ちょっと効果がでたようです。beam.smp 自体は concurrent に動いてはいるんですけどね。いろんな作用のせいで(ちょっと詳しく見る必要ありそう)、単体時が 500 rec/sec, RB時が 600 rec/sec ぐらいになっていました。
ab してみる (2)
次に MapReduce View。これは更新しながらはからないと余り意味がないのですが、とりあえず、tweetを日付順に並び替えて、10件とるURIをたたきまくります。
CouchDB 直接。
$ ab -n 1000 -c 30 "http://relax:5984/relax/_design/mytweets/_view/by_date?limit=10&descending=true" Concurrency Level: 30 Time taken for tests: 13.289 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 27286000 bytes HTML transferred: 27082000 bytes Requests per second: 75.25 [#/sec] (mean) Time per request: 398.667 [ms] (mean) Time per request: 13.289 [ms] (mean, across all concurrent requests) Transfer rate: 2005.17 [Kbytes/sec] received
Varnish 経由。
$ ab -n 1000 -c 30 "http://relax:6081/relax/_design/mytweets/_view/by_date?limit=10&descending=true" Concurrency Level: 30 Time taken for tests: 10.043 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 27377000 bytes HTML transferred: 27082000 bytes Requests per second: 99.57 [#/sec] (mean) Time per request: 301.293 [ms] (mean) Time per request: 10.043 [ms] (mean, across all concurrent requests) Transfer rate: 2662.06 [Kbytes/sec] received
こちらも若干改善された感じ。全部メモリにのっちゃうのでそんな変わらないかなー、と思いきや。
ab してみる (3)
最後に、HTMLレンダリングのURIもやってみます。これは毎回JSプロセスがCGI的に動くので、どうでしょう。
$ ab -n 1000 -c 30 "http://relax:5984/relax/_design/mytweets/_list/timeline/by_date?limit=10&descending=true" Concurrency Level: 30 Time taken for tests: 40.317 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 7127000 bytes HTML transferred: 6942000 bytes Requests per second: 24.80 [#/sec] (mean) Time per request: 1209.498 [ms] (mean)
割とがんばっている気がします。JSでHTMLをレンダリングするって結構重い処理なので。。。(pure JS実装のtemplate engineしかないもので。。。
Varnish版。
$ ab -n 1000 -c 30 "http://relax:6081/relax/_design/mytweets/_list/timeline/by_date?Concurrency Level: 30 Time taken for tests: 34.386 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 7217500 bytes HTML transferred: 6942000 bytes Requests per second: 29.08 [#/sec] (mean) Time per request: 1031.594 [ms] (mean) Time per request: 34.386 [ms] (mean, across all concurrent requests) Transfer rate: 204.97 [Kbytes/sec] received
あんまりかわらなくなっちゃいました。
じゃあ、Varnish Cache を有効にしてみようか。
vcl_recv で最後に lookup します。尚、キャッシュはデフォルトの1GB。
sub vcl_recv { if (req.request != "GET" && req.request != "HEAD") { return (pass); } set req.backend = couchdb; return (lookup); }
測定
$ ab -n 1000 -c 30 "http://relax:6081/relax/_design/mytweets/_list/timeline/by_date?descending=true" Concurrency Level: 30 Time taken for tests: 1.673 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 196220832 bytes HTML transferred: 195932256 bytes Requests per second: 597.55 [#/sec] (mean) Time per request: 50.205 [ms] (mean) Time per request: 1.673 [ms] (mean, across all concurrent requests) Transfer rate: 114504.36 [Kbytes/sec] received
20倍ぐらい高速になりました。 tweet 自体は cron で 10 分ごとにまとめてとってきてCouchDBにつっこんでいるので、そのタイミングで Varnish Cache を破棄させるもよし、 CouchDB の update notirifcation を使って Cache を破棄させるもよし。
なんかこの組み合わせはすばらしい気がする。
もう少し気になること。
master -> slave 構成ですが、逆向きのincremental replicationも起動時に仕込んでおけば、POST, PUT もVarnishにくわせることができるんじゃないかと。