最後はCouchDBへの負荷を分散させる方法を紹介します。Loungeを使えば、複数のCouchDBをノードとして集約し、1つのデータベースに見立てることができます。1つのサーバでは対処できないリクエストをさばく場合に有効です。また、データの量やDBへのリクエストの増加に対して、サーバを追加することで対応できます。
いまは大した負荷がなくても、データやリクエストの増加を見込んで、あらかじめスケールしやすい構成に仕込んでおくのもよいでしょう。また、インスタンスをノードとして登録するときに、ノードが落ちた場合に代替で起動するほかのノードを指定することができます。これにより、可用性の向上も期待できます。
Loungeの構成を下図にまとめました。クライアントからのリクエストはdumbproxyというリバースプロキシが受け付け、リクエストの対象となるノードに処理を振り分けます。dumbproxyはNginxを基にして作られています。
リクエストの中で、すべてのノードが対象になる処理、例えば、デザインドキュメントのMap&Reduceや_all_dbs APIといった処理は、いったんsmartproxyというTwistedを利用したリバースプロキシに処理が引き継がれ、実行されます。また、Loungeの仕様では、デザインドキュメントをノードすべてに配置する必要があり、スクリプトを定期的に実行することでデザインドキュメントの同期処理を取っています。
Loungeを以下の手順で導入してみましょう。GitHubからソースコードをcloneしたあと、Loungeに必要なプログラムをコンパイルし、インストール していきます。
ノードとして利用するCouchDBは、Lounge用のパッチを適用したあとにコンパイルします。このパッチを適用すると、レプリケーションの対象をデザインドキュメントのみに制限できるオプションが使えるようになります。パッチファイルは0.9.0〜0.10.1各バージョンごとにcouchdb-lounge/couchdbに用意されています。
(0.10.1のCouchDBにパッチファイルを適用する例) $ patch -p1 < ../couchdb-lounge/couchdb/couchdb-0.10.1-initenabled.patch patching file configure.ac $ patch -p2 < ../../couchdb-lounge/couchdb/couchdb-0.10.1-designreplication.patch patching file couchdb/couch_rep.erl patching file couchdb/couch_rep_reader.erl
次に、local.ini内の[update_notification]に定期的にレプリケーションを実行するスクリプトを設定します。レプリケーションの目的はデザインドキュメントの同期取りと、マスターのDBからスレーブのDBへデータをコピーすることです。replication_notifier.pyを任意のディレクトリにコピーし、パーミッションを変更します。
コード内では、host名を取得する関数を使用していますが、IPアドレスを指定してCouchDBを使う場合は適宜変更してください。また、UPDATES_PER_REPLICATIONを調整して、レプリケーションを実行する頻度を調整できます。初期値では、DBへの更新回数が10以上になった時点で実行する設定になっています。
$ sudo install -m 755 replicator/replication_notifier.py /usr/local/bin/
# replication_notifier.pyの中 # 40行目 + socket.gethostname() + を適宜変更 me = 'http://' + socket.gethostname() + ':5984/' #43行目 更新の頻度に応じてレプリケーション UPDATES_PER_REPLICATION = 10
;/usr/local/etc/couchdb/local.ini 内部 [update_notification] replicator = /usr/bin/python /usr/local/bin/replication_notifier.py
replication_notifier.pyが使用するpython-loungeモジュールをインストールします。logを格納するディレクトリも作成しておきましょう。
$ cd python-lounge/ $ sudo python setup.py install $ sudo mkdir /var/log/lounge/replicator/
続いて、dumbproxyとsmartroxyを導入します。dumbproxyに同梱されているjson-cライブラリも同時にインストールします。この2つのプロキシはCouchDBのノードとは別のサーバに配置しても構いません。
# json-cのインストール $ cd json-c/ $ tar zxvf libjsonc_0.7.orig.tar.gz $ gunzip libjsonc_0.7-1.diff.gz $ patch -p0 < ../libjsonc_0.7-1.diff $ ./configure $ sudo make $ sudo make install #dumbproxyのインストール #Ubuntuだと、pidディレクトリが必要。またはconfigure時に変更。 $ sudo mkdir /var/run/pid $ cd dumbproxy/ #筆者の環境ではlibpcre++-devも追加で必要でした。 $ sudo apt-get install libpcre++-dev $ ./configure $ sudo make $ sudo make install # smartproxyのインストール $ sudo apt-get install python-twisted $ sudo apt-get install python-cjson $ sudo apt-get install python-simplejson $ sudo apt-get install python-pyicu $ cd smartproxy/ $ sudo make $ sudo make install
dumbproxyとsmartproxyのパラメーターを設定します。nginx.confはcouchdb-lounge/dumbproxy/conf/nginx.confからコピーします。ホスト名やIPアドレスなど、適宜変更しましょう。
#/etc/lounge/nginx/nginx.conf http { lounge-replication-map /etc/lounge/shards.conf; server_name_in_redirect off; #dumbproxyが稼働するサーバ名,IP upstream lounge_backend { server xxxx:666; lounge-proxy; } #smartproxyが稼働するサーバ名,IP upstream smart_proxies { server xxxx:2008; } #Loungeから使うFutonユーティリティなどを提供するCouchDB upstream passthrough { server xxxx:5984; } #Loungeとしてサービスを提供するサーバ名,IP server { listen 6984; server_name xxxx; location ~ ^/[^/]*/_view/.*$ { proxy_buffering off; ... }
#/etc/lounge/smartproxy.tac #27行目 smartproxyがリクエストを受けるサーバ名,IP i = internet.TCPServer(http_port, site, interface="0.0.0.0")
/etc/lounge/shards.confを編集し、ノードとなるCouchDBサーバを登録します。この設定はノードごとに行う必要があります。設定例を図解したものも合わせてご覧ください。
{ "shard_map": [[0,1], [1,0]], "nodes": [ ["site-a", 5984], ["site-b", 5984] ] }
“nodes”には、Loungeに組み込むCouchDBのホスト名とポートを指定します。上の例の場合、ノードは2つです。左からsite-aがノード0番、site-bがノード1番となります。
“shard_map”にはDBをいくつに分割するかを指定します。この場合はDBを2つに分割することになり、bookというDBは内部的にはbook0,book1に分かれます。最初の[0,1]がshardの0番目(book0)を意味し、カッコの中の“0”はマスターを0番のノードに配置することを意味します。次の“1”という値は、障害発生時にスレーブとして動かすノードを指定します。
例の場合、site-aで障害が発生した場合、site-bが“book0”も含めてすべてのリクエストに対応します。ユーザーはsite-aの障害を意識することはありません。ちなみに、各ノードには同じホスト名を指定することもできます。それだとデータの分散にはなりませんが、Loungeのテストをしたい場合は便利なやり方です。
2〜3個程度のノードであればshards.confを手動で設定してもよいですが、数が大きくなると設定が面倒です。Loungeのgoogle codeにあるPythonスクリプトを使えば、shards.confを自動的に生成してくれます。
$python shard_gen nodelist 4 1 > shards.conf
ノードとなる各CochDBと2つのプロキシを順次起動していきましょう。CouchDBを起動するコマンドはノードごとに実行します。
$ sudo -i -u couchdb couchdb -b $ sudo /etc/init.d/nginx-lounge start $ sudo /etc/init.d/smartproxyd start
6984番のポートを指定して、Futonを起動します(http://xxxx:6984/_utils)。いつもと同じようにデータベースを作成し、ドキュメントが作成できたでしょうか。ノードを意識することなく、いつもどおりにドキュメントを操作することができます。curlやプログラムからアクセスしても同じです。そのあとに、5984番のポートを指定してCouchDBのノードを個別に見てみると、作成したデータベースに番号が振られて分割して格納している状態が確認できると思います。作成したドキュメントはどこかのノードに格納されているはずです。
Loungeを使用してみての感想ですが、パッチを当てないとCouchDBをノードとして登録できない点には導入面での敷居の高さを感じました。CouchDBの次期バージョンでは、レプリケーションの内容をフィルタリングする機能が検討されているようです。フィルタリングでデザインドキュメントのみをレプリケーションできるようになれば、通常のCouchDBをLoungeのノードとして登録できるようになり、Loungeもより気軽に導入できるようになると思います。世界中に散らばったCouchDBを必要な数だけ集約し、大規模な分散DBをまたたく間に構築できる世界を想像すると心が躍ります。
CouchDB本体の開発だけではなく、couchdb-luceneやLoungeといった本体の機能を拡張するソフトウェアも精力的に開発が続けられています。最新の情報はCouchDBのメーリングリストを購読したり、CouchDBやコミッタのTwitterアカウントをフォローすると、タイムリーに得ることができます。詳細はCouchDB Wikiの“Community”の欄をご覧ください。
次回はCouchDBのソースコードを読み解きながら、その内部構造に迫る予定です。ご期待ください!
z.ohnami
CouchDB JP所属。
くつろげるDB環境を追い求め、CouchDBと出会う。以後、趣味の1つとしてCouchDBをたしなむ。仕事では、メインフレームのプロジェクトでDBAを担当後、オープン系がメインの現場でDB設計の標準化活動とプロジェクト支援に携わる。
Copyright © ITmedia, Inc. All Rights Reserved.