はじめに
参考
- serf
- docker
- serfとDockerでクラスタを組んでみる
- SerfでCDP実装 ~ HA-NAT編|アドカレ2013 : CFn #11
- メッセージングツールSerfをEC2で使ってみる
- Serf+HAProxyで作るAutomatic Load Balancer
- Serf を使ってみた
- 【Serf】v0.2.0 へのバージョンアップと、変わった所を確認してみた
- Serfが面白いと俺の中で話題にwwwwww
構築のイメージ
構築のイメージは以下のような感じ。
ということで、早速、やったみよー!
準備
ホスト側にも serf をインストール
こちらを参考にさせて頂いて、VirtualBox
内の Ubuntu Server 13.04
に serf
をインストール。インストールの手順としてはとても簡単。
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
を起動する。
serf agent &
serf 入りのコンテナを作る
以下のような Dockerfile
を使ってコンテナを作成。
FROM inokappa/wheezy-7.2-basic RUN apt-get update RUN apt-get -y install openssh-server RUN apt-get -y install build-essential wget curl unzip RUN cd /tmp && wget https://dl.bintray.com/mitchellh/serf/0.3.0_linux_amd64.zip && unzip 0.3.0_linux_amd64.zip RUN cp /tmp/serf /usr/local/bin/ RUN mkdir -p /var/run/sshd RUN useradd -d /home/serf -m -s /bin/bash serf RUN echo serf:serf | chpasswd RUN echo 'serf ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
FROM
には inokappa/wheezy-7.2-basic を指定しているが、適宜、適当なコンテナに読み替えても問題無いかと思う(Debian
とか Ubuntu
であればイケるはず!)
docker build -t inokappa/wheezy-serf .
コンテナを起動して serf を起動する
以下のようにして serf
入りのコンテナを起動して ssh
を使ってログインする。
container=`sudo docker run -t -d -p 22 inokappa/wheezy-serf /usr/sbin/sshd -D` ssh serf@`sudo docker inspect $container | jq -a '.[].NetworkSettings.IPAddress' | cut -d\" -f2`
ssh
でログインしたら以下のように serf
を起動してクラスタにジョインする。
serf agent -join 172.17.42.1:7946
172.17.42.1
は docker
コンテナのデフォルトゲートウェイであると同時にホストの docker0
の IP
アドレスとなる。
クラスタの状況を確認してみる
serf members
を使ってクラスタの状態を確認する。
serf members
以下のような出力が得られる。
2013/12/22 13:21:04 [INFO] agent.ipc: Accepted client: 127.0.0.1:55332 ubuntu1304 172.16.80.60:7946 alive 1ebce8b3a058 172.17.0.23:7946 alive 773969c90968 172.17.0.25:7946 alive
おお、一応、クラスタ構成されているっぽい。
クラスタの一台を停止させてみる
コンテナを停止してみる。
sudo docker stop ${コンテナ ID}
他のクラスタ内のホストに以下のようなログが出力されると共に serf members
でも該当のホストが fail
となる。
2013/12/22 13:24:01 [INFO] serf: EventMemberFailed: 773969c90968 172.17.0.25 2013/12/22 13:24:02 [INFO] agent: Received event: member-failed
serf members
は以下のような出力となる。
ubuntu1304 172.16.80.60:7946 alive 1ebce8b3a058 172.17.0.23:7946 alive 773969c90968 172.17.0.25:7946 failed
イベントハンドラを試してみる
イベントタイプ
serf
のセールスポイントの一つ(だと思っている)、クラスタ内のノードからイベントを検知した時点で任意のスクリプトを実行することが出来る。
イベントタイプは以下の通り。(ドキュメントをなんちゃって翻訳)
イベント | 詳細 |
---|---|
member-join | クラスター内にメンバーがジョインした場合 |
member-leave | クラスター内のメンバーが外れた場合 |
member-failed | クラスタ内のメンバーに通信障害が発生した場合(ping のレスポンスが内場合) |
user | ユーザーが任意のタイミング |
試す準備
member-failed
が発生したら該当するコンテナを停止して新しいコンテナを起動してジョインさせるというハンドラスクリプト(handler.sh
)を以下のように書いてみた。
#!/bin/sh CONTAINER_IMAGE="inokappa/wheezy-serf" CONTAINER=`serf members | grep failed | awk '{print $1}'` echo ${CONTAINER} sudo docker stop ${CONTAINER} sudo docker run -d -t ${CONTAINER_IMAGE} serf agent -join 172.17.42.1:7946
予め sudo docker
コマンドをパスワード入力無しで動作させられるように /etc/sudoers
に以下を設定しておく。
user ALL=(ALL) NOPASSWD:/usr/bin/docker*
また、serf
のコマンドライン引数として -event-handler=
オプションがあるが member-failed
だけをどうやって引数として渡すかが解らなかった(以下、追記)ので serf.conf
というファイルを下記のように作成し -config-file
で serf.conf
を指定して起動することで対応した。 (形式は JSON
形式となっている)
{ "role": "test-cluster", "event_handlers": [ "member-failed=./handler.sh" ] }
※追記
-event-handler=
に member-failed
等のサブイベントを指定する場合には以下のようにすると良い。
serf agent -log-level=debug -event-handler=member-failed=./handler.sh
実際に試す
ホスト側(Ubuntu Server 13.04
)で以下のように serf
を起動する。
serf agent -log-level=debug -config-file=serf.conf
次に docker
コンテナを下記のように起動する。
sudo docker run -d -t inokappa/wheezy-serf serf agent -join 172.17.42.1:7946
serf member
でメンツを確認する。
ubuntu1304 172.16.80.60:7946 alive test-cluster 9eb01f63d9bc 172.17.0.32:7946 alive
起動したコンテナを停止してみる。
sudo docker stop 9eb01f63d9bc
停止後、暫くするとホスト側(Ubuntu Server 13.04
)の標準出力に以下のようなメッセージが表示される。
2013/12/22 17:36:53 [INFO] agent.ipc: Accepted client: 127.0.0.1:59413 2013/12/22 17:36:56 [INFO] serf: EventMemberFailed: 9eb01f63d9bc 172.17.0.32 2013/12/22 17:36:57 [INFO] agent: Received event: member-failed 2013/12/22 17:36:57 [INFO] agent.ipc: Accepted client: 127.0.0.1:59414 2013/12/22 17:37:12 [DEBUG] Event 'member-failed' script output: 9eb01f63d9bc 9eb01f63d9bc c2683c387161546136ac53e51b77998e6097506693317f3748ba70d426f1e530 2013/12/22 17:37:12 [INFO] Responding to push/pull sync with: 172.17.0.33:44921 2013/12/22 17:37:12 [INFO] serf: EventMemberJoin: c2683c387161 172.17.0.33 2013/12/22 17:37:12 [DEBUG] serf-delegate: messageJoinType: c2683c387161 2013/12/22 17:37:12 [DEBUG] serf-delegate: messageJoinType: c2683c387161 2013/12/22 17:37:12 [DEBUG] serf-delegate: messageJoinType: c2683c387161 2013/12/22 17:37:12 [DEBUG] serf-delegate: messageJoinType: c2683c387161 2013/12/22 17:37:12 [INFO] Initiating push/pull sync with: 172.17.0.33:7946 2013/12/22 17:37:13 [INFO] agent: Received event: member-join 2013/12/22 17:37:21 [DEBUG] serf: forgoing reconnect for random throttling 2013/12/22 17:37:22 [INFO] agent.ipc: Accepted client: 127.0.0.1:33185 2013/12/22 17:37:42 [INFO] Initiating push/pull sync with: 172.17.0.33:7946
改めて serf member
メンツを確認してみる。
ubuntu1304 172.16.80.60:7946 alive test-cluster 9eb01f63d9bc 172.17.0.32:7946 failed c2683c387161 172.17.0.33:7946 alive
おお、新しいコンテナ(c2683c387161
) がメンバーとしてジョインしている。でも、failed
となったコンテナが member
一覧から消えてしまわないのはナゼなのか(そもそも消えるという概念がないのか)、まだまだ serf
は奥深い。(以下、追記(2))
※追記(2)
ドキュメントに一部、エージェントが停止した際のことが書かれているのでざっくり意訳。
An agent can be stoped in two ways: gracefully or forcefully. To gracefully halt an agent, send the process an interrupt signal, which is usually Ctrl-C from a terminal. When gracefully exiting, the agent first notifies the cluster it intends to leave the cluster. This way, other cluster members notify the cluster that the node has left.
エージェントを停止する方法は gracefully
と forcefully
の二つの方法がありまっせ。通常ターミナル上から Ctrl-c
を送るとプロセスは interrupt signal
をエージェントに送る。gracefully
にエージェントが停止する際には最初にクラスタに対して「すまん、帰る(intends to leave)」と通知する。その他のクラスタメンバーにはエージェントが停止したノードが去りました(node has left)と通知される。
Alternatively, you can force kill the agent by sending it a kill signal. When force killed, the agent ends immediately. The rest of the cluster will eventually (usually within seconds) detect that the node has died and will notify the cluster that the node has failed.
あるいは(意訳:ちなみに)、エージェントを kill
シグナルで強制的に kill
することが出来る。強制的に kill
した場合にはすぐにエージェントが停止する。残されたクラスターのメンバー達はエージェントが停止したことを(ノードがお亡くなりになったと)検出し、クラスター内にノードは failed
だと通知する。
と書かれているので、ノード内の serf
エージェントの停止の仕方次第(強制停止 or serf leave
)でクラスタ内に通知される情報は異なる(left
or failed
)ものの、一旦、メンバーシップを組んでしまうとお亡くなりになったノードも残り続ける(serf members
に表示される)のが正しい動きのようだ。
最後に
serf
初めてちゃんと使ってみたけど面白かったserf
とdocker
の組み合わせで自立するインフラがマジで作れるような気がする- 既に先人たちが道を切り開いて下さっているので意外にサクッと試すことが出来た
- ただし、今回は「使ってみた」だけなので、もう少し、深堀して動作等を確認していきたい