2016年09月26日
Serfの障害検知とメンバシップ管理について
はじめに
KLabさんの協力会社として一緒にお仕事をさせて頂いておりますクラスターコンピューティングと申します。今回Serfという面白そうなツールがあるので試してみました。システムの高可用性化を目的にクラスタを構成することはよく行われていると思います。可用性を維持するために大切なことはクラスタに属するサーバの状態を常に把握しておくことです。そしてサーバの状態に変化が生じた場合ーたとえばサーバが不意に停止した場合ーそれに応じて適切な動作ができるようにする必要があります。システムのサービスはこの土台となるサーバ管理の仕組みの上に構築されます。
Serfはこのクラスタの土台となるサーバの管理の部分をサポートしてくれるツールです。Serfはゴシッププロトコルを利用したP2P型のクラスタを構成します。これによりシンプルな作りでかつ信頼性やスケーラビリティに優れ、そしてネットワーク的な効率も良い管理システムを提供しています。
Serfの基本機能
本家ホームページ
SerfはHashcorpで開発されているクラスタの管理ツールです。
イントロダクションのページにも書かれていますが、Serfの基本機能は以下の3つになります。
- メンバシップ管理
- メンバの障害検知
- メンバへのイベント伝達
Serf はいわゆるP2P型のクラスタを構成します。クラスタを構成する全てのサーバ上でSerfエージェントが実行されています。クラスタを管理する中央サーバ的なものは存在せす、それぞれの Serfエージェントに役割や機能は同じです。Serfでは各エージェントが相互に通信することによって最新のクラスタの状態を把握することによりクラスタ構成しています。すべてのエージェントが保持しているクラスタについての情報は常に最新で同一のものになっています。
Serfでのメンバシップ管理
Serfエージェントが実行されクラスタに所属しているサーバをメンバと呼びます。Serfにおけるメンバシップ管理は次の完全性に基いています。- メンバシップ管理における完全性:各メンバが他の全てのメンバの状態を常に把握している。
Serfではこの完全性を満たすため次のようなアプローチをとっています。
クラスタの状態に変化が発生したとき、少なくとも1つの正常なノードがそれを検知し、全体にそれを伝達(ブロードキャスト)する。
有限時間内それを検知し、それを有限時間内に全体に伝たえる。
例えば、自身の状態に変化が生じたときは自分でそれを全体に伝えるアクションを実行する必要があります。また、新たなメンバがクラスタに参加する際には、新規メンバはすでに参加しているいずれか1つのメンバを知っていればクラスタに参加できます。複数のメンバをもつクラスタのいずれか1つのメンバが別のクラスタに参加した場合、残りのメンバもそのクラスタに参加し全体でに1つのクラスタになります。
Serfのメンバシップ管理でやり取りされる情報は、メンバのホスト名、IPアドレス、障害ステータス(死活)、タグなどの情報です。
完全性を満たすためには確実に状態の変化を検知する仕組みおよびクラスタ全体にすみやかに情報を伝達する仕組みが必要になります。Serfではゴシッププロトコル的な手法を利用することによって、ネットワークの負荷を抑えつつこれらの仕組みを構築しています。
メンバの障害ステータス
メンバのステータスはserf membersというコマンドで取得することができます。どのメンバ上で実行しても同じ結果が得られます。
server1 # serf members
server1 192.168.0.101:7946 alive
server2 192.168.0.102:7946 alive
server3 192.168.0.103:7946 alive
メンバの障害ステータスは正常(alive)、正常終了(left)、障害(failed)の3つの状態で表されます。
aliveは正常に他のメンバからそのメンバにアクセスできる状態です。
leftはSerfエージェントがserf leaveコマンドやCtrl-Cなどによって正常に終了したことを示しています。
Serfエージェントは正常に終了する際に自身の終了のクラスタ全体に通知します。
受け取った各エージェントはそのメンバのステータスをleftに変更して障害検知の対象から外します。
failed はそのメンバにアクセスができず障害と判断されていることを示しています。この場合はそのメンバに対する死活確認は継続し、復旧が確認された場合は自動的 にクラスタのメンバの復帰します。また、failedの状態で決められた期間(デフォルトでは24時間)が経過すると障害検知対象から外されるようにもなっています。
メンバのタグ
Serfではそのメンバ同士でタグを利用した簡単な情報の共有も可能です。タグとは foo=barで表されるようないわゆるキーバリューのことです。serf tagsコマンドを利用してそれぞれのメンバ上で設定します。各メンバに設定されたタグはserf membersコマンドで確認できます。タグのセット
server1 # serf tags -set foo=bar
Successfully updated agent tags
タグの確認
server2 # serf members
server1 192.168.0.101:7946 alive foo=bar
server2 192.168.0.102:7946 alive
server3 192.168.0.102:7946 alive
タグの消去
server1# serg tags —delete foo
タグもステータスと同様に全てのメンバで常に最新のタグ情報が共有されます。
Serfの障害検知とメッセージ伝達
メンバシップ管理での完全性を満たすためには、障害検知やメッセージのブロードキャストのためかかるネットワークの負荷が問題になります。そのための工夫としてSerfではゴシッププロトコル的な障害検知手法およびメッセージ伝達方法を利用しています。Serfの障害検知
図2
SWIMでの障害検知のイメージ複数の経路からメンバの死活を確認することにより確実性を高めている。
Serfのゴシッププロトコルについてのページ
SerfではSWIMという手法を利用して障害検知をしています。SWIMとは相互監視によるP2P型の障害検知方法で、次のような特徴があります。
- あるメンバの障害を残りの全ての正常なメンバが確実に検知できる。
- メンバの障害を信頼性の低いネットワークであっても誤検知しない。
- メンバの障害を速やかに有限の時間内で検知できる。
- メンバの障害検知のためのネットワーク負荷をクラスタの規模に応じて常に一定量にできる。
障害検知の仕組みは次のようになっています(図2参照)- 各メンバはお互いに常に短い周期で他のメンバの障害監視を行っている。ここではあるメンバ(D)について考える。
- メンバ(D)は自身の持っているメンバのリストからランダムに対象メンバ(T)を選択し障害を確認する。
- 対象メンバ(T)にアクセス(PING)を実行する。応答(ACK)がなかった場合すぐに障害と判断せずに、他のK個メンバ(S1...SK)に対象メンバ(T)への代替アクセスを要請(PING-REQ)する(図2ではK=3)。要請をするメンバも自身の持っているメンバのリストのなかからランダムに選択する。
- PING-REGを受け取ったメンバは代わりに対象メンバ(T)にアクセス(PING)して、その応答(ACK)があれば(D)に応答(ACK)を返す。
- (D)はタイムアウトまでにPINGあるいはPING-REQのACKが得られれば対象メンバ(B)は正常(Alive)と判断する。タイムアウトまでにACKが得られなかった場合、障害(Failed)と判断する。 障害と判断された場合、判断したメンバが全体にその旨を通知する。通知を受け取った各々のメンバは自身のもっている(T)のステータス情報を更新する。
障害検知の対象とするメンバの選択は基本的にはランダムです。ただ、SWIMではある全てのメンバが一定の時間間隔で確実に検知対象になるように、対象メンバが一周するまで同じメンバは選択しないように制限をかけて選択しているようです。メッセージ伝達
図3
各メンバ間では常時障害検知のためのパケットがランダムに行き来している。
障害検知のためのパケットにメッセージを載せることによりランダムな経路で全体にメッセージが伝達される。
SWIM では障害の検知と検知した障害の全体への通知を分けて考えています。初期のSWIMでは全体への通知はマルチキャストを利用していたようですが、確実性とネットワーク負荷の点で問題があったのでやめにしたようです。改良されたSWIMでは障害通知のパケットを障害検知のためのパケット(PING/ACK /PING-REQ)に載せて(Piggyback)伝達するようしています。
障害検知のパケットは常時それぞれのメンバからランダムな相手に対して送信されています。このパケットに便乗して通知のパケットを送信します。送られた先でさらに別の障害検知のパケットに便乗して再送信します。 このようにして、メンバからメンバにランダムに送信されていき、やがて全体に浸透します。これがいわゆるゴシッププロトコル的な方法になっています。
Serf ではこの便乗する仕組みを障害検知の結果の通知以外にも、イベントなどのメッセージの伝達する場合にも利用しています。ただ、障害検知のパケットの送信間隔がやや長く(1秒程度)これだけではメッセージの伝達速度的に問題があるので、メッセージを伝達するだけの専用のパケットを同様の方法でより短い間隔 (200ミリ秒)で送信しているようです。
障害検知のパケットがUDPなのでメッセージの伝達もUDPで実行される。
メッセージにはカウンタがついており規定の回数再送信されるそれ以上送信されなくなる。
仕様上メンバは同じメッセージを複数回受け取る可能性がある。メッセージのバッファは管理されているので同じメッセージであることは認識できる。
ゴシッププロトコルとメッセージ到達性
図4
Fanout数2、送信回数3のゴシッププロトコルのイメージ
0から3の順番でうわさ話(Gossip)のようにメッセージが伝達されていく。
伝達先の選択はランダムであるため、すでに受け取ったメッセージを再度受け取ったり、同じタイミングで2箇所からメッセージを受け取ることが発生する
ゴシッププロコトルというのはうわさ話(Gossip)のようにランダムな経路でメッセージを全体に伝達する方法です。ちょうど病気の感染が広がっていく様子とも一致することから感染症プロトコルなどとも呼ばれます。
ゴシッププロトコルの伝達性は1回の送信でランダムに選択する送信先の数(Fanout数)およびサーバからサーバへメッセージを(再)送信する回数で決まります。送信先の選択がランダムなのですべてのメンバにメッセージが伝わるかどうかは確率的にしか保証されません。ランダムに選択する送信先の数を増やしたり、再送信の回数を増やせばメッセージの伝達速度や全体に浸透する確率が高くなりますが、その分ネットワークの負荷が高くなります。
ゴシッププロコトルによるメッセージの伝達性や情報の収束性について厳密に評価するのは難しい問題のようです。ゴシッププロトコルによるメッセージの伝達を表すモデルはいくつかあるようですが、Serfでは簡単な感染症モデル(SIモデル)から導出される式を利用しているようです。Fanout数とクラスタの規模をパラメータとして充分な確率で全体に浸透するのに必要な再送信数を計算して決めています。
またSerfでは独自の実装としてゴシッププロトコルによるメッセージの伝達とは別に一定の間隔(デフォルトでは30秒)でランダムなメンバとのTCP通信よる保持しているクラスタの情報(メンバのステータスやタグ)の同期を行っています。
Serfの使い所
Serf
は通常のHAクラスタからメンバシップの管理およびサーバの障害検知の部分だけを取り出したようなツールになっています。P2P型の構成であることにより導入も簡単です。Serf自身ではなにかサービス等を提供できるわけでありませんが、システムにこれらの機能を簡単に追加することが可能です。- HAクラスタのような高度に連携したシステムでなくても、各サーバにserfエージェントを起動してサーバの死活確認に利用する。
- タグを利用して各サーバの役割や実行しているカーネルやアプリケーションのバージョン情報を全体で共有する。
Serfのメンバの追加や削除、タグの追加や変更は簡単に動的に可能です。- 仮想マシンやコンテナで構成されるクラスタのような、メンバが動的に変化するシステムでのメンバシップの管理。
- デプロイツールによってアプリケーション構成が動的に変化するシステムでの情報の共有。
などにも適していると考えられます。まとめ
Serf はクラスタの構成をサポートするためのツールです。Serfを利用することによりサーバのメンバシップの管理や障害検知が可能になります。Serfはゴ シッププロトコル的手法を障害検知およびメッセージの伝達に利用しており、それによってP2P形式のクラスタ構成ながら、ネットワーク負荷を抑え情報のす ばやい浸透が可能になっています。参考文献
SWIM: Scalable Weakly-consistent Infection-style Process Group Membership ProtocolSerfのWebページからも引用されているSWIMに関する論文です。Failure Detectionの解析的な部分は基本結果のみ引用されています。
On Scalable and Efficient Distributed Failure Detectors
SWIMのFailure Detectionに解析的な議論についての論文です。
The Mathematics of Infectious Diseases
SWIMおよびSerfで利用している感染症モデルについての解説が最初にあります。ただ、ここでは感染からの回復(R)も含めたSIRモデルになっています。
tech_ccmp at 17:00│Comments(0)│TrackBack(0)