Droongaチュートリアル: 既存クラスタへのreplicaの追加

チュートリアルのゴール

既存のDroongaクラスタについて、新しいreplicaを追加し、既存のreplicaを削除し、および、既存のreplicaを新しいreplicaで置き換えるための手順を学ぶこと。

前提条件

このチュートリアルでは、最初のチュートリアルで準備した2つの既存のDroongaノード:node0 (192.168.100.50) 、 node1 (192.168.100.51) と、新しいノードとして使うもう1台のコンピュータ node2 (192.168.100.52) があると仮定します。 あなたの手元にあるDroongaノードがこれとは異なる名前である場合には、以下の説明の中のnode0node1node2は実際の物に読み替えて下さい。

「replica」とは?

Droongaのノードの集合には、「replica」と「slice」という2つの軸があります。

「replica」のノード群は、完全に同一のデータを持っており、検索などのリクエストを各ノードが並行して処理する事ができます。 新しいreplicaを追加する事によって、増加するリクエストに対して処理能力を増強することができます。

他方、「slice」のノード群はそれぞれ異なるデータを持ちます(例えば、あるノードは2013年のデータ、別のノードは2014年のデータ、という具合です)。 新しいsliceを追加する事によって、増大するデータ量に対してクラスタとしての容量を拡大することができます。

現在の所、Groonga互換のシステムとして設定されたDroongaクラスタについては、replicaを追加することはできますが、sliceを追加することはできません。 この点については将来のバージョンで改善する予定です。

ともかく、このチュートリアルでは既存のDroongaクラスタに新しいreplicaを追加する手順を解説します。 早速始めましょう。

既存のクラスタに新しいreplicaノードを追加する

クラスタへのreplicaノードの「Hot-Add」(サービスのダウンタイム無しでクラスタ構成を動的に変更すること。この場合はreplicaの動的な追加)を行うためには、クラスタ内に2つ以上の既存replicaが存在している必要があります。 この操作の進行中は、既存のreplicaのうち1つが新しいreplicaに対する「データのコピー元」となり、それ以外のノードがサービスを提供することになります。

クラスタ内に既存のreplicaが1つしかない場合には、cronjobによるバッチ処理、クローラなどのデータベースに対する変更操作を、操作が完了するまでの間全て停止しておく必要があります。 でないと、追加したreplicaと既存のreplicaの内容に不整合が発生します。 以下は、データベースに変更を加える代表的な組み込みのコマンドの一覧です:

ただし、既存のデータに一切変更を加えない種類のメッセージ(searchsytem.statusなど)については、この操作の間も利用できます。 端的に言うと、既存のreplicaが1つだけしかない場合は、新しいreplicaを追加する操作の間はクローリングを停止する必要がある、という事ですね。

ここでは、node0node1 の2つのreplicaノードからなるDroongaクラスタがあり、新しいreplicaノードとして node2 を追加すると仮定します。

新しいノードをセットアップする

まず、新しいコンピュータをセットアップし、必要なソフトウェアのインストールと設定を済ませます。

(on node2)
# curl https://raw.githubusercontent.com/droonga/droonga-engine/master/install.sh | \
    HOST=node2 bash
# curl https://raw.githubusercontent.com/droonga/droonga-http-server/master/install.sh | \
    ENGINE_HOST=node2 HOST=node2 bash

注意点として、空でないノードを既存のクラスタに追加することはできません。 もしそのコンピュータがかつてDroongaノードとして使われていた事があった場合には、最初に古いデータを消去する必要があります。

(on node2)
# droonga-engine-configure --quiet \
                           --clear --reset-config --reset-catalog \
                           --host=node2
# droonga-http-server-configure --quiet --reset-config \
                                --droonga-engine-host-name=node2 \
                                --receive-host-name=node2

では、サービスを起動しましょう。

(on node2)
# service droonga-engine start
# service droonga-http-server start

この時点で、この新しいノードは既存のクラスタのノードとしては動作していません。 この事は、system.statusコマンドを通じて確認できます:

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

継続的に流入するメッセージの準備

前の項目に引き続いてこのチュートリアルを順番に読み進めている場合、クラスタに対してはデータの流入がまだ無い状態になっているはずです。 Hot-Addを試してみるために、以下のようにして、継続的に新しいレコードを追加する仮想的なデータソースを準備しましょう:

(on node0)
$ count=0; maxcount=500; \
  while [ "$count" -lt "$maxcount" ]; \
  do \
    droonga-add --host node0 --table Store --key "dummy-store$count" --name "Dummy Store $count"; \
    count=$(($count + 1)); \
    sleep 1; \
  done

これは、合計で500件のレコードを1秒ごとに1レコードずつ追加する例です。 droonga-addはDroongaが提供しているコマンドラインユーティリティですが、今のところは詳細について理解していなくてもも大丈夫です。

新しいreplicaをクラスタに参加させる

既存のクラスタに新しいreplicaを追加するには、いずれかの既存のreplicaまたは新しく追加するreplicaの上で、droonga-engine-joinというコマンドを以下のようにして実行します:

(on node2)
$ droonga-engine-join --host=node2 \
                      --replica-source-host=node0 \
                      --receiver-host=node2
Start to join a new node node2
       to the cluster of node0
                     via node2 (this host)
    port    = 10031
    tag     = droonga
    dataset = Default

Source Cluster ID: 8951f1b01583c1ffeb12ed5f4093210d28955988

Changing role of the joining node...
Configuring the joining node as a new replica for the cluster...
Registering new node to existing nodes...
Changing role of the source node...
Getting the timestamp of the last processed message in the source node...
The timestamp of the last processed message at the source node: xxxx-xx-xxTxx:xx:xx.xxxxxxZ
Setting new node to ignore messages older than the timestamp...
Copying data from the source node...
100% done (maybe 00:00:00 remaining)
Restoring role of the source node...
Restoring role of the joining node...
Done.

