raft

77
Raft 株株株株株株株株株株株株株株株株株株株株株 2014 株 3 株 13 株

Upload: preferred-infrastructure-preferred-networks

Post on 28-May-2015

6.115 views

Category:

Technology


0 download

DESCRIPTION

Raftの解説

TRANSCRIPT

Page 1: Raft

Raft

株式会社プリファードインフラストラクチャー

2014年 3 月 13日

Page 2: Raft

自己紹介

久保田展行 (@nobu_k) Preferred Infrastructure America, Inc. 取締役

本日の NGワード「いつアメリカ行くの?」 MessagePack for C,C++メンテナ 分散システム、 DB、検索エンジン 最近 golangに夢中

2

Page 3: Raft

今日の話: Raft

Raftとは 複製されたログを管理するためのコンセンサス ( 合意 ) アルゴリズム

Raftはわかりやすさを重視して作られた 既存のアルゴリズムは難しすぎて正しく実装するのが困難 もしくは、難しい部分を簡単にしようとして安全ではなくなったり

この先生きのこるには分散システムの理解が必須 コンセンサスは安全な分散システムを構築する上で必須のトピック ツールとして使うにしても、中身や特性は理解しておくべき

資料として使えるように字が多めになっておりますご了承ください

3

Page 4: Raft

スライドの流れ

Raftの前に コンセンサスとは Paxosとその問題点

Raft Raftの概要 アルゴリズムの説明 運用のために必要な機能の説明 評価の話は・・・なしの方向で・・・w

4

Page 5: Raft

コンセンサスとは

5

Page 6: Raft

コンセンサス ( 合意 ) とは

コンセンサスとは プロセスの集合が 1 つの値において合意を取ること 分散システムにおける重要な問題の 1 つ

6

どれか 1 つを選択

BB

AA

CC DD

EE

つけ麺

つけ麺

ロンアールロンアール

ロンアールロンアール

ロンアールロンアール

Red BullRed Bull

「お昼どうする?」

Page 7: Raft

コンセンサスアルゴリズムはなぜ必要か

分散システムで高可用性 (High Availability)を実現するため 高可用性を実現する方法は「冗長化」ただ 1 つ

ハードウェア障害は防げない

7

Page 8: Raft

どうシステムを冗長化するのか

プロセスを State Machineだと考える プロセスは決まった状態の集合を持っている 外部からの入力 ( 命令 ) と状態遷移関数により状態が変わる

Replicated State Machine 冗長化した全プロセスが同じ State Machineとして動く 同じ入力を同じ順序で全プロセスに与え、同じ状態を維持

コマンド列をログとして同じ順序で複製する問題としてとらえる

8

Page 9: Raft

なぜレプリケーションにコンセンサスが必要なのか

同じ命令を同じ順序で届けるため 「 n 番目にどの命令を実行するか」という合意を取る

ナイーブに実装すると障害時に次のような問題が起こる n 番目に同じ命令を実行してない or 命令の実行順序が異なる

プロセス停止、メッセージの喪失、到達タイミングの差などにより プロセスが停止すると状態が巻き戻る ネットワークが分断したときに 2 つの独立したグループができる プロセスの構成 ( メンバ ) を変更できない

実は超絶に難しい問題

ではどうやって実装するのか Paxos

9

Page 10: Raft

( ザックリ )Paxos

10

Page 11: Raft

Paxosとは

最強のコンセンサスアルゴリズム 安全性 難しさ Raftと一緒に 50分で説明できるレベルじゃない

詳しくは 2012/7/5の PFIセミナーを参照してください! http://www.slideshare.net/pfi/paxos-13615514 コンセンサスの話ももう少し詳しく紹介しています

11

Page 12: Raft

Paxosの問題点

理論的には問題はない ( と思う ) 停止保証がないのは?

→そもそも非同期システムにおいて 1 個以上のプロセスが障害を起

こす可能性がある状況で停止保証があるコンセンサスアルゴリズム

は存在しない

ではなにが問題なのか 理論も実装も理解しづらいのが問題

