【Serf】イベントハンドラを整理してみる

【Serf】イベントハンドラを整理してみる はてなブックマーク - 【Serf】イベントハンドラを整理してみる


今日も自分用の整理も兼ねて。

Serf は「イベントハンドラ」が特長とサイトに書かれていたものの、自分が検証し始めた当初は、イベントハンドラ?(゚Д゚)??という状況だたのを思い出し、改めて整理しようと思い立ちました。

なお、Serf v.0.5.0 相当です。

■イベントハンドラとは (event handler)

Serf の3つの特徴は、メンバーシップ ( membership ) と、障害検知および復旧 ( failure detection and recovery ) 、そして、カスタムイベントの伝搬 ( custom event propagation ) があげられます。

唐突に「イベント」が出てきましたが、イベントとは何でしょうか。ドキュメントによると、Serf には、Serf クラスタにかかわる挙動の事を「イベント」と呼びます。イベントには2種類あります。

  • メンバーシップに関するイベント
    例:「クラスタにメンバが加わる」「メンバが離れる」など既定のもの。
  • カスタムイベントやクエリ
    例:任意に発行するイベント名称

そして、このイベント発生に対応づけられているスクリプトを実行する事を、「イベントハンドラ」と呼びます。イベントハンドラは、Serf クラスタ全体で、同時に、一斉に実行される事が重要な点です。Serf が強力かつフレキシブルなのは、この柔軟なイベントハンドラの機能があるからとも言えるでしょう。

イベントハンドラで実行できるのは、スクリプトであったり、コマンド(またはプログラム)です。シェルスクリプトを実行させることも出来れば、Perl、Ruby 等で書かれたものを実行させることができます。たとえば、イベント発行のタイミングで、アプリケーションを実行させたり、デプロイ用のスクリプトを実行させることもできます。

■イベントの種類

イベントには、メンバーシップに関するものと、ユーザイベントとクエリの2種類が存在します。

まず、メンバーシップに関するイベントは、Serf 既定のものです。

  • member-join … メンバが加わった時
  • member-leave … メンバが離れた時
  • member-failed … 対象メンバで障害が発生した時
  • member-reap … 対象メンバの情報を削除する時
  • member-update … メンバのタグを更新する時

では、1つ1つの詳細を見ていきます。

member-join

Serf クラスタに新しいメンバが「参加」(join) した時に発生するイベントです。クラスタ全体で新しいノードが加わったことが共有されます。

左図では、node2 という名前の新しい Serf ノードが Serf クラスタに加わります。

このとき、’member-join’ のイベントハンドラを serf 起動時や設定ファイルで定義しておけば、そのタイミングで任意のコマンドを実行する事ができます。例えば、何かアプリケーションをデプロイしたり、デーモンを起動させても良いと思いますし、監視を開始することも出来るでしょう。

member-left

Serf クラスタからノードが離れた(left) 時に発生するイベントです。あくまで正常終了した場合 ( graceful shutdown ) のイベントです。

Serf クラスタ側から「正常終了」とみなされた場合、対象ノードの稼働状況チェックは行いません。そのため、Serf が起動しても、再びクラスタ参加 ( join ) を行わない限り、復帰しません。

使い道としては、ロードバランサのバランシングから外すなり、監視設定の一時停止などを行っても良いでしょう。

敢えて「正常終了」と書いたのは、異常終了があるからです。それが、member-failed 。

member-failed

member-failed は、エージェントが強制終了したり、サーバダウンでネットワークが途絶するなど、異常終了した場合に発生します。

これは member-left と違い、他のクラスタからはダウン後も、継続してサーバが復旧するかどうか定期的にチェックが実行されます(設定ファイル上の ‘reconnect_interval’ で間隔を調整します。デフォルトは 30 秒です)。

チェック時に対象ノードとの通信が復旧した場合、対象ノード上の Serf は特に参加 ( join ) を行わなくても、直ちにノードに復旧するのが特長です。