このコマンドは、以下のようにして別のノード上で実行することもできます:

(on node1)
$ droonga-engine-join --host=node2 \
                      --replica-source-host=node0 \
                      --receiver-host=node1
Start to join a new node node2
       to the cluster of node0
                     via node1 (this host)
...

すると、このコマンドは自動的にクラスタ内のデータを新しいreplicaに同期し始めます。 全てのデータを無事に同期し終えたら、新しいノードはクラスタのreplicaとしてそのまま働き始めます。

以上の操作で、新しいreplicaノードがDroongaクラスタへ無事に追加されました。 system.statusコマンドを使って、これらのノードがクラスタとして動作していることを確かめましょう:

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

新しいノードnode2がクラスタに参加したため、各ノードのdroonga-http-serverは自動的に、メッセージをnode2にも分配するようになります。

全てのreplicaが同等になっているかどうかは、system.statistics.object.count.per-volumeを使って以下のように確かめられます:

(on node0)
$ curl "http://node0:10041/droonga/system/statistics/object/count/per-volume?output\[\]=total" | jq "."
{
  "node0:10031/droonga.000": {
    "total": 540
  },
  "node1:10031/droonga.000": {
    "total": 540
  },
  "node2:10031/droonga.000": {
    "total": 540
  }
}

出力されている数値は、各ノードのデータベースに何個のオブジェクトが保持されているかを示しています。 全ての値が同じなので、各レプリカはお互いに同等の状態になっているということが分かります。

既存のクラスタからreplicaノードを削除する

Droongaノードは、メモリ不足、ディスク容量不足、ハードウェア障害など、様々な致命的な理由によって動作しなくなり得ます。 Droongaクラスタ内のノードは互いに監視しあっており、動作しなくなったノードに対してはメッセージの配送を自動的に停止して、動作しないノードがあってもクラスタ全体としては動作し続けるようになっています。 このような時には、動作していないノードを取り除く必要があります。

もちろん、他の目的に転用したいといった理由から、正常動作中のノードを取り除きたいと考える場合もあるでしょう。

ここでは、node0node1node2 の3つのreplicaノードからなるDroongaクラスタがあり、最後のノード node2 をクラスタから離脱させようとしていると仮定します。

既存のreplicaをクラスタから分離する

replicaノードを既存のクラスタから削除するには、クラスタ内のいずれかのノードの上で、以下のようにしてdroonga-engine-unjoinコマンドを実行します:

(on node0)
$ droonga-engine-unjoin --host=node2 \
                        --receiver-host=node0
Start to unjoin a node node2:10031/droonga
                    by node0 (this host)

Unjoining replica from the cluster...
Done.

すると、指定されたノードがクラスタから自動的に離脱します。

これで、ノードのクラスタからの切り離しは無事に完了しました。 node2が本当にクラスタから離脱しているかどうかは、`system.status コマンドを使って以下のように確かめられます:

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

ノードnode2はもはやクラスタの一員ではないため、node0node1droonga-http-servernode2droonga-engineへはもうメッセージを送りません。 またその一方で、node2droonga-http-serverはそのノード上のdroonga-engineにのみ関連付けられており、他のノードへはメッセージを送りません。

クラスタ内の既存のreplicaノードを新しいreplicaノードで置き換える

ノードの置き換えは、上記の手順の組み合わせで行います。

ここでは、node0node1 の2つのreplicaノードからなるDroongaクラスタがあり、node1 が不安定で、それを新しいreplicaノード node2 で置き換えようとしていると仮定します。

既存のreplicaをクラスタから分離する

まず、不安定になっているノードを取り除きます。以下のようにしてクラスタからノードを離脱させて下さい:

(on node0)
$ droonga-engine-unjoin --host=node1

これで、ノードがクラスタから離脱しました。この事は system.status コマンドで確かめられます:

$ curl "http://node0:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "status": "active"
    }
  },
  "reporter": "..."
}

新しいreplicaを追加する

次に、新しいreplica node2を用意します。 必要なパッケージをインストールし、catalog.jsonを生成して、サービスを起動します。

(on node2)
# curl https://raw.githubusercontent.com/droonga/droonga-engine/master/install.sh | \
    HOST=node2 bash
# curl https://raw.githubusercontent.com/droonga/droonga-http-server/master/install.sh | \
    ENGINE_HOST=node2 HOST=node2 bash

そのコンピュータがかつてDroongaノードの一員だったことがある場合は、インストール作業の代わりに、古いデータを消去する必要があります:

(on node2)
# droonga-engine-configure --quiet \
                           --clear --reset-config --reset-catalog \
                           --host=node2
# droonga-http-server-configure --quiet --reset-config \
                                --droonga-engine-host-name=node2 \
                                --receive-host-name=node2

そうしたら、そのノードをクラスタに参加させましょう。

(on node2)
$ droonga-engine-join --host=node2 \
                      --replica-source-host=node0

最終的に、node0node2 の2つのノードからなるDroongaクラスタができあがりました。

この事は、system.status コマンドの結果を見ると確認できます:

$ curl "http://node0:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "status": "active"
    },
    "node2:10031/droonga": {
      "status": "active"
    }
  },
  "reporter": "..."
}

まとめ

このチュートリアルでは、既存のDroongaクラスタに新しいreplicaノードを追加する方法を学びました。 また、既存のreplicaを取り除く方法と、既存のreplicaを新しいreplicaで置き換える方法も学びました。