どう言うレベルで理解しづらい? 熟練リサーチャーが解説を求められると固まるレベル

理解しづらいのは問題なのか? 大問題

12

Page 13: Raft

理解しづらいことによる問題点

正しい Paxosが実装されない そもそもどう実装すると正しくなるのか分からない 論文で語られていない詳細も多い

結果として、勘で実装した部分が正しいか保証できない Multi-Paxosの実装すら同じものが存在しないレベル

Chubbyも・・・ “There are significant gaps between the description

of the Paxos algorithm and the needs of a real-world

system.... the final system will be based on an un-

proven protocol” (Paxos made live) コンセンサスが正しく取れていることを仮定して構築されているシス

テムの信頼性が崩壊!

13

Page 14: Raft

なぜ Paxosは難しくなってしまったのか

手法が直感的でない Single-decree Paxosをベースにしているのがよろしくない?

Multi-decreeへの拡張方法も説明不足 若干主観的な主張ではあるが、元論文を読むと共感できる

実装する上で必要な情報を十分に提示できていない 例: multi-decree Paxosでインスタンス IDはどうやって決め

る? リーダー (distinguished proposer)はどうやって決める?

別の合意の仕組みが必要

ログ複製の文脈に限って言えば proposerが複数存在できるのは無駄 Paxosも結局 multi-Paxosで最適化としてリーダーの存在を許す

14

Page 15: Raft

Raft

15

Page 16: Raft

Raft概要:誤解を恐れずにザックリ説明すると

16

Leader

Follower

Follower

Follower

Follower

Client

Client

Client

Raftクラスタ : 1人の Leaderと複数人の Followerがいる

Clientからのリクエストを Leaderがシリアライズしてクラスタ内の他のプロセスに同じ順序でばらまく。

RPCで通信

Page 17: Raft

Raftの特徴

わかりやすさを重視したコンセンサスアルゴリズム “ 複製されたログを管理するためのコンセンサスアルゴリズム” Paxosと違いシステムを構築するための情報がすべて書かれている 少し制約はあるが、汎用的にも使える

Leaderが強い権限を持つ すべてのリクエストは Leaderを通る ログの管理は Leaderが行う 情報は Leaderからのみ流れる RPCの呼び出しも Leaderのみが行う

とりあえずさっきの図は説明していないことが多すぎる Leaderはどう選ぶ? Leaderが死んだらどうする?他の障害は?

17

Page 18: Raft

説明の流れ

アルゴリズムの基本部分実装 Leader選出 ログレプリケーション Safetyの保証

システムとして運用するために必要な機能の実装 メンバシップ管理 ログコンパクション Clientとのやりとり

18

Page 19: Raft

Leader選出強力な権限を持ったプロセスを 1 つだけ選出する

19

Page 20: Raft

Raftにおけるプロセスの状態

プロセスは次の 3 つの状態を持つ: Follower, Candidate,

Leader この状態は Replicated State Machine内部の状態とは別

Follower: 完全受け身の状態。通信を受け取って応答するだけ。 Candidate: Leaderになりたい人。 Leader: ボス。すべてのリクエストは Leader経由で行われる。

必ず最大 1 プロセスになるように! 起動直後はみんな Follower

20

Follower Candidate Leader

Start up

Page 21: Raft

Leaderと Term

Raft内では Termという論理時間が使われる Termとは特定の Leaderが連続して活動していた期間 Termには単調増加する IDが割り振られる 各 Termは Leaderの選出から始まる 各 Termには 0 人か 1 人の Leaderがいる Leaderがいなくなったら新しい Candidateが次の Termを開始

 は Leader選出期間  は通常稼働中の期間

21

Term 1 Term 2 T 3 Term 4

Page 22: Raft

Leader選出開始のタイミング

Raftではハートビートを利用して Leader選出の開始判断をする ハートビート = 空の AppendEntries RPC(後述 ) 一定期間 Leaderからハートビートが来なかったら選出開始

Election timeout: 実験では 150ms前後に設定 タイムアウトした Followerが Candidateとなる

