読者です 読者をやめる 読者になる 読者になる

$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

serfとDockerでクラスタを組んでみる

operation tech

 最近Serfというツールも気になっていたので、とりあえずクラスタを組んでイベントハンドラの設定をしてみるところまでやってみました。

Serfとは

Serf is a decentralized solution for service discovery and orchestration that is lightweight, highly available, and fault tolerant.

Orchestrationという層を支援する軽いツールみたいですね。これをうまく使うことで、クラスタにjoinしたweb serverを自動的に配下に加えるHAProxyとかを実装したり出来ます。参考: Serf+HAProxyで作るAutomatic Load Balancer - Glide Note - グライドノート

今回やること

 とりあえずlocalだけでもクラスタを組めますが、最近こういう記事も書いたので、Dockerを利用しながらクラスタを組んでみます。

  • vagrant内でserf agentを立て、event handlerを設定
  • vagrant内でDockerコンテナを何個も起動して、Vagrant内で起動しているクラスタにjoin
  • joinしたらvagrant内に直接立てたserf agentが現在のmemberを出力

くらいのことをしてみます。

イメージ図
f:id:shiba_yu36:20131208163014j:plain:w400

インストール

golangで書かれてるので、バイナリを一つ落としてくるだけです。http://www.serfdom.io/intro/getting-started/install.html

まずvagrantにいれます。

$ wget https://dl.bintray.com/mitchellh/serf/0.3.0_linux_amd64.zip
$ unzip 0.3.0_linux_amd64.zip
$ cp serf /usr/local/bin/

あとserfの入ったdocker imageを作っておきます。

$ docker run -i -t ubuntu /bin/bash

# ここからdockerコンテナ内
$ apt-get install -y unzip
$ apt-get install -y wget
$ wget https://dl.bintray.com/mitchellh/serf/0.3.0_linux_amd64.zip
$ unzip 0.3.0_linux_amd64.zip
$ cp serf /usr/local/bin/
$ exit

# ここからvagrant側
$ docker ps -a
$ docker commit 467e607d9733 serf # psで出てきたCONTAINER ID指定

これで準備が出来ました。

クラスタを組む

 準備ができたので、クラスタを組みます。Run the Agent - Serf, Join a Cluster - Serfあたりを参考に。

 まずvagrant側のserfを起動します。

$ serf agent
==> Starting Serf agent...
==> Starting Serf agent RPC...
==> Serf agent running!
    Node name: 'precise64'
    Bind addr: '0.0.0.0:7946'
     RPC addr: '127.0.0.1:7373'
...

 これで7946ポートでjoinを受け付けるようになりました。ここにjoinすればこのクラスタに入れます。ではDockerコンテナでここに入ってみます。

 vagrant側でifconfigするとdocker0というnetwork interfaceがあるので、Docker側からはここにつなげば良いのでしょうか(ここきちんとは理解できてないです)。

$ ifconfig
docker0   Link encap:Ethernet  HWaddr 00:00:00:00:00:00
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::9c67:53ff:fe5f:5da9/64 Scope:Link

 なのでこのIPの7946 portにつなぐコンテナを立ちあげてみます。

$ docker run -t -d serf serf agent -join 172.17.42.1:7946
7e82c0ccd2df997bbfa0cd9c483d27bdcee31b1cd215a8ed8e28fc03f8ab5d31
$ serf members
precise64    10.0.2.15:7946    alive
7e82c0ccd2df    172.17.0.14:7946    alive

 繋げました。この調子でどんどん立ちあげます。

$ docker run -t -d serf serf agent -join 172.17.42.1:7946
$ docker run -t -d serf serf agent -join 172.17.42.1:7946
$ docker run -t -d serf serf agent -join 172.17.42.1:7946
$ docker run -t -d serf serf agent -join 172.17.42.1:7946
$ docker run -t -d serf serf agent -join 172.17.42.1:7946
$ serf members
precise64    10.0.2.15:7946    alive
7e82c0ccd2df    172.17.0.14:7946    alive
7bde1fc5768a    172.17.0.15:7946    alive
8bb05f9040ee    172.17.0.16:7946    alive
d37508949955    172.17.0.17:7946    alive
2c86f6b745a7    172.17.0.18:7946    alive
2bbdfce351a4    172.17.0.19:7946    alive

 これでとりあえずDockerの外と複数Dockerコンテナでクラスタを作ることが出来たっぽいです。簡単ですね。

vagrant側にイベントハンドラを作る

 今はとりあえずクラスタを組んだだけですが、今度はvagrant側にイベントハンドラを設置して、イベントが来たらmember一覧を表示するというだけをやってみます。Event Handlers - Serfあたりを参考に。

 serf agentにはscriptを一つだけ渡せて、SERF_EVENT環境変数にイベント名、標準入力にデータが入っているっぽいです。なので以下の様なスクリプトを作っておきます。単にイベント名とデータを出力した後、serf membersを表示するだけです。

#!/bin/bash

echo
echo "New event: ${SERF_EVENT}. Data follows..."
while read line; do
    printf "${line}\n"
done

echo "current members..."
serf members

 ではこのイベントハンドラを設定して、vagrant側のserfを起動します。

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

 ここにDockerコンテナのserfを接続してみます。

$ docker run -t -d serf serf agent -join 172.17.42.1:7946

 するとvagrant側に以下の出力が流れます。joinしたということと、現在のmemberが出力されました。

    2013/12/08 07:56:48 [DEBUG] Event 'member-join' script output:
New event: member-join. Data follows...
c1fbbfe7c9ea    172.17.0.20
current members...
precise64    10.0.2.15:7946    alive
c1fbbfe7c9ea    172.17.0.20:7946    alive

 もう一つjoinさせます。

$ docker run -t -d serf serf agent -join 172.17.42.1:7946

 するとvagrant側でまた出力が出ます。

    2013/12/08 07:58:03 [DEBUG] Event 'member-join' script output:
New event: member-join. Data follows...
0a479cdc6f47    172.17.0.21
current members...
precise64    10.0.2.15:7946    alive
c1fbbfe7c9ea    172.17.0.20:7946    alive
0a479cdc6f47    172.17.0.21:7946    alive

 このあとDockerコンテナを突然止めてみます。

$ docker stop 0a479cdc6f47a2e128d71678fd1955db2187f707612dbe86d806dda9c0ce9353

 するとvagrant側で出力が出ます。なるほど、突然止めてみるとfailedという状態になるんですね。

    2013/12/08 07:59:15 [DEBUG] Event 'member-failed' script output:
New event: member-failed. Data follows...
0a479cdc6f47    172.17.0.21
current members...
precise64    10.0.2.15:7946    alive
c1fbbfe7c9ea    172.17.0.20:7946    alive
0a479cdc6f47    172.17.0.21:7946    failed

 というわけでとりあえずハンドラを設定するというところまでやってみました。

まとめ

 とりあえずserfを使ってクラスタを組んで、イベントハンドラを設定してみるところまでやってみました。まだあんまり生産的なことは出来てませんが、いろいろおもしろいこと出来そうですね。