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を追加することができます。

その一方で、クラスタへの新しいデータの流入は、新しいノードが動作を始めるまでの間停止しておく必要があります。 (将来的には、新しいノードを完全に無停止で追加できるようにする予定ですが、今のところはそれはできません。)

ここでは、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 start droonga-engine
# service start droonga-http-server

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

$ curl "http://node0:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}
$ curl "http://node1:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}
$ curl "http://node2:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node2:10031/droonga": {
      "live": true
    }
  }
}

書き込みを伴うリクエストの流入を一時的に停止する

新しいreplicaとの間でデータを完全に同期する必要があるので、クラスタの構成を変更する前に、クラスタへのデータの書き込みを行うリクエストの流入を一時停止する必要があります。 そうしないと、新しく追加したreplicaが中途半端なデータしか持たない矛盾した状態となってしまい、リクエストに対してクラスタが返す処理結果が不安定になります。

データの書き込みを伴うリクエストとは、具体的には、クラスタ内のデータを変更する以下のコマンドです:

cronjobとして実行されるバッチスクリプトによって load コマンド経由で新しいデータを投入している場合は、cronjobを停止して下さい。 クローラが add コマンド経由で新しいデータを投入している場合は、クローラを停止して下さい。 あるいは、クローラやローダーとクラスタの間にFluentdを置いてバッファとして利用しているのであれば、バッファからのメッセージ出力を停止して下さい。

前項から順番にチュートリアルを読んでいる場合、クラスタに流入しているリクエストはありませんので、ここでは特に何もする必要はありません。

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

新しいreplicaノードを既存のクラスタに追加するには、いずれかの既存のノードもしくは新しいreplicaノードのいずれかにおいて、catalog.json が置かれているディレクトリで、droonga-engine-join コマンドを実行します:

(on node2)
$ droonga-engine-join --host=node2 \
                      --replica-source-host=node0
Joining new replica to the cluster...
...
Update existing hosts in the cluster...
...
Done.

コマンドを実行すると、自動的に、クラスタのデータが新しいreplicaノードへと同期され始めます。 データの同期が完了すると、ノードが自動的に再起動してクラスタに参加します。 すべてのノードのcatalog.jsonも同時に更新され、この時点をもって、新しいノードは晴れてそのクラスタのreplicaノードとして動作し始めます。

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

$ curl "http://node0:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    },
    "node2:10031/droonga": {
      "live": true
    }
  }
}

古いレスポンスキャッシュを無視するために、各リクエストに追加の一意なパラメータを加えていることに注意して下さい。

書き込みを伴うリクエストの流入を再開する

さて、準備ができました。 すべてのreplicaは完全に同期した状態となっているので、このクラスタはリクエストを安定して処理できます。 cronjobを有効化する、クローラの動作を再開する、バッファからのメッセージ送出を再開する、などの操作を行って、クラスタ内のデータを変更するリクエストの流入を再開して下さい。

以上で、Droongaクラスタに新しいreplicaノードを無事参加させる事ができました。

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

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

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

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

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

新しいreplicaノードを既存のクラスタから削除するには、クラスタ内のいずれかのノードの上で、catalog.json が置かれたディレクトリにおいて droonga-engine-unjoin コマンドを実行します:

(on node0)
$ droonga-engine-unjoin --host=node2
Unjoining replica from the cluster...
...
Done.

すると、ノードがクラスタから自動的に離脱し、すべてのノードの catalog.json も同時に更新されます。 これで、ノードはクラスタから無事離脱しました。

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

$ curl "http://node0:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}
$ curl "http://node1:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}
$ curl "http://node2:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node1:10031/droonga": {
      "live": true
    }
  }
}

古いレスポンスキャッシュを無視するために、各リクエストに追加の一意なパラメータを加えていることに注意して下さい。

node2 までもが、node2 がクラスタの一員ではないと報告していることに注目して下さい。 これは、クラスタから離脱したノードと新しいノードとの違いです。

クラスタ内の既存の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?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    }
  }
}

古いレスポンスキャッシュを無視するために、各リクエストに追加の一意なパラメータを加えていることに注意して下さい。

新しい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?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node2:10031/droonga": {
      "live": true
    }
  }
}
$ curl "http://node2:10041/droonga/system/status?_=$(date +%s)" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "live": true
    },
    "node2:10031/droonga": {
      "live": true
    }
  }
}

古いレスポンスキャッシュを無視するために、各リクエストに追加の一意なパラメータを加えていることに注意して下さい。

まとめ

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