Droongaクラスタを自分で構築して、Groonga互換のサーバとして利用できるようにするための手順を学ぶこと。
Droongaは分散アーキテクチャに基づくデータ処理エンジンで、「distributed-Groonga」がその名の由来です。 名前が示す通り、Droongaはいくつかの点での改善(具体的には、レプリケーションとシャーディング)を含んだGroonga互換のサーバとして動作することができます。
アーキテクチャ、設計、APIなどの点で、DroongaはGroongaと大きく異なっています。 しかしながら、Droongaを単にGroonga互換のサーバとして使う限りにおいては、Droongaのアーキテクチャ全体を理解する必要はありません。
例として、ニューヨークにあるスターバックスの店舗を検索できるデータベースシステムを作成することにします。
Droongaベースのデータベースシステムは、Droongaクラスタと呼ばれます。 この節では、Droongaクラスタを0から構築する方法を解説します。
Droongaクラスタは、Droongaノードと呼ばれる1つ以上のコンピュータによって構成されます。 まず、Droongaノードにするためのコンピュータを用意しましょう。
このチュートリアルは、既存のコンピュータを使ってDroongaクラスタを構築する手順について解説しています。
以下の説明は基本的には、DigitalOcean上のサーバでUbuntu 15.10 x64
またはCentOS 7 x64
の仮想マシンが正しく準備されており、コンソールが利用できる状態になっている、という前提に基づいています。
単にDroongaを試したいだけの場合は、自分のコンピュータ上に複数台の仮想マシンを用意する手順の解説も参照してみて下さい。
注意:
hostname -f
で報告されるホスト名、またはhostname -i
で報告されるIPアドレスが、クラスタ内の他のコンピュータからアクセス可能なものであることを確認して下さい。curl
コマンドがインストールされていることを確認して下さい。
curl
はインストールスクリプトをダウンロードするために必要です。有効なレプリケーションを実現するためには2台以上のコンピュータを用意する必要があります。 ですので、このチュートリアルでは以下のような2台のコンピュータがあると仮定して説明を進めます:
192.168.100.50
で、ホスト名がnode0
であるコンピュータ。192.168.100.51
で、ホスト名がnode1
であるコンピュータ。Groongaはバイナリのパッケージを提供しているため、環境によっては簡単にインストールできます。 (Groongaのインストール手順を参照)
それに対し、コンピュータをDroongaノードとしてセットアップする手順は以下の通りです:
droonga-engine
をインストールする。droonga-http-server
をインストールする。上記の手順を各コンピュータに対して実施する必要があることに注意して下さい。 しかしながら、各手順は非常に簡単です。
それでは、node0
(192.168.100.50
)にログインしてDroongaの構成コンポーネントをインストールしましょう。
まず、droonga-engine
をインストールします。
これはDroongaシステムの主要な機能を提供する、核となるコンポーネントです。
インストールスクリプトをダウンロードし、root権限でbash
で実行して下さい:
# curl https://raw.githubusercontent.com/droonga/droonga-engine/master/install.sh | \
bash
...
Installing droonga-engine from RubyGems...
...
Preparing the user...
...
Setting up the configuration directory...
This node is configured with a hostname XXXXXXXX.
Registering droonga-engine as a service...
...
Successfully installed droonga-engine.
そのノード自身の名前(コンピュータのホスト名から推測されたもの)がメッセージの中に出力されていることに注意して下さい。 この名前は様々な場面で使われますので、各ノードの名前が何であるかを忘れないようにして下さい。
次に、droonga-http-server
をインストールします。
これはHTTPのリクエストをDroongaネイティブのリクエストに変換するために必要な、フロントエンドとなるコンポーネントです。
インストールスクリプトをダウンロードし、root権限でbash
で実行して下さい:
# curl https://raw.githubusercontent.com/droonga/droonga-http-server/master/install.sh | \
bash
...
Installing droonga-http-server from npmjs.org...
...
Preparing the user...
...
Setting up the configuration directory...
The droonga-engine service is detected on this node.
The droonga-http-server is configured to be connected
to this node (XXXXXXXX).
This node is configured with a hostname XXXXXXXX.
Registering droonga-http-server as a service...
...
Successfully installed droonga-http-server.
ここまでの操作が終わったら、同じ操作をもう1台のコンピュータ node1
(192.168.100.51
) に対しても行います。
これで、無事に2台のコンピュータをDroongaノードとして動作させるための準備が整いました。
各Droongaノードは、他のノードと通信するために、そのノード自身のアクセス可能な名前を把握している必要があります。
インストールスクリプトはそのノードのアクセス可能なホスト名を自動的に推測します。 どのような値がそのノード自身のホスト名として認識されたかは、以下の手順で確認できます:
# cat ~droonga-engine/droonga/droonga-engine.yaml | grep host
host: XXXXXXXX
しかしながら、そのコンピュータが適切に設定されていないと、この自動認識に失敗することがあります。
例えば、そのノードのホスト名がnode0
であると設定されているにも関わらず、他のノードがnode0
というホスト名から実際のIPアドレスを名前解決できないと、そのノードは他のノードから送られてくるメッセージを何も受信することができません。
そのような場合、以下のようにして、そのノード自身のIPアドレスを使ってノードを再設定する必要があります:
(on node0=192.168.100.50)
# host=192.168.100.50
# droonga-engine-configure --quiet --reset-config --reset-catalog \
--host=$host
# droonga-http-server-configure --quiet --reset-config \
--droonga-engine-host-name=$host \
--receive-host-name=$host
(on node1=192.168.100.51)
# host=192.168.100.51
...
この操作により、コンピュータ node0
は 192.168.100.50
というホスト名のDroongaノード、コンピュータ node1
は 192.168.100.51
というホスト名のDroongaノードとして設定されます。
前述した通り、ここで設定された名前は様々な場面で使われますので、各ノードの名前が何であるかを忘れないようにして下さい。
このチュートリアルでは、各コンピュータはお互いのホスト名node0
とnode1
を正しく名前解決できるものと仮定します。
あなたの環境ではホスト名の解決ができないという場合には、以下の説明の中のnode0
とnode1
は、実際のIPアドレス(例えば192.168.100.50
と192.168.100.51
)に読み替えて下さい。
ちなみに、インストールスクリプトに対しても、以下のように、環境変数を使って任意の値をホスト名として指定することができます:
(on node0=192.168.100.50)
# host=192.168.100.50
# curl https://raw.githubusercontent.com/droonga/droonga-engine/master/install.sh | \
HOST=$host bash
# curl https://raw.githubusercontent.com/droonga/droonga-http-server/master/install.sh | \
ENGINE_HOST=$host HOST=$host bash
(on node1=192.168.100.51)
# host=192.168.100.51
...
この方法は、使おうとしているコンピュータがお互いのホスト名を名前解決できないことがあらかじめ分かっている場合に便利でしょう。
現時点で、これらのノードはまだ個別に動作する状態になっています。 それでは、これらを1つのクラスタとして動作するように設定しましょう。
以下のようなコマンドを各ノードで実行して下さい:
# droonga-engine-catalog-generate --hosts=node0,node1
当然ながら、--hosts
オプションには各ノードの正しいホスト名を指定する必要があります。
もしこれらのノードがIPアドレスをホスト名として設定されている場合には、コマンド列は以下のようになります:
# droonga-engine-catalog-generate --hosts=192.168.100.50,192.168.100.51
これで、Droongaクラスタの準備が完了しました。 2つのノードは1つのDroongaクラスタとして動作するための準備が完了しています。
引き続き、クラスタの使い方の説明に進みましょう。
Droongaクラスタと通信する方法としては、HTTPとDroongaネイティブプロトコル(fluentdのプロトコルと同じ物)の2つの異なるインターフェースがあります。
このセクションでの説明は、主にHTTPでの使い方について述べます。
GroongaをHTTPサーバとして使う場合は、以下のように -d
オプションを指定するだけでサーバを起動できます:
# groonga -p 10041 -d --protocol http /tmp/databases/db
一方、DroongaクラスタをHTTP経由で使うためには、各Droongaノードにおいて複数のサーバ・デーモンを起動する必要があります。
Droongaノードをインストールスクリプトを使ってセットアップした場合、デーモンは既に、systemctl
コマンドによって管理されるシステムのサービスとして設定されています。
サービスを起動するには、以下のようなコマンドを各Droongaノードで実行して下さい:
# systemctl start droonga-engine
# systemctl start droonga-http-server
これらのコマンドにより、各サービスが動作し始めます。 これで、2つのノードは1つのクラスタを形成し、お互いの状態を監視し合う状態になりました。 もしノードが1つ停止しても、他のノードが生存していれば、それらの生存ノードだけでDroongaクラスタは動作し続けます。 ですので、秘密裏のうちに機能停止したノードを復旧したりクラスタに復帰させたりすることができます。
重要な注意事項:droonga-engine
とdroonga-http-server
のどちらも、起動時に自動的に依存性を解決するようになっています。
例えば、これらはSerfのバイナリをダウンロードしてきます。
すべての依存性が解決された後で初めて、これらのサービスは動作を開始します。
ですので、初回起動時には、正常動作が始まるまでしばらく待つ必要があります。
クラスタが動作している事を、system.status
コマンドを使って確認してみましょう。
コマンドはHTTP経由で実行できます:
$ curl "http://node0:10041/droonga/system/status"
{
"nodes": {
"node0:10031/droonga": {
"status": "active"
},
"node1:10031/droonga": {
"status": "active"
}
},
"reporter": "..."
}
この結果は、2つのノードが正常に動作している事を示しています。 Droongaはクラスタで動作するので、他のエンドポイントも同じ結果を返します。
$ curl "http://node1:10041/droonga/system/status"
{
"nodes": {
"node0:10031/droonga": {
"status": "active"
},
"node1:10031/droonga": {
"status": "active"
}
},
"reporter": "..."
}
droonga-http-server
はクラスタ内のすべてのdroonga-engine
に接続し、ロードバランサーのように、リクエストをそれらへ分配します。
また、もしいくつかのdroonga-engine
が停止しても、droonga-http-server
はそれらの死んだノードを自動的に回避するため、クラスタは正常に動作し続けます。
サービスを停止するには、以下のコマンドを各Droongaノード上で実行します:
# systemctl stop droonga-engine
# systemctl stop droonga-http-server
確認が終わったら、再度サービスを起動しておきましょう:
ここまでに述べたHTTPでの使い方に対して、Gemパッケージdroonga-client
に含まれるコマンドラインユーティリティのdroonca-request
コマンドを使うと、droonga-engine
のサービスに対してネイティブ形式のメッセージを送ることができます。
例えば、droonga-http-server
のサービスを経由せずにdroonga-engine
ノードにsystem.status
のリクエストを送る場合は以下の要領です:
$ echo '{"dataset":"Default","type":"system.status"}' | \
droonga-request --host node0 --receiver-host node0
Elapsed time: 0.023726995
{
"inReplyTo": "1430292510.4677904",
"statusCode": 200,
"type": "system.status.result",
"body": {
"nodes": {
"node0:10031/droonga": {
"status": "active"
},
"node1:10031/droonga": {
"status": "active"
}
},
"reporter": "..."
}
}
上記の通り、Droongaのネイティブ形式のメッセージはJSON形式で書かれます。 これはGroongaのやり方と大きく異なるので、このチュートリアルではひとまず詳細は割愛します。
以上の手順で、Groonga HTTPサーバ互換のHTTPサーバとして動作するDroongaクラスタができました。
リクエストの送信方法はGroongaサーバの場合と全く同じです。
新しいテーブル Store
を作るには、table_create
コマンドにあたるGETリクエストを送信して下さい:
$ endpoint="http://node0:10041"
$ curl "$endpoint/d/table_create?name=Store&flags=TABLE_PAT_KEY&key_type=ShortText"
[
[
0,
1401358896.360356,
0.0035653114318847656
],
true
]
リクエストの送信先として、Droongaノード中でdroonga-http-serverが動作しているDroongaノードのどれか1つを指定する必要がある事に注意して下さい。 言い換えると、接続先(エンドポイント)としてはクラスタ中のどのノードでも好きな物を使う事ができます。 すべてのリクエストは、クラスタ中の適切なノードに配送されます。
さて、テーブルを正しく作成できました。
table_list
コマンドを使って、作成されたテーブルの情報を見てみましょう:
$ curl "$endpoint/d/table_list"
[
[
0,
1401358908.9126804,
0.001600027084350586
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"default_tokenizer",
"ShortText"
],
[
"normalizer",
"ShortText"
]
],
[
256,
"Store",
"/home/vagrant/droonga/000/db.0000100",
"TABLE_PAT_KEY|PERSISTENT",
"ShortText",
null,
null,
null
]
]
]
Droongaはクラスタで動作するので、他のエンドポイントも同じ結果を返します。
$ curl "http://node1:10041/d/table_list"
[
[
0,
1401358908.9126804,
0.001600027084350586
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"default_tokenizer",
"ShortText"
],
[
"normalizer",
"ShortText"
]
],
[
256,
"Store",
"/home/vagrant/droonga/000/db.0000100",
"TABLE_PAT_KEY|PERSISTENT",
"ShortText",
null,
null,
null
]
]
]
次は、column_create
コマンドを使って Store
テーブルに name
と location
という新しいカラムを作ります:
$ curl "$endpoint/d/column_create?table=Store&name=name&flags=COLUMN_SCALAR&type=ShortText"
[
[
0,
1401358348.6541538,
0.0004096031188964844
],
true
]
$ curl "$endpoint/d/column_create?table=Store&name=location&flags=COLUMN_SCALAR&type=WGS84GeoPoint"
[
[
0,
1401358359.084659,
0.002511262893676758
],
true
]
インデックスも作成しましょう。
$ curl "$endpoint/d/table_create?name=Term&flags=TABLE_PAT_KEY&key_type=ShortText&default_tokenizer=TokenBigram&normalizer=NormalizerAuto"
[
[
0,
1401358475.7229664,
0.002419710159301758
],
true
]
$ curl "$endpoint/d/column_create?table=Term&name=store_name&flags=COLUMN_INDEX|WITH_POSITION&type=Store&source=name"
[
[
0,
1401358494.1656318,
0.006799221038818359
],
true
]
$ curl "$endpoint/d/table_create?name=Location&flags=TABLE_PAT_KEY&key_type=WGS84GeoPoint"
[
[
0,
1401358505.708896,
0.0016951560974121094
],
true
]
$ curl "$endpoint/d/column_create?table=Location&name=store&flags=COLUMN_INDEX&type=Store&source=location"
[
[
0,
1401358519.6187897,
0.024788379669189453
],
true
]
結果を確認してみましょう:
$ curl "$endpoint/d/table_list"
[
[
0,
1416390011.7194495,
0.0015704631805419922
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"default_tokenizer",
"ShortText"
],
[
"normalizer",
"ShortText"
]
],
[
261,
"Location",
"/home/droonga-engine/droonga/databases/000/db.0000105",
"TABLE_PAT_KEY|PERSISTENT",
"WGS84GeoPoint",
null,
null,
null
],
[
256,
"Store",
"/home/droonga-engine/droonga/databases/000/db.0000100",
"TABLE_PAT_KEY|PERSISTENT",
"ShortText",
null,
null,
null
],
[
259,
"Term",
"/home/droonga-engine/droonga/databases/000/db.0000103",
"TABLE_PAT_KEY|PERSISTENT",
"ShortText",
null,
"TokenBigram",
"NormalizerAuto"
]
]
]
$ curl "$endpoint/d/column_list?table=Store"
[
[
0,
1416390069.515929,
0.001077413558959961
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"type",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"source",
"ShortText"
]
],
[
256,
"_key",
"",
"",
"COLUMN_SCALAR",
"Store",
"ShortText",
[]
],
[
258,
"location",
"/home/droonga-engine/droonga/databases/000/db.0000102",
"fix",
"COLUMN_SCALAR",
"Store",
"WGS84GeoPoint",
[]
],
[
257,
"name",
"/home/droonga-engine/droonga/databases/000/db.0000101",
"var",
"COLUMN_SCALAR",
"Store",
"ShortText",
[]
]
]
]
$ curl "$endpoint/d/column_list?table=Term"
[
[
0,
1416390110.143951,
0.0013172626495361328
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"type",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"source",
"ShortText"
]
],
[
259,
"_key",
"",
"",
"COLUMN_SCALAR",
"Term",
"ShortText",
[]
],
[
260,
"store_name",
"/home/droonga-engine/droonga/databases/000/db.0000104",
"index",
"COLUMN_INDEX|WITH_POSITION",
"Term",
"Store",
[
"name"
]
]
]
]
$ curl "$endpoint/d/column_list?table=Location"
[
[
0,
1416390163.0140722,
0.0009713172912597656
],
[
[
[
"id",
"UInt32"
],
[
"name",
"ShortText"
],
[
"path",
"ShortText"
],
[
"type",
"ShortText"
],
[
"flags",
"ShortText"
],
[
"domain",
"ShortText"
],
[
"range",
"ShortText"
],
[
"source",
"ShortText"
]
],
[
261,
"_key",
"",
"",
"COLUMN_SCALAR",
"Location",
"WGS84GeoPoint",
[]
],
[
262,
"store",
"/home/droonga-engine/droonga/databases/000/db.0000106",
"index",
"COLUMN_INDEX",
"Location",
"Store",
[
"location"
]
]
]
]
それでは、Store
テーブルにデータを読み込みましょう。
まず、データを stores.json
という名前のJSON形式のファイルとして用意します。
stores.json:
[
["_key","name","location"],
["store0","1st Avenue & 75th St. - New York NY (W)","40.770262,-73.954798"],
["store1","76th & Second - New York NY (W)","40.771056,-73.956757"],
["store2","2nd Ave. & 9th Street - New York NY","40.729445,-73.987471"],
["store3","15th & Third - New York NY (W)","40.733946,-73.9867"],
["store4","41st and Broadway - New York NY (W)","40.755111,-73.986225"],
["store5","84th & Third Ave - New York NY (W)","40.777485,-73.954979"],
["store6","150 E. 42nd Street - New York NY (W)","40.750784,-73.975582"],
["store7","West 43rd and Broadway - New York NY (W)","40.756197,-73.985624"],
["store8","Macy's 35th Street Balcony - New York NY","40.750703,-73.989787"],
["store9","Macy's 6th Floor - Herald Square - New York NY (W)","40.750703,-73.989787"],
["store10","Herald Square- Macy's - New York NY","40.750703,-73.989787"],
["store11","Macy's 5th Floor - Herald Square - New York NY (W)","40.750703,-73.989787"],
["store12","80th & York - New York NY (W)","40.772204,-73.949862"],
["store13","Columbus @ 67th - New York NY (W)","40.774009,-73.981472"],
["store14","45th & Broadway - New York NY (W)","40.75766,-73.985719"],
["store15","Marriott Marquis - Lobby - New York NY","40.759123,-73.984927"],
["store16","Second @ 81st - New York NY (W)","40.77466,-73.954447"],
["store17","52nd & Seventh - New York NY (W)","40.761829,-73.981141"],
["store18","1585 Broadway (47th) - New York NY (W)","40.759806,-73.985066"],
["store19","85th & First - New York NY (W)","40.776101,-73.949971"],
["store20","92nd & 3rd - New York NY (W)","40.782606,-73.951235"],
["store21","165 Broadway - 1 Liberty - New York NY (W)","40.709727,-74.011395"],
["store22","1656 Broadway - New York NY (W)","40.762434,-73.983364"],
["store23","54th & Broadway - New York NY (W)","40.764275,-73.982361"],
["store24","Limited Brands-NYC - New York NY","40.765219,-73.982025"],
["store25","19th & 8th - New York NY (W)","40.743218,-74.000605"],
["store26","60th & Broadway-II - New York NY (W)","40.769196,-73.982576"],
["store27","63rd & Broadway - New York NY (W)","40.771376,-73.982709"],
["store28","195 Broadway - New York NY (W)","40.710703,-74.009485"],
["store29","2 Broadway - New York NY (W)","40.704538,-74.01324"],
["store30","2 Columbus Ave. - New York NY (W)","40.769262,-73.984764"],
["store31","NY Plaza - New York NY (W)","40.702802,-74.012784"],
["store32","36th and Madison - New York NY (W)","40.748917,-73.982683"],
["store33","125th St. btwn Adam Clayton & FDB - New York NY","40.808952,-73.948229"],
["store34","70th & Broadway - New York NY (W)","40.777463,-73.982237"],
["store35","2138 Broadway - New York NY (W)","40.781078,-73.981167"],
["store36","118th & Frederick Douglas Blvd. - New York NY (W)","40.806176,-73.954109"],
["store37","42nd & Second - New York NY (W)","40.750069,-73.973393"],
["store38","Broadway @ 81st - New York NY (W)","40.784972,-73.978987"],
["store39","Fashion Inst of Technology - New York NY","40.746948,-73.994557"]
]
データが準備できたら、load
コマンドのPOSTリクエストとして送信します:
$ curl --data "@stores.json" "$endpoint/d/load?table=Store"
[
[
0,
1401358564.909,
0.158
],
[
40
]
]
これで、JSONファイル中のすべてのデータが正しく読み込まれます。
以上で、すべてのデータが準備できました。
試しに、select
コマンドを使って最初の10レコードを取り出してみましょう:
$ curl "$endpoint/d/select?table=Store&output_columns=name&limit=10"
[
[
0,
1401362059.7437818,
4.935264587402344e-05
],
[
[
[
40
],
[
[
"name",
"ShortText"
]
],
[
"1st Avenue & 75th St. - New York NY (W)"
],
[
"76th & Second - New York NY (W)"
],
[
"Herald Square- Macy's - New York NY"
],
[
"Macy's 5th Floor - Herald Square - New York NY (W)"
],
[
"80th & York - New York NY (W)"
],
[
"Columbus @ 67th - New York NY (W)"
],
[
"45th & Broadway - New York NY (W)"
],
[
"Marriott Marquis - Lobby - New York NY"
],
[
"Second @ 81st - New York NY (W)"
],
[
"52nd & Seventh - New York NY (W)"
]
]
]
]
もちろん、query
オプションを使って検索条件を指定する事もできます:
$ curl "$endpoint/d/select?table=Store&query=Columbus&match_columns=name&output_columns=name&limit=10"
[
[
0,
1398670157.661574,
0.0012705326080322266
],
[
[
[
2
],
[
[
"_key",
"ShortText"
]
],
[
"Columbus @ 67th - New York NY (W)"
],
[
"2 Columbus Ave. - New York NY (W)"
]
]
]
]
$ curl "$endpoint/d/select?table=Store&filter=name@'Ave'&output_columns=name&limit=10"
[
[
0,
1398670586.193325,
0.0003848075866699219
],
[
[
[
3
],
[
[
"_key",
"ShortText"
]
],
[
"2nd Ave. & 9th Street - New York NY"
],
[
"84th & Third Ave - New York NY (W)"
],
[
"2 Columbus Ave. - New York NY (W)"
]
]
]
]
このチュートリアルでは、Ubuntu LinuxまたはCentOSのコンピュータを使ってDroongaクラスタを構築しました。 また、Groongaサーバ互換のシステムとしてデータを読み込ませたり取り出したりすることにも成功しました。
現在の所、DroongaはGroonga互換のコマンドのうちいくつかの限定的な機能にのみ対応しています。 詳細はコマンドリファレンスを参照して下さい。
続いて、Droongaクラスタのデータをバックアップしたり復元したりする手順を学びましょう。