今回はノードの追加と削除をやってみます。
トークンの算出
ノードを追加するケースはリングの負荷状態によって、大きく分けて
- 保存するデータのハッシュやアクセスに偏りが大きく、特定ノードの容量または処理の負荷が高いので負荷集中を分散したい。
- リング全体の容量、または処理の負荷が高いので、全体的に数を増やして均一に分散したい。
の2通りかと思います。
1の場合、負荷の高いノードの近くにノードを追加し、ノードの均一化はせずに特定のトークンの範囲にノードを集中させたほうがよさそうです。
2の場合、ノードを追加したら、全ノードが均等にリング上に再配置したほうが全体としての負荷は抑えられそうです。
今回は1の場合を例にとってみます。
まず、トークンを計算するためのツールをつくっておくと便利です。
引数にノードの数を与えるとそれがリング上に均等に配置されるようなトークンのリストを返すツールです。
計算式はPartitionerによってことなり、
#RandomPartitionerの場合 def tokens(nodes): for x in xrange(nodes): print 2 ** 127 / nodes * x #Murmur3Partitionerの場合 def tokens(num_nodes): for i in range(num_nodes): print ((2**64 / num_nodes) * i) - 2**63
のようになります。
今回はMurmur3Partitionerを使用しているので、
以下のようなpythonスクリプトを作ります。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import sys | |
argvs = sys.argv | |
tokenc = int(argvs[1]) | |
def tokens(num_nodes): | |
for i in range(num_nodes): | |
print ((2**64 / num_nodes) * i) - 2**63 | |
tokens(tokenc) |
引数にノード数を入れて実行すると、ノードリングに均等に配置された場合のハッシュトークンのリストが出力されます。
$ ./tokens.py 4 -9223372036854775808 -4611686018427387904 0 4611686018427387904 $ ./tokens.py 8 -9223372036854775808 -6917529027641081856 -4611686018427387904 -2305843009213693952 0 2305843009213693952 4611686018427387904 6917529027641081856
既存のノードが4つだったとして、トークンが以下のように配置されているとします。
- A:0
- B:4611686018427387904
- C:-9223372036854775808
- D:-4611686018427387904
この場合Aの両脇にノードを2つ追加しておくとよさそうです。
また、追加ノードのトークンは、
- B、DもCに比べて負荷が高めであればA-B, A-Cのそれぞれ中間
- BDの負荷はそれほどでもなければもっとAより
に配置する形になります。
前者であれば、./tokens 8、後者であれば./tokens 12 などでAの位置の両脇のトークンを使用します。
たとえば後者のパターンで追加してみます。
$ ./tokens 12 -9223372036854775808 -7686143364045646507 -6148914691236517206 -4611686018427387905 -3074457345618258604 -1537228672809129303 -2 1537228672809129299 3074457345618258600 4611686018427387901 6148914691236517202 7686143364045646503
-2の部分がAのノード位置です。
その両脇の
- -1537228672809129303
- 1537228672809129299
が今回の追加ノードとなります。
以前の記事でちょっと触れましたが、自動でリングに追加されるようなAMIを作ります。
AMIの作成
AMIの作成をします。cassandraをインストールしただけでまだ一度も起動していないインスタンスをベースに作業を行うとスムーズです。
以下の設定ファイルにはIPなどノード固有の情報(赤字の部分)が含まれます。
/usr/local/cassandra/conf/cassandra.yaml
cluster_name: 'クラスタ名' initial_token: トークン seed_provider: - class_name: org.apache.cassandra.locator.SimpleSeedProvider parameters: - seeds: "シードIP" listen_address: 自分のIP endpoint_snitch: Ec2Snitch auto_bootstrap: シードならfalse,非シードならtrue
/etc/hosts
127.0.0.1 localhost.localdomain localhost ip-10-0-1-10
この固有の項目を書き換えるようなシェルスクリプトを書きます。
このスクリプトは後でUserDataから呼び出され、第1引数にトークン、第2引数にauto_bootstrapのブーリアン文字列、第3引数にシードIPが渡されます。
/etc/cloud/cassandra.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sed -i "1s/ip-.*//g" /etc/hosts | |
sed -i "1s/$/ $(hostname)/g" /etc/hosts | |
sed -i "s/^listen_address:.*$/listen_address: $(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)/" /usr/local/cassandra/conf/cassandra.yaml | |
sed -i "s/^initial_token:.*$/initial_token: $1/" /usr/local/cassandra/conf/cassandra.yaml | |
sed -i "s/^auto_bootstrap:.*$/auto_bootstrap: $2/" /usr/local/cassandra/conf/cassandra.yaml | |
sed -i "s/^ - seeds:.*/ - seeds: \"$3\"/" /usr/local/cassandra/conf/cassandra.yaml | |
/etc/init.d/cassandra start |
ここまで設定したらno rebootでAMIを作成します。
ノードの自動追加
このAMIをつかって新規ノードを追加します。
クラスタも何もない状態で、一番最初のノードを立ち上げる場合は以下のようにUserDataを指定します。
#!/bin/sh /etc/cloud/cassandra.sh "0" false 10.0.1.10
そして、プライベートIPアドレスを10.0.1.10に固定して起動します。
また、2個め以降(ここでは例として6個目)の追加ノードは以下のようにUserDataを指定します。
ここで、第1引数のトークンは冒頭で記した計算ツールで算出しておきます。
#!/bin/sh /etc/cloud/cassandra.sh "1537228672809129299" true 10.0.1.10,10.0.1.146,10.0.1.176,10.0.1.232,10.0.1.134
特にプライベートIPを指定する必要はありません。
起動前は、
# /usr/local/cassandra/bin/nodetool ring Datacenter: ap-northeast ========== Replicas: 3 Address Rack Status State Load Owns Token -4611686018427387905 10.0.1.146 1a Up Normal 75.43 KB 66.67% -1537228672809129303 10.0.1.10 1a Up Normal 61.65 KB 50.00% 0 10.0.1.176 1a Up Normal 58.33 KB 58.33% -9223372036854775808 10.0.1.232 1a Up Normal 58.45 KB 50.00% 4611686018427387901 10.0.1.134 1a Up Normal 58.48 KB 75.00% -4611686018427387905
だったのが、起動後
# /usr/local/cassandra/bin/nodetool ring Datacenter: ap-northeast ========== Replicas: 3 Address Rack Status State Load Owns Token -4611686018427387905 10.0.1.213 1a Up Normal 49.55 KB 33.33% 1537228672809129299 10.0.1.146 1a Up Normal 63.7 KB 66.67% -1537228672809129303 10.0.1.10 1a Up Normal 66.59 KB 50.00% 0 10.0.1.176 1a Up Normal 63.28 KB 50.00% -9223372036854775808 10.0.1.232 1a Up Normal 63.4 KB 33.33% 4611686018427387901 10.0.1.134 1a Up Normal 63.43 KB 66.67% -4611686018427387905
↑のようにノード追加されていることが分かります。
ノードの取り外し
逆に問題があるノードを取り外すときは、そのノードに入って、
# /usr/local/cassandra/bin/nodetool decommission
とすれば外れます。
再び接続させるには、cassandraを追加します。
#/etc/init.d/cassandra stop #/etc/init.d/cassandra start
接続できない場合
たまに、リングに接続できなかったりした場合は、まず7000(ノード間), 9160(Thrift), 7199(JMX)などのポートを準備できているかセキュリティグループやnsetstatで確認します。netstatでlistenできていない場合は、cassandraを再起動させたりログを調べたりして原因を探していく流れになります。
# netstat -tupln | grep -i listen | grep java tcp 0 0 0.0.0.0:9160 0.0.0.0:* LISTEN 3967/java tcp 0 0 0.0.0.0:38250 0.0.0.0:* LISTEN 3967/java tcp 0 0 0.0.0.0:53170 0.0.0.0:* LISTEN 3967/java tcp 0 0 10.0.1.213:7000 0.0.0.0:* LISTEN 3967/java tcp 0 0 0.0.0.0:7199 0.0.0.0:* LISTEN 3967/java
これで、ノードを簡単に増やせるようになりました。
以上です。