22

Follower Candidate Leader

Start up

Times out,Starts election

Page 23: Raft

投票開始

Current term number 各プロセスは current term numberを持つ Leader、 Candidateのリクエストは current term number

を含む Current term numberは新しい値を受け取る度に更新される

Followerは Candidateになったタイミングで、1. 自身の Current term numberを増やす

2. RequestVote RPCを全メンバに送信する

23

Page 24: Raft

RequestVote API

引数 term: Candidateが観測している現在の term candidateId: Candidateのクラスタ内での ID lastLogIndex, lastLogTerm: ログレプリケーションのとき

に説明 Candidateが持つ情報の新しさを判定するために利用

Followerからの返値 term: Followerが観測している term voteGranted: bool値。 trueなら Followerが投票したこと

を表す。

24

Page 25: Raft

投票と集計

Followerは一番最初に受け取った有効リクエストに対して投票する ただし、後ほど安全性を保証するために条件を追加 Followerは 1 termで 1 回までしか投票しない

これにより 1 termで 1 Leaderしか選出されない Candidateはクラスタの半数以上から投票されたら Leaderにな

る 自分は自分に投票する ( これも 1 term 1回に含まれる )

25

Follower Candidate Leader

Start up

Times out,starts election

Receives votes frommajority of servers

Page 26: Raft

投票終了

Leaderになった Candidateはハートビートを全員に送信 Followerはハートビートを受け取ったら Leaderの情報を更新 Candidateはハートビートを受け取ったら Followerに戻る

ただし、ハートビートの current term numberが古かったら拒

否! そのまま Candidateの状態を維持

26

Follower Candidate Leader

Start up

Times out,starts election

Receives votes frommajority of servers

Discovers the currentleader

Page 27: Raft

古い Leader、 Candidateの扱い

Current term numberを使って古い Leader、 Candidateを検出 新しい current term numberのリクエストを受け取ることで検

知 Followerが Leaderより新しい current term numberを持つ

ことも 通信障害などで、旧 Leaderが知らない場所で新しい Leaderが誕

生 古い Leaderや Candidateは、検出次第すぐに Followerに戻る Followerは古い Leaderや Candidateからのリクエストを拒否

する

27

Follower Candidate Leader

Start up

Times out,starts election

Receives votes frommajority of servers

Discovers a processwith higher term

Discovers the currentleader or new term

Page 28: Raft

投票が完了しないとき

投票が完了しない Termもある 複数の Candidateがほぼ同時に登場し、全員が過半数に至らない メッセージロスト そう言うときは election timeoutが発生して投票をやり直し

Live lock やり直し時に複数 Followerがほぼ同時に Candidateになると厄介

無限に投票がやり直される可能性もある 解決策: Election timeoutの時間をランダムにする

大体 Candidateは 1 プロセスだけになるのでうまくいく 実装では 150-300msとした

あくまで大体うまくいくのであって、たまに衝突は起こる ただし、実用上問題の無いレベル!ということ

28

Page 29: Raft

まとめ: Leader選出

Followerのルール: 自分より termが新しい Candidateからの RequestVoteに投票する Election timeoutが発生したら Candidateになる

Candidateのルール: 自分に投票する 全プロセスに対して RequestVote RPCを実行 自分を含め過半数のプロセスから投票されたら Leaderになる 新しい Leaderからの AppendEntries RPCが来たら Followerに戻

る Election timeoutが発生したら新しい termで再投票

後で safety実現のために新ルールが追加される

29

Page 30: Raft

ログレプリケーションコマンド列をログとして複製する

30

Page 31: Raft

ログの持ち方

ログはすべて Leaderからの AppendEntries RPCにより複製される

31

1x←3

1x←3

1x←3

1x←3

1x←3

1y←1

1y←1

1y←1

1y←1

1y←1

1y←9

1y←9

1y←9

1y←9

2x←2

2x←2

2x←2

2x←2

3x←0

3x←0

3x←0

3x←0

3y←7

3y←7

3y←7

3x←5

3x←5

3x←5

3x←4

