Akka Cluster の ののののの のののの Scala の西 2016 のののののののの https://github.com/TanUkkii007/blog/blob/master/akka_cluster_resilience.
Akka Cluster の耐障害設計安田裕介
Scala 関西 2016
スピーカーノートhttps://github.com/TanUkkii007/blog/blob/master/akka_cluster_resilience.md
自己紹介• 安田裕介• @TanUkkii007
• 東京で Scala の仕事をしています• Akka 好き
Akka Cluster の適用領域Basically
Soft state
Eventually consistent
Available
client-facing で
な分散システム
Akka Cluster を使う利点• 位置透過性
• サービスの成長に合わせてスケール戦略を切り替えられる• スケール戦略が変わってもコードが変わらない
• エコシステム• シャーディングやレプリケーションなど、分散システムでよく用いるアルゴリズムを実装した拡張がある
Akka Cluster の基本
Akka Cluster とは?• クラスターのメンバーシップ管理を行う Akka 拡張• Amazon Dynamo や riak に影響を受けている• gossip プロトコルによるメンバーの状態伝播と、 failure detector による障害検知を可能にする
failure detector• クラスターのメンバーは互いにハートビートを送り合っている• failure detector はハートビートをもとにメンバーの生死を推測する• 生きているメンバーは
reachable 、死んでいるメンバーはunreachable とマークする
gossip プロトコルとメンバーシップライフサイクル• メンバーには状態があり、 gossip プロトコルで他のメンバーと状態を同期する• gossip 収束時に一意に決まるリーダーという役割がある• リーダーがメンバーの状態を変える行為をリーダーアクションという• クラスターに参加するメンバーは joining 状態から始まる• リーダーは joining 状態のメンバーを up 状態にし、クラスターに参加させる• down 状態になったメンバーは、リーダーによってremoved 状態にされ、クラスターから取り除かれる
doc.akka.io
Akka Cluster の耐障害性設計
故障の単位:プロセス• 故障の単位をプロセスという• アクタープログラミングではプロセスはアクター• この発表では Akka Cluster の1ノードをプロセスとする• つまり Akka Cluster の UNIX プロセスと同義
故障の種類クラッシュストップ故障
オミッション(切り捨て)故障クラッシュ・リカバリー故障
ビザンチン(任意)故障Reliable and secure distributed programming, Ch.2
クラッシュストップ故障オミッション(切り捨て)故障
クラッシュ・リカバリー故障ビザンチン(任意)故障
Reliable and secure distributed programming, Ch.2
クラッシュストップ故障
クラッシュストップ故障• 正常に処理を実行していたプロセスがある時刻以降処理を停止して2度と戻らない故障• 故障のなかでもっとも単純• Akka Cluster のレイヤーで考えなければならない故障のほぼ全てはこれ
クラッシュストップ故障で停止する処理• unreachable なメンバーがいると、 gossip プロトコルで状態を完全に同期できない( gissip 非収束状態)• クラスターの状態を変えるリーダーアクションが行えなくなる• → メンバーの追加ができない
gossip 非収束状態の解決
• unreachable なメンバーのハートビートが回復して failure detector によって再び reachable になる• unreachable なメンバーを down 状態にする
クラスターのメンバーがクラッシュストップ故障を起こすと、そのメンバーに gossip プロトコルによってクラスターの状態を伝播できなくなる。このとき gossip は収束しない。
gossip の非収束を解決する方法
doc.akka.io
Auto-downing• デフォルトでは unreachable なメンバーを自動的に down 状態にはしない• リーダーが unreachable なメンバーを指定時間後に自動的に down する機能に auto-down がある• auto-down は使ってはならない
_人人人人人人人人_> < ̄ Y^Y^Y^Y^Y^Y^Y  ̄
doc.akka.io
なぜ auto-down を使ってはならないのかを考える根本的な問題は、 failure detector がメンバーを
unreachable と判定したとき、そのメンバーは本当に死んでいるのか、ネットワーク遅延や分断なのか、 GCや負荷による遅延なのかが分からないということ。この問題が分散システムを難しくしている理由の1つ。down したメンバーが実は生きていた場合、 split
brain 問題がおきる。
split brain 問題• failure detector がメンバーを死と推定したとしても、実際には生きている場合がある• 生きているメンバーをクラスターから分離すると、結果的にクラスターが分裂する問題を split brain 問題という• 分離したクラスターの状態は同期出来ず、誤った情報をクライアントや
DB に伝える
リーダーの決定• リーダー選出という過程はない• 各メンバーが gossip プロトコルで同期されたメンバーリストから独立に決める• リーダーは unreachable でないメンバーのうち Up と Leaving状態のものを優先的に選択し、アドレス順に並べて先頭のもの
/** * Up|Leaving, Joining, Exiting, Down の順に並べ先頭のものがリーダー。同じ状態の場合最小のアドバイスのものを選ぶ。コードは以下を参照。 * https://github.com/akka/akka/blob/v2.4.10/akka-cluster/src/main/scala/akka/cluster/Gossip.scala#L190-L196 */val leader = reachableMembers.min(Ordering.fromLessThan[Member] { (a, b) ⇒ (a.status, b.status) match { case (as, bs) if as == bs ⇒ Member.addressOrdering.compare(a.address, b.address) <= 0 case (Down, _) ⇒ false case (_, Down) ⇒ true case (Exiting, _) ⇒ false case (_, Exiting) ⇒ true case (Joining, _) ⇒ false case (_, Joining) ⇒ true case _ ⇒ Member.addressOrdering.compare(a.address, b.address) <= 0 }})
auto-down が引き起こす split brain
split brain 問題を解決するには• リーダーよりも整合性の高い方法で決定でき、 down を果たせる役割は何か?• 2つ以上に分割される場合、どれが正しいクラスターなのか?• 正しくないクラスターを決めたとして、そのメンバーをどうすべきか?
split brain resolver
• Keep Reference• Keep Oldest• Static Quorum• Keep Majority
• Lightbend Reactive Platform
• TanUkkii007/akka-cluster-custom-downing
split-brain-resolver のストラテジ
Keep Oldest• 最古のメンバーがいる側を正のクラスターとする• 最古のメンバーとは起動時のタイムスタンプがもっとも小さいもの
• そうでない側のメンバーは自らシャットダウンする• unreachable なメンバーを down する役割は最古メンバーが担う• 最古のメンバーは gossip 非収束時にも一意に決まる(※例外あり)• 可用性の点で問題あり。最古のメンバーが故障した場合、全クラスターがシャットダウンする。(オプションで回避可能)
val oldest = members.filterNot(_.status == Removed).min(Member.ageOrdering)
Keep Oldest
Static Quorum• 残存メンバーが quorum size に満たない場合、そのメンバーを
down しする• 他のメンバーを down する役割はリーダーが担う• quorum-size * 2 - 1 を超えるメンバーを追加しない限り split
brain はおきない• quorum-size と総ノード数で可用性を調節可能• メンバーの数を固定しなければならない点が弱点。 quorum を適用するロールを限定して他のロールのメンバー数を動的にすると良い。
Static Quorum
FLP Impossibility “非同期なシステムにおいては、
ただ1つのプロセスが故障しただけでも、完璧に合意できる分散アルゴリズムは存在しない”
Fisher, Lynch, Paterson (1985) Impossibility of Distributed Consensus with One Faulty Process
完璧な split brain resolver のストラテジはない
オミッション(切り捨て)故障クラッシュ・リカバリー故障
ビザンチン(任意)故障Reliable and secure distributed programming, Ch.2
オミッション(切り捨て)故障クラッシュストップ故障
オミッション(切り捨て)故障• プロセスが送るべきメッセージを送らない、あるいは受信するべきメッセージを受信できない故障• プロセスがクラッシュした場合も送るべきメッセージを送れないので、オミッション故障はクラッシュストップ故障のより一般的な場合と見ることができる
Akka Remote におけるオミッション故障• システムメッセージを送信できず、ローカルとリモートのアクターシステム間の状態が同期できなくなったときにオミッション故障となる• システムメッセージには、リモートスーパーバイザーに管理されたアクターのライフサイクルイベント、 watch による死活監視、リモートアクターのデプロイメントがある• このとき Akka Remote の状態は quarantined になり、そのメンバーは unreachable から戻ってこれなくなる• split brain resolver を使用している場合、 unreachable なメンバーはクラスターから取り除かれ、取り除かれたメンバーはシャットダウンする→クラッシュストップ故障と全く同じ扱いになる
オミッション(切り捨て)故障クラッシュ・リカバリー故障
ビザンチン(任意)故障Reliable and secure distributed programming, Ch.2
クラッシュ・リカバリー故障クラッシュストップ故障
クラッシュ・リカバリー故障• プロセスがクラッシュしただけでなく、そこからリカバリーできない、あるいはクラッシュと再起動を繰り返してしまう故障• リカバリーできない場合、送るべきはずのメッセージを送れないので、オミッション故障と見ることもできる
Akka Cluster におけるクラッシュ・リカバリー故障• クラッシュして取り除かれたノードが再起動してクラスターに再加入することを前提としていない• クラッシュ・リカバリー故障はおきない
メンバーの識別• Akka Cluster はメンバーを hostname:port:uid の3つの識別子で認識する• uid はアクターシステム起動時に発行されるユニークな ID• ホストとポートが同じでも、再起動した後では uid が異なる• つまりクラッシュして再起動したプロセスは、以前のプロセスと別物と認識される• →新しくクラスターに加入することと全く同じ:クラッシュ・リカバリー故障はおきない
蘇り( incarnation )ノード• Q: クラッシュしたメンバーは unreachable となってリーダーアクションがとれなくなるため、再起動してもノードがクラスターに再加入できるのか?• A: できる• クラスターのメンバーと同じホスト:ポートのペアをもつメンバーが加入( joining )してきた場合、リーダーは古いメンバーを自動的に downする• 同じホスト:ポートのペアをもつメンバーが同時に2つ存在することはありえない。古いメンバーはクラッシュしたことをリーダーは判断できる。• auto-down や split brain resolver を使わなくてもこの機能は働く
再起動が引き起こすsplit brain 問題
正しい第一 seed 設定
再起動が引き起こすsplit brain 問題
誤った第一 seed 設定