はじめに

Software Design 2014/09号に「Serf・Consul入門」が載っていたのでそれを参考に少し動かしてみました。また記事を書かれている @zenbutu さんのQiita上でのまとめを参考にしています。

導入

http://www.serfdom.io/downloads.html

上記から各サーバにあったものを落とす。 ここではUbuntu14.04を2台で試すので

https://dl.bintray.com/mitchellh/serf/0.6.3_linux_amd64.zip

を利用する

$ apt-get install unzip
$ wget https://dl.bintray.com/mitchellh/serf/0.6.3_linux_amd64.zip
$ unzip 0.6.3_linux_amd64.zip
$ cp ./serf /usr/bin/serf

バージョンの確認

$ serf version
Serf v0.6.3
Agent Protocol: 4 (Understands back to: 2)

起動

バックグラウンドでの起動

root@serf1:~# serf agent &
[1] 10094
root@serf1:~# ==> Starting Serf agent...
==> Starting Serf agent RPC...
==> Serf agent running!
         Node name: 'serf1'
         Bind addr: '0.0.0.0:7946'
          RPC addr: '127.0.0.1:7373'
         Encrypted: false
          Snapshot: false
           Profile: lan

==> Log data will now stream in as it occurs:

    2014/08/21 18:31:15 [INFO] agent: Serf agent starting
    2014/08/21 18:31:15 [INFO] serf: EventMemberJoin: serf1 10.114.16.197
    2014/08/21 18:31:16 [INFO] agent: Received event: member-join

エージェントの確認/クラスタの状態の確認

root@serf1:~# serf members
    2014/08/21 18:32:13 [INFO] agent.ipc: Accepted client: 127.0.0.1:44401
serf1  10.114.16.197:7946  alive

2台目のサーバで、上記のSerf導入後以下を実施

root@serf2$ serf agent &
root@serf1$ serf join 10.114.16.197 
    2014/08/21 18:35:05 [INFO] agent.ipc: Accepted client: 127.0.0.1:48145
    2014/08/21 18:35:05 [INFO] agent: joining: [10.114.16.197] replay: false
    2014/08/21 18:35:05 [INFO] serf: EventMemberJoin: serf1 10.114.16.197
    2014/08/21 18:35:05 [INFO] agent: joined: 1 nodes
Successfully joined cluster by contacting 1 nodes.
root@serf2:~#     2014/08/21 18:35:06 [INFO] agent: Received event: member-join

1台目でMemberの確認

root@serf1:~# serf members
    2014/08/21 18:36:12 [INFO] agent.ipc: Accepted client: 127.0.0.1:44421
serf1  10.114.16.197:7946  alive
serf2  10.114.16.196:7946  alive

止める

root@serf1:~# serf leave
    2014/08/21 18:44:31 [INFO] agent.ipc: Accepted client: 127.0.0.1:44457
    2014/08/21 18:44:31 [INFO] agent.ipc: Graceful leave triggered
    2014/08/21 18:44:31 [INFO] agent: requesting graceful leave from Serf
    2014/08/21 18:44:31 [INFO] serf: EventMemberLeave: serf1 10.114.16.197
    2014/08/21 18:44:31 [INFO] agent: requesting serf shutdown
    2014/08/21 18:44:31 [INFO] agent: shutdown complete
    2014/08/21 18:44:31 [WARN] agent: Serf shutdown detected, quitting
Error leaving: client closed
[1]+  終了                  serf agent

mDNSを利用したクラスタへのログイン

serfサーバをJoinするのにIPアドレスなどでしてしないでディスカバリーする。今回の環境はSoftLayerなのでマルチキャストDNS(mDNS)が通信できるためディスカバリーすることが出来ます。

先に2台とも停止しておくserf leave

root@serf1:~# ==> Starting Serf agent...
==> Starting Serf agent RPC...
==> Serf agent running!
         Node name: 'serf1'
         Bind addr: '0.0.0.0:7946'
          RPC addr: '127.0.0.1:7373'
         Encrypted: false
          Snapshot: false
           Profile: lan
      mDNS cluster: serf

==> Log data will now stream in as it occurs:

    2014/08/21 18:46:50 [INFO] agent: Serf agent starting
    2014/08/21 18:46:50 [INFO] serf: EventMemberJoin: serf1 10.114.16.197
    2014/08/21 18:46:51 [INFO] agent: joining: [10.114.16.197:7946] replay: false
    2014/08/21 18:46:51 [INFO] agent: joined: 1 nodes
    2014/08/21 18:46:51 [INFO] agent.mdns: Joined 1 hosts
    2014/08/21 18:46:51 [INFO] agent: Received event: member-join

root@serf1:~# serf members
    2014/08/21 18:47:01 [INFO] agent.ipc: Accepted client: 127.0.0.1:44467
serf1  10.114.16.197:7946  alive

次にserf2側でも実行

root@serf2:~# ==> Starting Serf agent...
==> Starting Serf agent RPC...
==> Serf agent running!
         Node name: 'serf2'
         Bind addr: '0.0.0.0:7946'
          RPC addr: '127.0.0.1:7373'
         Encrypted: false
          Snapshot: false
           Profile: lan
      mDNS cluster: serf