3x←4

1 2 3 4 5 6 7 8 log index

leader

followers

term number

(a)

(b)

(c)

(e)

(f)

Page 32: Raft

AppendEntries RPC

引数 term: Leaderの term number leaderId: Leaderのクラスタ内での ID prevLogIndex, prevLogTerm: 1個古いエントリのバージョン情

報 entries[]: ログエントリ ( 配列 ) 、空だと heartbeat leaderCommit: Leaderがコミット済みの index number

返値 term: Followerが観測している term number success: prevLogIndex/Termが一致したかどうか

falseだったら古いログの再送が必要 もしくは Leaderが古くなってる

32

Page 33: Raft

複製とコミットのフロー (1/2)

33

Leader

Follower

Follower

Follower

Follower

Client

1. リクエスト2. ログに書く( まだコミットしない )

3. AppendEntries

Raftクラスタ

Page 34: Raft

複製とコミットのフロー (2/2)

34

Leader

Follower

Follower

Follower

Follower

Client

4. ログに書く

6. 過半数の followerから返答があったらステートマシンにリクエストを反映( コミット )

5. Leaderに返答する

7. Clientに返答する

Raftクラスタ

Page 35: Raft

コミット

Leaderは自分のステートマシンにコマンドを反映する ここからはやり直しが効かないし、ログの上書きも起こらない コミットしたタイミングで commitIndexを増やす

Followerのコミットタイミング判断 AppendEntriesの leaderCommitを参照して判断

Leaderがコミットしたところまでは自分もコミットする つまり、コミットのタイミングは Leaderと Followerで異なる

後ほど問題が無いことを説明

35

Page 36: Raft

異常系: Leader障害が積み重なると・・・1

Leaderfor term 8

ログの再送・上書きによるコミット前エントリの同期・修正が必要。一貫性チェックの仕組みと一緒に考える。

36

1 1 1 4 4 5 5 6 6 6

1 1 1 4 4 5 5 6 6

1 1 1 4

1 1 1 4 4 5 5 6 6 6

1 1 1 4 4 5 5 6 6 6

1 1 1 4 4

1 1 1 2

6

7 7

4 4

2 2 3 3 3 3 3

(a)

(b)

(c)

(d)

(e)

(f)

2 3 4 5 6 7 8 9 10 11 12

同期遅れ

未コミットのエントリを持つ

同期遅れ&未コミットエントリ持ち

Page 37: Raft

ログの一貫性維持

守りたい制約 異なる 2 つのログにて、ある indexのエントリが同じ termだったら

ログの中身は同じ それ以前のログの内容は同じ

AppendEntries時にチェックを加える Leader: 直前のエントリの indexと term numberを渡す Follower: 自分の最新エントリが同じ index/termを持つか

チェック 持ってた:自分のログにエントリを追加し AppendEntriesに応答 持ってなかった: falseを返しつつ・・・

– 自分の termが新しかったらそれを leaderに伝える

– 自分が遅れていたら古いエントリの再送を待つ ヤバいケースに関しては safetyのところで

37

Page 38: Raft

ログの再送と上書き

AppendEntriesでは Followerのログの上書きも行う ただし未コミットのログのみ!!

制約を守っていればコミット済みのログの書き換えは行われない また、 Leaderは自分のログに対しては Appendしか行わない

同期遅れが原因で AppendEntriesが失敗したら昔のエントリを再送 Leaderは 1 つ古いエントリについて AppendEntriesを再実行

2 個前のエントリの index/termを使って 1 個前のエントリを再送 成功したら次のエントリも再送 失敗したら 2 個前のエントリに関して同様に再送を試みる

成功するまで続ける・・・

– 効率悪くない?→現実のケースでは大丈夫だったらしい

– 必要であれば最適化も可能だが、 keep it simple (stupid)!38

Page 39: Raft

異常系の対応

Leader Followerへの通信がタイムアウト:あとで再送する クラッシュ:未配布のエントリは消失、 Clientが再送

Follower クラッシュ: Leaderが後で再送してくれる

