Droongaチュートリアル: 使ってみる/Groongaからの移行手順

チュートリアルのゴール

Droongaクラスタを自分で構築して、Groonga互換のサーバとして利用できるようにするための手順を学ぶこと。

前提条件

Droongaとは何か?

Droongaは分散アーキテクチャに基づくデータ処理エンジンで、「distributed-Groonga」がその名の由来です。 名前が示す通り、Droongaはいくつかの点での改善(具体的には、レプリケーションとシャーディング)を含んだGroonga互換のサーバとして動作することができます。

アーキテクチャ、設計、APIなどの点で、DroongaはGroongaと大きく異なっています。 しかしながら、Droongaを単にGroonga互換のサーバとして使う限りにおいては、Droongaのアーキテクチャ全体を理解する必要はありません。

例として、ニューヨークにあるスターバックスの店舗を検索できるデータベースシステムを作成することにします。

Droongaクラスタをセットアップする

Droongaベースのデータベースシステムは、Droongaクラスタと呼ばれます。 この節では、Droongaクラスタを0から構築する方法を解説します。

Droongaノード用のコンピュータを用意する

Droongaクラスタは、Droongaノードと呼ばれる1つ以上のコンピュータによって構成されます。 まず、Droongaノードにするためのコンピュータを用意しましょう。

このチュートリアルは、既存のコンピュータを使ってDroongaクラスタを構築する手順について解説しています。 以下の説明は基本的には、DigitalOcean上のサーバでUbuntu 14.04 x64またはCentOS 7 x64の仮想マシンが正しく準備されており、コンソールが利用できる状態になっている、という前提に基づいています。

単にDroongaを試したいだけの場合は、自分のコンピュータ上に複数台の仮想マシンを用意する手順の解説も参照してみて下さい。

注意:

有効なレプリケーションを実現するためには2台以上のコンピュータを用意する必要があります。 ですので、このチュートリアルでは以下のような2台のコンピュータがあると仮定して説明を進めます:

コンピュータをDroongaノードとしてセットアップする

Groongaはバイナリのパッケージを提供しているため、環境によっては簡単にインストールできます。 (Groongaのインストール手順を参照)

それに対し、コンピュータをDroongaノードとしてセットアップする手順は以下の通りです:

  1. droonga-engineをインストールする。
  2. droonga-http-serverをインストールする。
  3. そのノードを他のノードと協調して動作するように設定する。

上記の手順を各コンピュータに対して実施する必要があることに注意して下さい。 しかしながら、各手順は非常に簡単です。

それでは、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
...

この操作により、コンピュータ node0192.168.100.50 というホスト名のDroongaノード、コンピュータ node1192.168.100.51 というホスト名のDroongaノードとして設定されます。 前述した通り、ここで設定された名前は様々な場面で使われますので、各ノードの名前が何であるかを忘れないようにして下さい

このチュートリアルでは、各コンピュータはお互いのホスト名node0node1を正しく名前解決できるものと仮定します。 あなたの環境ではホスト名の解決ができないという場合には、以下の説明の中のnode0node1は、実際のIPアドレス(例えば192.168.100.50192.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ノードの上でのサービスの開始と停止

GroongaをHTTPサーバとして使う場合は、以下のように -d オプションを指定するだけでサーバを起動できます:

# groonga -p 10041 -d --protocol http /tmp/databases/db

一方、DroongaクラスタをHTTP経由で使うためには、各Droongaノードにおいて複数のサーバ・デーモンを起動する必要があります。

Droongaノードをインストールスクリプトを使ってセットアップした場合、デーモンは既に、serviceコマンドによって管理されるシステムのサービスとして設定されています。 サービスを起動するには、以下のようなコマンドを各Droongaノードで実行して下さい:

# service droonga-engine start
# service droonga-http-server start

これらのコマンドにより、各サービスが動作し始めます。 これで、2つのノードは1つのクラスタを形成し、お互いの状態を監視し合う状態になりました。 もしノードが1つ停止しても、他のノードが生存していれば、それらの生存ノードだけでDroongaクラスタは動作し続けます。 ですので、秘密裏のうちに機能停止したノードを復旧したりクラスタに復帰させたりすることができます。

クラスタが動作している事を、system.status コマンドを使って確認してみましょう。 コマンドはHTTP経由で実行できます:

$ curl "http://node0:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}

この結果は、2つのノードが正常に動作している事を示しています。 Droongaはクラスタで動作するので、他のエンドポイントも同じ結果を返します。

$ curl "http://node1:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}

サービスを停止するには、以下のコマンドを各Droongaノード上で実行します:

# service droonga-engine stop
# service droonga-http-server stop

確認が終わったら、再度サービスを起動しておきましょう:

テーブル、カラム、インデックスの作成

以上の手順で、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" | jq "."
[
  [
    0,
    1401358896.360356,
    0.0035653114318847656
  ],
  true
]

リクエストの送信先として、Droongaノード中でdroonga-http-serverが動作しているDroongaノードのどれか1つを指定する必要がある事に注意して下さい。 言い換えると、接続先(エンドポイント)としてはクラスタ中のどのノードでも好きな物を使う事ができます。 すべてのリクエストは、クラスタ中の適切なノードに配送されます。

次は、column_create コマンドを使って Store テーブルに namelocation という新しいカラムを作ります:

$ curl "$endpoint/d/column_create?table=Store&name=name&flags=COLUMN_SCALAR&type=ShortText" | jq "."
[
  [
    0,
    1401358348.6541538,
    0.0004096031188964844
  ],
  true
]
$ curl "$endpoint/d/column_create?table=Store&name=location&flags=COLUMN_SCALAR&type=WGS84GeoPoint" | jq "."
[
  [
    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" | jq "."
[
  [
    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" | jq "."
[
  [
    0,
    1401358494.1656318,
    0.006799221038818359
  ],
  true
]
$ curl "$endpoint/d/table_create?name=Location&flags=TABLE_PAT_KEY&key_type=WGS84GeoPoint" | jq "."
[
  [
    0,
    1401358505.708896,
    0.0016951560974121094
  ],
  true
]
$ curl "$endpoint/d/column_create?table=Location&name=store&flags=COLUMN_INDEX&type=Store&source=location" | jq "."
[
  [
    0,
    1401358519.6187897,
    0.024788379669189453
  ],
  true
]

注意: テーブルが完全にできあがるまでは、table_listcolumn_list といったコマンドを実行しないでください。テーブルができあがる前にこれらのコマンドを実行してしまうと、インデックスが破損した状態になってしまいます。これはバージョン1.0.6での既知の不具合で、将来のバージョンで修正される予定です。

さて、テーブルを正しく作成できました。 table_list コマンドを使って、作成されたテーブルの情報を見てみましょう:

$ curl "$endpoint/d/table_list" | jq "."
[
  [
    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" | jq "."
[
  [
    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
    ]
  ]
]

テーブルへのデータの読み込み

それでは、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" | jq "."
[
  [
    0,
    1401358564.909,
    0.158
  ],
  [
    40
  ]
]

これで、JSONファイル中のすべてのデータが正しく読み込まれます。

テーブル中のデータを取り出す

以上で、すべてのデータが準備できました。

試しに、select コマンドを使って最初の10レコードを取り出してみましょう:

$ curl "$endpoint/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    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" | jq "."
[
  [
    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" | jq "."
[
  [
    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クラスタのデータをバックアップしたり復元したりする手順を学びましょう。