2014年4月24日木曜日

cassandra リングから外れたノードの再追加方法


cassandraプロセスがダウンした場合などに
その原因は取り除いたとし、
どのように再度リングに追加させればよいか、
その手順を検討する。

下記内容を理解していることを前提とする。
cassandraの運用時に必要な定期バッチ処理の考察



 条件
停止していた時間によって対応方法が変わる。

プロセスがダウンしてからgc_grace_seconds時間を経過していない
また、その時間未満であっても起動させた後に網羅的な修復(復帰後の必須の作業)を行い
それが完了するまでにgc_grace_secondsを超える可能性がない

⇒ YES
⇒ NO



 条件がYESの場合
1. プロセス起動
普通に起動させればよい。
$ /etc/init.d/cassandra start


2. 状態確認
リングに追加されるまでは少し時間がかかる。
$ nodetool ring


3. 手動での網羅的な修復
網羅的なデータの修復には2通り手段がある。
・ Anti Entropy
・ 全データへのアクセスによるRead Repair

2重起動を避けるため、網羅的なデータ修復用のcronを止めて
手動でデータの修復を実行した方がいいだろう。


以上である。


次に条件がNOの場合だが、その対応方法を検討する前に
ブートストラップ時の動きを理解しておく必要がある。
さらに、ブートストラップの前に、seedノードの役割も知っておいた方がいいだろう。



seedノードの役割
seedノードとは、リングの最新情報が集まるノードである。
役割は大きく2つある。

・新規ノードのブートストラップ時にseedノードが参照されリング内の他ノードの情報の学習

・gossip(ローカルノードが持っているリング情報をgossip digestとして交換)のハブとして動作し、
リング情報の伝搬効率化
※複数台設定指定できるため、複数のseedを構成すべきである。
gossipプロトコルは設定したseedノードにランダムに送出される。
seedがいないくともリングは機能するが、ノード状態の変化の検出に時間がかかる。



ブートストラップ(boot strap)時の動作
ブートストラップとはクラスタに未参加のノードを新規に起動して、
クラスタに参加させる処理である。

例えば、5台のノードでリングを組み、3台でレプリケーションをとる構成をとっているとする。
それぞれのノードは0から2つずつインクリメントされるトークン値が
割り当てられていると仮定する。

・ 正常時
例えば、データのキーが下のトークンの間にある書き込みは、
token 0 < key <= token 2

2のトークンを担当するノードと、
そこから順繰りにレプリケーション台数分のノードが受け持つ。

node  token
○     0
○     2    ← data
○     4    ← data
○     6    ← data
○     8

node記号
○:正常稼働中
△:ブートストラップ中
×:障害発生中


・ 故障発生時
一台のノードがダウンしたとする。

node  token
○     0
○     2     data
×
○     6     data
○     8

ノードへの障害が発生している間に書き込みが行われる場合は、
Hinted Handoffの機能が働く。


・ ブートストラップ中
原因を取り除き障害発生ノードを起動させる。
リングに追加されるまでのブートストラップ中は
6のトークンを持ったノードが4のトークン分もデータを受け持ち、
書き込みと参照が行わる。
レプリケーション数は3なのでtokenが8のノードにもデータが書き込まれる。
この動作はブートストラップ中だけである。
故障中はHinted Handoffの機能が働く。

node  token
○     0 
○     2         ← data

○     6 + 4     ← data
○     8         ← data


・ 復帰
復帰したノードは当然他レプリケーションノードとデータの差分が存在する。
それは各種修復機能によって修復されていく。

復旧後は自動でのHinted Handoff、Read Repairではなく、
即、手動によるAnti Entropy、または網羅的なRead Repairによる
修復ツールを走らせた方がいい。
データを持たない状態でseednodeに設定されていないノードを組み込むと
レプリカ以外の全データを取得するためである。

node  token
○     0 
○     2         data
○     4         data 修復
○     6         data 
○     8         data 残留

また、これらの修復機能では戻らないデータが残る。
トークン8のノードはブートストラップ中に書かれたデータが
保持されたままになる(この後に述べるcleanup処理を実施する理由がこれである)。
ブートストラップ後はトークン8のノードにはデータの書き込みと参照は発生しないが、
古いデータが残ることで、ノードに余計な負荷がかかる。
また、この状態で新たなブートストラップを行うと、
新規ノードの自動トークン選択にも影響が出る可能性がある。



それでは条件がNOの場合を考える。
この条件での手順はデータが何かしらの理由で破損等起きた場合にも使える。

 条件がNOの場合
1. データディレクトリの削除
gc_grace_secondsを超えているのでデータはすべて削除する。
$ rm -rf /var/lib/cassandra/commitlog/
$ rm -rf /var/lib/cassandra/data/
$ rm -rf /var/lib/cassandra/saved_caches/
パスは設定に合わせて読み替えること。


2. プロセス起動
$ /etc/init.d/cassandra start


3. 状態確認
リングに追加されるまでは少し時間がかかる。
$ nodetool ring


4. cleanup処理
ブートストラップ時の動きを見てもらえれば分かるように、
本来は担当する予定ではなかったノードがデータを管理している場合がある。
そのためにcleanup処理が必要となる。

どのノードがどのデータをもっているのかを考えるよりも、
リング内全てのノードで実行すればいいだろう。
$ nodetool cleanup


5. 手動での網羅的な修復
・ Anti Entropy
・ 全データへのアクセスによるRead Repair


以上である。



補足(とはいっても超重要)
cleanup手順を以下で回避することも可能である。
cassandraのプロセスを起動させる前に、
明示的にトークン値を割り振っておけば
ブートストラップ中に、他ノードがデータを受け持つことはない。

2.の手順前に以下のようにコンフィグを書き換えておく。
$ vi conf/cassandra-env.sh
JVM_OPTS="$JVM_OPTS -Dcassandra.replace_token=<トークン値>

設定するトークン値は正常に起動している時にリング状態を記録しておくことが前提になる。
ちなみに、構築後の初回起動時は、設定値を入れず、
自動でトークン値を計算をさせればよい。

これでcleanup処理は不要になる。
プロセス起動後の手動での網羅的な修復はしておくこと。



(その他 関連記事)
cassandraのモニタリングポイント