Client Leaderへの通信がタイムアウト:再送

アプリケーション側で冪等性を保証する仕組みが必要・・・

39

Page 40: Raft

まとめ:ログレプリケーション

Leaderがすべてのリクエストを処理する 異常系は基本的に再送でカバー

Clientは冪等性に注意しなければならない・・・ 後ほど少し補足

過半数のプロセスが書いたエントリをコミット コミット =Replicated State Machineに命令を反映すること

Followerは leaderCommitからコミットすべきエントリを知る Leaderとコミットタイミングがずれるが大丈夫

40

Page 41: Raft

Safetyログを壊さない

41

Page 42: Raft

普通ならここからが本当の地獄のはずだ・・・

今までは比較的普通のケースのみを扱った いよいよ障害時に耐えうるシステムを設計する

Replicated State machineの一貫性が保てるようにするのが課

ところで、 Raftで問題になるのってどんなケース? コミット済みのログエントリが上書きされる

= 各プロセスが異なるコマンド列を実行する

どう言う状況で起こる? Leaderがログエントリ複製中もしくはコミット直後に死亡

あれ、簡単・・・?42

Page 43: Raft

( 今更ながら )Raftが保証するもの : 論文のFigure 3 Election Safety

1 termで 1 Leaderしか選択されない Leader Append-Only

Leaderは自分のログに対しては Appendしかしない Followerのログ ( 未コミット分 ) を書き換えることはある

Log Matching ログの一貫性を維持するための性質

Leader Completeness ( 若干省略 )Leaderはそれまでにコミットされた全ログを持つ

State Machine Safety ( 若干省略 ) 全プロセスが同じコマンドを state machineに適用す

る 最終的に保証したいもの

43

Page 44: Raft

Leader Completeness

守りたい制約 Leaderはそれまでにコミットされた全ログエントリを持つ

実現方法:選出のタイミングまでにコミットされたすべてのエントリを持つプロセスしか Leaderとして選出されなければ良い 前 Leaderがコミットしたエントリは未コミット状態でも良い

あくまでコミット済みエントリを持っているだけで十分 他のプロセスから不足分を調達するような複雑さは不要

Leader選出とコミットのタイミングに関してルールを追加 2 つ合わせて初めて機能するルール

44

Page 45: Raft

Leader選出の追加ルール

過半数の Followerからログがより新しいと認められたら OK Candidateの termと index numberを元に判断

RequestVote RPCで渡される

2 つのログのどちらが新しいかは次のように判定する1. 最新のエントリの Termが異なる場合は Termが大きい方が新しい

2. Termが同じなら index numberが大きい ( ログが長い ) 方が新し

これと次の新コミットルールを同時に守る その前にコミット時に起こる問題を見てみる

45

1

1 2

41 2

1 2

3

3

1 2

1 2

3

3

1 2

1 2

3

3

3

4

<<1. 2.

Page 46: Raft

コミットタイミングの問題 (1/2)

Leader S1が死亡 S2にだけエントリを複製

S5が Term 3の Leader

に エントリを 1 個作る その後複製せずに死亡

S1が再度 Leaderに S3にエントリを複製 2 は過半数に複製された

この時点で 2 をコミットできるか?

46

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

3

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

3

4

2

3

Page 47: Raft

コミットタイミングの問題 (2/2)

安全にコミットできない S1が 2 をコミット 直後に S1が死亡する

S2,S3は未コミット S5が Term 5の Leaderに

全員 index=2 Termは 3 が最新 S2,S3,S4が投票可能

S1がコミット後に死んだら S5が 3 を全員に複製 S1が復活

エントリの不整合

47

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

3

4

2

3

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

3

4

2

3

S5は Term 3のエントリ 2 を全員に上書きする権限を持つ

Page 48: Raft

コミットタイミングの追加ルール

まず自分の termでコミット可能なエントリを作り出す 過半数のプロセスが自分の index/termで同じログエントリを持

つ Log Matching propertyより、それらのプロセスのログは同一 さらに、 Leaderはそれらのプロセスからしか選出されない

