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

チュートリアルのゴール

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

前提条件

「replica」とは?

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

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

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

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

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

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

このケースでは、検索のように読み取りのみを行うリクエストに対しては、クラスタの動作を止める必要はありません。 サービスを停止することなく、その裏側でシームレスに新しいreplicaを追加することができます。

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

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

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

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

(on 192.168.0.12)
# apt-get update
# apt-get -y upgrade
# apt-get install -y ruby ruby-dev build-essential nodejs nodejs-legacy npm
# gem install droonga-engine
# npm install -g droonga-http-server

新しいノードには、クラスタの既存のノードから catalog.json をコピーする必要があります。

(on 192.168.0.12)
# mkdir ~/droonga
# scp 192.168.0.10:~/droonga/catalog.json ~/droonga/

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

(on 192.168.0.12)
# kill $(cat $PWD/droonga-engine.pid)
# rm -rf ~/droonga
# mkdir ~/droonga
# scp 192.168.0.10:~/droonga/catalog.json ~/droonga/

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

(on 192.168.0.12)
# cd ~/droonga
# host=192.168.0.12
# DROONGA_BASE_DIR=$PWD
# droonga-engine --host=$host \
                 --log-file=$DROONGA_BASE_DIR/droonga-engine.log \
                 --daemon \
                 --pid-file=$DROONGA_BASE_DIR/droonga-engine.pid
# env NODE_ENV=production \
    droonga-http-server --port=10041 \
                        --receive-host-name=$host \
                        --droonga-engine-host-name=$host \
                        --cache-size=-1 \
                        --daemon \
                        --pid-file=$DROONGA_BASE_DIR/droonga-http-server.pid

この時点では、ノードの情報が catalog.json に含まれていないため、この新しいノードはクラスタのノードとしては動作していません。 新しいノードにリクエストを送っても、それらはすべてクラスタ内の既存のノードに転送されます。

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

# curl "http://192.168.0.10:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}
# curl "http://192.168.0.11:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}
# curl "http://192.168.0.12:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}

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

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

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

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

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

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

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

(on 192.168.0.12)
# cd ~/droonga
# droonga-engine-join --host=192.168.0.12 \
                      --replica-source-host=192.168.0.10

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

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

# curl "http://192.168.0.10:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    },
    "192.168.0.12:10031/droonga": {
      "live": true
    }
  }
}

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

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

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

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

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

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

ここでは、192.168.0.10192.168.0.11、および 192.168.0.12 の3つのreplicaノードからなるDroongaクラスタが存在していて、最後のノード 192.168.0.12 をクラスタから取り除こうとしていると仮定します。

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

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

(on 192.168.0.10)
# cd ~/droonga
# droonga-engine-unjoin --host=192.168.0.12

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

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

# curl "http://192.168.0.10:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}
# curl "http://192.168.0.11:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}
# curl "http://192.168.0.12:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}

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

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

192.168.0.10192.168.0.11 の2つのノードからなるDroongaクラスタがあり、ノード 192.168.0.11 の動作が不安定になっていて、これを新しいノード 192.168.0.12 で置き換えようとしていると仮定します。

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

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

(on 192.168.0.10)
# cd ~/droonga
# droonga-engine-unjoin --host=192.168.0.11

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

# curl "http://192.168.0.10:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    }
  }
}

新しいreplicaを追加する

次に、新しいreplicaを用意します。 必要なパッケージをインストールし、クラスタの既存のノードから catalog.json をコピーして、サーバを起動します。

(on 192.168.0.12)
# cd ~/droonga
# scp 192.168.0.10:~/droonga/catalog.json ./
# host=192.168.0.12
# DROONGA_BASE_DIR=$PWD
# droonga-engine --host=$host \
                 --log-file=$DROONGA_BASE_DIR/droonga-engine.log \
                 --daemon \
                 --pid-file=$DROONGA_BASE_DIR/droonga-engine.pid
# env NODE_ENV=production \
    droonga-http-server --port=10041 \
                        --receive-host-name=$host \
                        --droonga-engine-host-name=$host \
                        --cache-size=-1 \
                        --daemon \
                        --pid-file=$DROONGA_BASE_DIR/droonga-http-server.pid

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

(on 192.168.0.12)
# cd ~/droonga
# droonga-engine-join --host=192.168.0.12 \
                      --replica-source-host=192.168.0.10

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

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

# curl "http://192.168.0.10:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.12:10031/droonga": {
      "live": true
    }
  }
}
# curl "http://192.168.0.12:10041/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.12:10031/droonga": {
      "live": true
    }
  }
}

まとめ

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