コンテナ/クラウド環境におけるSerfを使った構成管理 3ページ

Serfによるクラスタの管理

 Serfでは、「serf members」コマンドでクラスタに参加しているノードの情報を確認できる。たとえば以下の例では、「centos11」というノード(192.168.1.11)と、「centos10」というノード「192.168.1.10」がクラスタに参加していることが分かる。

$ serf members
centos11  192.168.1.11:7946  alive
centos10  192.168.1.10:7946  alive

 なお、ここで表示される最後のカラムはそのノードの状態を示しており、「alive」はそのノードが正常稼動していることを意味している。また、クラスタに参加した後にSerfエージェントが正常終了されたノードについては「left」、何らかの問題でSerfエージェントとの通信ができなくなっているノードについては「failed」と表示される。

クラスタ内で何らかのイベントが発生した場合に実行されるイベントハンドラ

 Serfでは、クラスタへのノードの加入や脱退、停止、状態の更新といったイベントの発生時に、あらかじめ指定しておいたコマンドを実行する機能が用意されている。実行するコマンドは、「node agent」コマンドの「-event-handler=<コマンド>」オプションで指定する。これはエージェントの起動時に指定しておく必要がある点に注意が必要だ。

 たとえばイベントの実行時に「/etc/serf/handler.sh」というスクリプトを実行するには、エージェントの起動時に以下のようなオプションを追加すれば良い。

-event-handler=/etc/serf/handler.sh

 コマンドの実行時には、「SERF_EVENT」という環境変数に発生したイベント名が格納されるので、スクリプト内ではこれをチェックして対応する処理を実行させることになる。イベント名としては表1のものが定義されている。

表1 Serfで定義されているイベント名
イベント名説明
member-joinクラスタに新たなメンバーが追加された
member-leaveクラスタからメンバーが脱退した
member-failedクラスタのメンバーが反応しない
member-updateクラスタメンバーの状態が更新された
member-reapクラスタのメンバーが反応せず、かつその後一定のタイムアウト時間が経過した
userユーザー定義のイベント
queryユーザー定義のクエリ

 これを利用して、たとえばクラスタにメンバーが追加された場合に特定の処理を実行する、といったことが実現できる。

ユーザー定義イベント/クエリを使う

 「ユーザー定義のイベント」は、ユーザーが任意のタイミングで発生させられるイベントだ。イベントを発生させるには、「serf event <イベント名> <ペイロード>」コマンドを使用する。この場合、実行されたコマンド(イベントハンドラ)では「SERF_USER_EVENT」環境変数で指定されたイベント名を参照でき、またペイロードとして指定されたデータは標準入力から取得できる。

 また、ユーザー定義のイベントと似たものとして「ユーザー定義のクエリ」がある。こちらは「serf query <クエリ名> <ペイロード>」で発生させることができる。ユーザー定義のイベントと異なるのは、各ノード上での実行結果(標準出力への出力)が「serf query」コマンドの実行側で表示される点だ。

 そのほか、クエリやイベントの実行時には環境変数経由で次のような情報を取得できる。

表2 Serfのイベントハンドラで利用できる環境変数
イベント名説明
SERF_EVENTイベント種別
SERF_SELF_NAMEイベントハンドラを実行するノードの名前
SERF_SELF_ROLEイベントハンドラを実行するノードのロール
SERF_TAG_${TAG}イベントハンドラを実行するノードに指定されたタグ
SERF_USER_EVENTユーザーイベント名
SERF_USER_LTIMEイベント発生時のタイムスタンプ
SERF_QUERY_NAMEユーザークエリ名
SERF_QUERY_LTIMEクエリ発生時のタイムスタンプ

 たとえばイベントハンドラとして「/etc/serf/handler.sh」というシェルスクリプトを指定しておき、ここに次のような内容を記述しておけば、「yum_update」というクエリを実行することで各ノードで一斉に「yum update -y」コマンドを実行することができる。

#!/bin/sh

# ↓クエリイベントによってイベントハンドラが実行されたかどうかをチェック
if [ "$SERF_EVENT" == "query" ]; then
        # ↓クエリ名をチェック
        if [ "$SERF_QUERY_NAME" == "yum_update" ]; then
                # ↓条件に合致していればコマンドを実行
                yum update -y
        fi
fi

 実際に「serf query」コマンドでこのクエリを実行した場合の出力結果は以下のようになる。

# serf query yum_update
Query 'yum_update' dispatched
Ack from 'centos10'
Ack from 'centos11'
Response from 'centos10': Loaded plugins: fastestmirror, priorities
Loading mirror speeds from cached hostfile
No packages marked for update
Response from 'centos11': Loaded plugins: fastestmirror, priorities
Loading mirror speeds from cached hostfile
No packages marked for update
Total Acks: 2
Total Responses: 2

 ここでは、「Response from ‘centos10’」以下で「centos10」ホストでの実行結果が、「Response from ‘centos11’:」以下で「centos11」ホストでの実行結果が表示されている。

シンプルなツールだからこそ使い方に工夫が必要

 Serfの備える機能はシンプルであるため、使う側で色々と工夫する必要があるが、その代わりさまざまな機能を実現できる。たとえばイベントハンドラ内でユーザーイベントに応じてサーバーの設定をリロード/変更/再起動するような記述を行うことで、クラスタ内のサーバーの設定をまとめて変更する、といった処理を実現できる。

 また、バイナリファイル1つを配置するだけで利用できるため、コンテナとの相性も良い。すでに稼動しているホストのIPアドレスが分からない状態でも、簡単にクラスタにホストを追加できるというメリットもある。そのため特に有用なのが、SerfとDockerクラスタの組み合わせだ。そこで次回は、SerfとDockerクラスタを組み合わせ、さまざまな処理を自動化する例を紹介する。