コミット可能なエントリができたら古いエントリから順次コミット

48

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

3

4

2

3

4

4

2 をコミットする前に 3 を過半数に複製。そうすることで、次の Leaderは 3 を持つ。この時点で 2 をコミット直後に死んでも次の Leaderも同じエントリをコミットする。3 はもちろん 2 の後にコミット。

Page 49: Raft

追加コミットルールの実現方法

論文的には 2 つ方法があるように見える

1. 次の Clientからのリクエストを待つ その段階で新しいエントリが作成される それがコミット可能になった段階で古いエントリもコミット

2. Candidateが Leaderになったら no-opエントリをコミットを試みる no-opがコミット可能になったら古いエントリから順次コミット 後ほど説明する readを最適化するときにも役立つ

実は論文だとそこで初めて出てくる情報

個人的には後者の方が無難な気がした

49

Page 50: Raft

補足:コミット直後の Leader死は大丈夫か

Term n+1のケースだけ考えれば良い (Leaderが死んだ直後なので )

Index 2は過半数のプロセスに複製されている この時点で Leaderになれるのは S2か S3のみ

コミット済みのエントリを持っているので安心

50

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

2

1 2

1 2

1

1

1

S1

S2

S3

S4

S5

1 2

2

S1が 2 をコミットした直後死亡

Page 51: Raft

まとめ: Safety

2 つの追加ルールで Leaderの障害に対応 ログが新しい Leaderを選出する termが変わった直後には古いエントリをコミットしない

Safetyの証明は論文を参照! 時間が足りなかった 基本的には新しい Leaderの 1 個前の Leaderがコミットしたエン

トリを持ってないと仮定して矛盾を示す流れ その後、 State Machine Safetyが満たされるのは自明

別文献で形式手法での証明もある Safety proof and formal specification for Raft.

http://ramcloud.stanford.edu/~ongaro/raftproof.pdf

51

Page 52: Raft

メンバーシップ管理クラスタ構成を変更する

52

Page 53: Raft

クラスタの構成変更

運用中にたまに発生する 故障したマシンの取り替え マシンの増大 etc

難しい点 新構成と旧構成でそれぞれ独立して Leaderが選ばれる可能性があ

る Raftの safetyは単一 Leaderが前提となっている

対処方法 一般的には 2 フェーズ以上での移行が必要 一番実現が簡単なのは全台停止→設定変更→再起動

しかし、普通は無停止で移行したい

53

Page 54: Raft

Joint consensus

新構成と旧構成が混ざった状態を間に挟む 旧構成: C_old={A, B, C} 新構成: C_new={B, C, D, E, F} 混在構成: C_old,new={A, B, C, D, E, F}

54

AA

BB

CC

DD

EE

FF

C_old

C_new

Page 55: Raft

Joint consensusでのルール

1. ログは C_old,newの全プロセスに複製される2. C_old, C_newのどちらのプロセスも Leaderになれる3. 合意 (Leader選出、ログのコミット ) は両構成からの過半数が必

要 C_oldと C_newで独立して過半数チェックを行う

55

AA

BB

CC

DD

EE

FF

C_old

C_new

紫は 3 プロセスだがC_old,C_newともに過半数を占める。

Page 56: Raft

構成変更フロー

1. C_oldの Leaderはまず C_old,newへの構成へ移るように指示 特殊なログエントリとして全 Followerに複製する このエントリの情報は未コミットでも反映される この時点ではまだ C_old内で合意が取れれば OK

2. C_old,newがコミットされたら joint consensusへ移行 同時に C_newに移る指示を同様の仕組みで全 Followerに通達

3. C_newがコミットされたら C_oldのプロセスは消滅する

C_old,newコミット前は、 C_oldが独断でコミットしても安全 C_newが複製され始めたら C_newが独断でコミットしても安全

56

Page 57: Raft

残りの問題

C_newコミット時に C_oldのプロセスが Leaderだったとき Leaderはその時点で退く C_newがコミットされるまで Leaderが投票権を持たない状態が続