==> Log data will now stream in as it occurs:

    2014/08/21 18:47:23 [INFO] agent: Serf agent starting
    2014/08/21 18:47:23 [INFO] serf: EventMemberJoin: serf2 10.114.16.196
    2014/08/21 18:47:23 [INFO] agent: joining: [10.114.16.196:7946 10.114.16.197:7946] replay: false
    2014/08/21 18:47:23 [INFO] serf: EventMemberJoin: serf1 10.114.16.197
    2014/08/21 18:47:23 [INFO] agent: joined: 2 nodes
    2014/08/21 18:47:23 [INFO] agent.mdns: Joined 2 hosts
    2014/08/21 18:47:24 [INFO] agent: Received event: member-join

root@serf2:~# serf members
    2014/08/21 18:47:31 [INFO] agent.ipc: Accepted client: 127.0.0.1:48185
serf2  10.114.16.196:7946  alive
serf1  10.114.16.197:7946  alive

使い方

このserfではEventというものを使って処理ができるようになるのが特徴。その際の処理はEvent Handlerと言うものが担っている。

EventHander自体は、スクリプト等の実行コマンドで各サーバ上に事前に用意しておく必要がある。したがって複数の処理をしたい場合などはGitなど経由でダウンロードする仕組みを組み込んでいくほうが良いと思われる。

最初に各サーバに最初のEventHandlerのシェルを配置する

以下の内容で ~/event.sh を作成

event.sh
1
2
3
4
5
6
7
8
#!/bin/sh 

LOG=event.log

echo "-----" >> $LOG
hostname >> $LOG
date >> $LOG
env |grep "^SERF" >> $LOG

次に各serfを起動する。一旦serf leaveで抜けておいて各サーバで以下のコマンドを実行する。

serf1側を、-role=serverとして serf2側を -role=agent として起動します。

serf agent -event-handler=./event.sh -discover=serf -role=<ROLE> &

これにより、何らかのイベントが発行される都度に event.shが実行されます。その際の書きだされる内容は

root@serf1:~# tail -f event.log
SERF_EVENT=member-join
SERF_SELF_ROLE=
SERF_SELF_NAME=serf1
-----
serf1
2014年  8月 21日 木曜日 19:16:36 JST
SERF_EVENT=member-join
SERF_SELF_ROLE=server
SERF_TAG_ROLE=server
SERF_SELF_NAME=serf1
    2014/08/21 19:17:08 [INFO] serf: EventMemberLeave: serf2 10.114.16.196
    2014/08/21 19:17:09 [INFO] agent: Received event: member-leave
-----
serf1
2014年  8月 21日 木曜日 19:17:09 JST
SERF_EVENT=member-leave
SERF_SELF_ROLE=server
SERF_TAG_ROLE=server
SERF_SELF_NAME=serf1
    2014/08/21 19:17:27 [INFO] serf: EventMemberJoin: serf2 10.114.16.196
    2014/08/21 19:17:28 [INFO] agent: Received event: member-join
-----
serf1
2014年  8月 21日 木曜日 19:17:28 JST
SERF_EVENT=member-join
SERF_SELF_ROLE=server
SERF_TAG_ROLE=server
SERF_SELF_NAME=serf1

次に

root@serf2:~# tail -f event.log
SERF_EVENT=member-join
SERF_SELF_ROLE=
SERF_SELF_NAME=serf2
-----
serf2
2014年  8月 21日 木曜日 19:17:28 JST
SERF_EVENT=member-join
SERF_SELF_ROLE=agent
SERF_TAG_ROLE=agent
SERF_SELF_NAME=serf2

つまり、このEventHandlerのスクリプトをゴニョゴニョすると色々出来ますね。という話です。任意のイベント名を指定して実行してみます。serf2からevent TESTを実行します。

root@serf2:~# serf event TEST

この時、serf1、serf2で同時に event.sh が実行されます。すると

serf1
2014年  8月 21日 木曜日 19:27:16 JST
SERF_EVENT=user
SERF_SELF_ROLE=server
SERF_TAG_ROLE=server
SERF_USER_LTIME=3
SERF_SELF_NAME=serf1
SERF_USER_EVENT=TEST

のようなイベントな環境変数で実行されます。 これを利用して、XXXサーバ機能として Role/Tag/Serf_Nameを駆使してサーバを特定して特定の処理を実施すると良いことになりますね。

まとめ

ざっくり言えば、用意しておいスクリプトをクラスタ環境で実行することが出来る。実行はサーバが起動した際等にmember-joinした際に動くなど、複数のサーバ間で何かの処理をしたい時に同時に実行することが出来るのが面白い。

スクリプトへ渡されるものは、SERF_EVENTの場合 msmber-* は、標準入力で引き渡されます。その際にはホスト名やIPアドレスを取得することが出来ます。またEventの場合には、引数で指定したデータが渡ります。

Dockerで試す、はじめてのSerf - Qiita

によるとEventHandlerをイベント事に指定することも出来るようなので色々出来そうです。反面、スクリプトを各サーバに用意しておく必要がありこの部分をプロビジョニング時にうまいことする必要もありそうですね。

参考