‘member-left’ で離れたノードは自動復旧しませんが、異常終了の ‘member-failed’ は自動的に復旧する、これが大きな違いです。

member-leap

’member-left’ または ‘member-failed’ によってクラスタからノードが離れた後、一定期間ノードの情報を保持し続けます。

消えるまでは、’serf members’ コマンドを実行すると、ノード上に情報が残っていることがわかります。

この、ノード情報が消えるタイミングが ‘member-reap’ です。特に明示しない場合、member-leap のタイミングは、member-left または member-failed 発生後から 24 時間です。(なお、reap は ’収穫’や’刈り取り’ の意味があります。serfdom の語源にかけているのかな)

member-reap の発生タイミングは、serf 起動時に設定ファイルで明示することで指定することが出来ます。指定が無い場合のデフォルト値は、24時間です。設定ファイルでは、以下のように指定します。

  "reconnect_timeout": "1h",
  "tombstone_timeout": "10m",

‘reconnect_timeout’ は、障害などで ‘member-failed’ となった後、どれだけユーザの情報を記憶しておくかです。一方、’tombstone_timeout’ は、通常終了による ‘member-left’ から、どれだけ情報を保持するかという違いがあります。

そのため、障害が発生した場合は、24時間情報を持ち続けるが、正常なノード終了であれば、1時間で情報を削除させるという使い分けも可能です。

さて、詳しくは、先日の記事をご覧下さい。

ところで ‘tombstone’ は、墓石とか、墓標の意味あいですね。私はここに居ません、的な時間を明示するようなものでしょうか。。

member-update

今の所、ダイナミックタグの変更時点で発生するイベントです。タグが変更されたら、そのタイミングでタグの内容を確認し、一致している場合に何かの処理を実行させることが出来ます。使いようによっては、かなり柔軟な運用が可能でしょう。

■ユーザ定義のイベントと query について

‘serf event XXX’ または ‘serf query XXX’ とコマンドを実行する事で、任意のイベントを発生する事が出来ます。それぞれイベントハンドラの指定があれば、対象のスクリプトが実行されます。

event と query の違いは、リクエストを発行したクライアントが、結果を受け取るかどうかです。query についての詳細は、こちらの先日の記事をご覧下さい

なお、serf コマンドを実行すると event や query を発行できますが、このイベントを発生させるクライアントでは serf エージェントが起動している必要はありません。serf コマンドは、デフォルトでは自分自身 ( 127.0.0.1 ) の RPC と通信してコマンドを発行します。ですが、コマンドラインで RPC サーバ及びポートを指定しておけば、外部の Serf エージェントに対して任意のイベント/クエリを発行させることが出来ます。

例:
$ serf event -rpc-addr=192.168.39.12:7373 test aaaa

■イベントとスクリプトとでデータのやりとりをするには

このイベントハンドラは環境変数を通して、スクリプトに処理を引き渡すことが出来ます。イベント毎に何を実行させるかは、シェルスクリプトであれば、環境変数 ${SERF_EVENT} に応じて、処理を分岐させる事になります。また、標準入力からは、ジョインしたノードの情報や、アップデートされたタグの情報、ユーザ event であれば、payload (引数) にあたるデータが引き渡されます。

■イベントハンドラの動作を理解するために

とりあえず、色々イベントを起こしてみるのが分かりやすいと思います。以前、github の issue にサンプルスクリプトが掲載されていました。それを、今のスクリプトにあわせて書き換えたのが、こちらになります。

ダウンロードするか、コピペのあとは、’chmod +x ./serf-event.sh’ のように実行属性を付け、

$ serf agent -log-level=debug -event-handler=./serf-event.sh

このように起動してみます。そのあとは、Serf のメンバに加わるなり、イベントを発行していくと、どのようにデータのやりとりをするか、分かりやすいと思います。

■参考

Event Handlers – Serf
http://www.serfdom.io/docs/agent/event-handlers.html