新規プロセスが登録されるとき 新しいプロセスはログを持ってないので同期が必要になる

同期が終わるまでクラスタ全体でコミットできなくなることも C_old,newの前にログ同期専用のフェーズを追加する

C++実装ではステージングフェーズと呼ばれている ログの同期が終わってから C_old,newを複製 ダウンタイムが最小に

57

Page 58: Raft

補足:ステージングの課題

著者の C++実装を見てみた ステージングの情報は Leaderしか持ってなさそう Leaderがステージング中に死ぬと構成変更が実行されない

クライアント側でタイムアウトで再送すればいい気もするが・・・

– しかし、同期には時間がかかるためタイムアウトの設定は難しい

ステージング開始前にステージング構成のログを複製すれば解決? って適当にやるとそれこそ Paxosの unproven実装問題が再発

58

Page 59: Raft

未解決問題

構成変更中音信不通だった C_oldのプロセスが生き返るケース C_oldのプロセスには当然 C_new専用のハートビートは届かない Election timeoutが何度も発生し、ひたすら Leader選出が続

く Leader election中はリクエストを受け付けられず可用性が下が

59

AA

BB

CC

DD

EE

FF

C_new

Page 60: Raft

まとめ:メンバシップ管理

Raftはクラスタの動的な構成変更に対応している しかし、まだ正しさが完全に保証されていない箇所も・・・ 未解決の課題もいくつかある

方針は立ってそうな感じ

ただ、課題はあるとは言え運用でカバーできる面が大きい 手を付けられない状況にはならない

60

Page 61: Raft

ログコンパクションログ容量を減らしつつ復旧時間を短くする

61

Page 62: Raft

ログの肥大化問題

ログは無限に貯められない どこかで不要なログを消す必要がある

消し方1. Log cleaning

不要になったエントリを取り除く

– 不要 = 取り除いても現在の状態に影響がない 意味づけや実装が難しい

2. Snapshotting Replicated State Machineのスナップショットをとる

– 分散ではなくプロセス毎に独立したスナップショット Chubbyや ZooKeeperでも取られている一般的なアプローチ

62

Page 63: Raft

Replicated State Machineのスナップショット ハッシュテーブルを利用した簡単なインメモリ KVSを考える

オペレーション : キーに値を設定 内部表現 : JSONのオブジェクトみたいなもの

63

x 3

y 1

y 9

x 2

x 0

y 7

x 5

last included index: 5last included term: 3

{ x: 0 y: 9}

1 2 3 4 5 6 7

y 7

x 5

6 7

Index 5の段階で取ったスナップショット+ 残りのログ

元ログ

Page 64: Raft

スナップショットとログ

スナップショットを取ったところまでのログは消せる スナップショットに index/term numberを含める

term numberは consistency checkで使われる

スナップショットのその他の使われ方 障害復旧後や、新規参入したプロセスを追いつかせる処理

ログだけだと復旧できないのでスナップショットを転送 InstallSnapshot RPC

64

Page 65: Raft

スナップショットの取り方

スナップショットを取っている間もシステムを停止させたくない Copy on Writeを活用する

1. 永続データ構造みたいなのを使う Purely functionalな感じのデータ構造

2. Linuxで forkを活用する (Redisのアプローチ ) スナップショットを取るプロセスを fork 元プロセスでは書き込み処理を継続可能

頻度 スナップショットはそれなりに重い処理 高頻度すぎるとシステムが遅く、低すぎると復旧時間が長くなる ログが一定サイズになったら実行するのがシンプル

65

Page 66: Raft

補足: Raftとスナップショット

スナップショットは Raft的には例外的なオペレーション 唯一 Followerが能動的にスナップショットを取る

なぜ Leaderがスナップショットを取って配れないのか? スナップショットの配布がネットワークを使い切っちゃう可能性が

Leaderがスナップショットを取るタイミングを指示できないか? 最適なタイミングを知るためにはシステムが若干複雑に

結局 Followerが自分でやるのが楽 必要な情報は全部 Followerが持ってる

66

Page 67: Raft

まとめ:ログコンパクション

ログは放っておくと際限なく増える 定期的にスナップショットを取って古いログを削除する スナップショットを取るのは重い処理なので実行頻度に気をつける

67

Page 68: Raft

Clientとのやりとり

68

Page 69: Raft

Clientと Leader

Raftとのやりとりはすべて Leaderを介して行う Followerへのリクエストはすべて失敗

代わりに自分が知っている最新の Leaderの位置を返す Followerはハートビートなどの情報から Leaderについて知って

いる

Clientはまず適当なノードにリクエストを送信 失敗したら Leaderに問い合わせ直す Leaderへのリクエストがタイムアウトしたときも同様

69

Page 70: Raft

Linearizabilityの実現

Linearizabilityとは ( 引用 ) “each operation appears to execute instantaneously,

exactly once, at some point between its invocation

and its response” Writeと Readで分けて考える

Write Leaderが 1 個 1 個実行しているためタイミングの問題は無い

Raftの RPCは冪等なので再送による問題も起こらない Clientのリトライによる重複実行が課題

アプリケーション側でコマンドに IDを持たせて重複実行を回避

– あっさり書いてるけど地味に大変・・・ Read

古い情報を返さないようにする70

Page 71: Raft

Linearizability: Read

Writeと同様にログエントリにすれば解決 しかし、状態を変更する必要が無いため高速化できる部分が多い

本来であればログに書き込む必要がない 命令のコミット処理もいらない

最適化するといくつか問題が起こる可能性がある 前 Leaderがコミット済みだが自分がコミットしてない情報を返す

まずは Leaderになった直後に自分の termで no-opをコミット 前 termまでの情報はすべてコミットされるので最新情報を返せる

自分が値を読んでる間に別の Leaderが誕生 返す前に全台にハートビートを送り過半数からのレスポンスを待つ ログに書き込むよりは低いコストで読み込める

71

Page 72: Raft

まとめ: Clientとのやりとり

Clientはまず適当なプロセスにリクエストを送信 Leaderじゃなかったら、教えて貰った Leaderに問い合わせし直

Linearizability Write

Raftのレイヤーでは問題無い Clientは再送検知の仕組みを自分で実装する

– もしくは全命令を冪等にする Read

最適化しつつ古い値を返さないように工夫する

72

Page 73: Raft

まとめ

73

Page 74: Raft

Raftとは

ログレプリケーションを前提に現実的な制約を付けパフォーマンスやわかりやすさを向上させたコンセンサスアルゴリズム

確かに分かりやすい Paxosの準備期間は 100時間、 Raftは 40時間くらい

実装も資料もいっぱいある http://raftconsensus.github.io Paxosは特許の関係もあり公開されてる実装は少ない また、ほとんどが” Paxos-based”

つまり Paxosは少し改変しないと現実では使いづらい または、 Paxosとは何か自身を持って主張できる人が少ない

74

Page 75: Raft

Paxosは無くなるのか?

無くならないと思う 問題になるのは Raftの時間的な制約

ブロードキャスト時間 << election timeout << MTBF 広域で使おうとすると難しい ( 使えなくもないとは思う )

RTTが 150msかかる場合の election timeoutはどれくらいが適

切? Single-decreeや基本的な Paxosで十分なことも多い

例: KVSにてキー毎に独立して合意を取る グローバルな順序付けがいらない

Paxosは Leaderが複数いても安全に動く Leaderを維持するコストがいらない ( 合意コストが上がるけど )

時間的な制約もほとんどないに等しい 結局は適材適所

75

Page 76: Raft

ZooKeeperは無くなるのか?

Raftは ZooKeeperじゃなくて ZAB相当 Raftの上に ZooKeeper相当のアプリケーションを作ることも可

能 完全に ZooKeeper相当では無いが、 etcdというのがある

https://github.com/coreos/etcd

ZooKeeper(や Chubby)のコンセプト自体は不滅 実装がどうなるかは謎!

76

Page 77: Raft

Copyright © 2006-2014

Preferred Infrastructure All Right Reserved.