Serf Cheat Sheet 日本語版

Serf を使う皆さんの参考になりましたら。ご意見、リクエストお待ちしています :smiley: 
## これは何？
> *"Serf is a decentralized solution for cluster membership, failure detection, and orchestration. Lightweight and highly available."*
> *訳：Serfは非中央集権型のクラスタメンバ管理、障害検知、そしてオーケストレーションのためのソリューション。軽量かつ高い可用性。*

Serf ( http://www.serfdom.io/ )は、オーケストレーションと管理を行うためのツール。[Vagrant](http://www.vagrantup.com/) や [Packer](http://www.packer.io/) を製作した [Hashicorp 社](http://www.hashicorp.com/)製のツールで、Go 言語で書かれており、オープンソース(Mozilla Public license, version 2.0)で公開されている。開発は [GitHub](https://github.com/hashicorp/serf) を通してオープンに行われている。

## インストール
オフィシャルの[ダウンロードサイト](http://www.serfdom.io/downloads.html)から、対応した OS のバイナリをダウンロードし、展開する。現在対応している OS は、Mac OS X、Linux、Windows、FreeBSD、OpenBSD である。
### CentOS6/RHEL6 でのセットアップ方法

```
$ wget -O 0.6.2_linux_amd64.zip https://dl.bintray.com/mitchellh/serf/0.6.2_linux_amd64.zip
$ unzip ./0.6.2_linux_amd64.zip
# cp ./serf /usr/bin/serf
```

動作確認は、バージョン番号を確認。

```
$ serf -v
Serf v0.6.2
Agent Protocol: 4 (Understands back to: 2)
```

## Serfの3大特長
### メンバ管理
* クラスタ参加・離脱を管理
* マスターサーバが無い（非中央集権型）
* ロールとタグ機能を持つ
* クラスタをゴシップロウトコル(SWIM拡張)で構成

### 障害検知
* イベントが直ちに伝わる（100ノードには2秒以内で到達）
* イベント発生のタイミングでイベントハンドラを通してコマンドを実行できる
* 一時的に離脱したあとも、継続的にダウンしたノードを補足。回復時は自動でクラスタ復旧。

### イベント処理
* 標準イベント ... Serf のクラスタ管理のため、自動発行されるイベント（メンバ参加・離脱など）
* カスタムイベント ... 任意のタイミングで、任意の処理が可能。

## Serf の起動とクラスタ構築テスト
ここは `node1` と `node2` の2台で Serf クラスタを構築し、イベントの発生状況を見るためのチュートリアル。予め2台のサーバで Serf をセットアップする。

* node1 ... 192.168.10.1
* node2 ... 192.168.10.2

実行は、まず node1 で `serf agent` と実行するだけ。

```
[node1]$ serf agent &
````
実行すると画面に Serf のログが流れ始める。同一サーバ内からであれば `serf monitor` コマンドでも同様の内容を確認できる。

node2 でも同様に serf を起動する

```
[node2]$ serf agent &
````

この時点ではクラスタは形成されない。node2 から node1 に接続を試みるには `join`オプションを用いる。

```
[node2]$ serf join 192.168.19.1
```

この状態で、両サーバで `serf members` と実行すると、お互いをクラスタとして認識していることが確認できる。

```
$ serf  members
node1    192.168.10.1    alive
node2    192.168.10.2    alive
```

イベントを送るためには、`event` を使う。

```
[node2]$ serf event 'Hello, World'
2013/12/05 19:36:17 [INFO] agent: Received event: user-event: Hello world
```
両サーバで同時にイベントが実行される事が分かる。

障害検知の確認は、片方の serf を kill で停止する。生存側では `serf member` を実行すると `failed` と表示される。なお、kill しない場合は、`left` となる。そして、再度 serf agent を起動すると、特に join 先を明示しなくてもクラスタに復旧する。

## イベントハンドラ
Serf は以下のイベントハンドラ (event_handler) 発生をトリガとして、任意のコマンドを実行させることが出来る。イベントハンドラは2種類。

* メンバ管理： member-* ではじまる、基本的にシステムが自動発行するクラスタ管理用
* ユーザイベント；event と query はユーザが任意タイミングで発行可能

### メンバ管理（メンバーシップ）
* `member-join` ... メンバが参加
* `member-leave` ... メンバが離脱
* `member-failed` ... メンバで障害発生（通信途絶による強制離脱）
* `member-reap` ... メンバ情報抹消
* `member-update` ... メンバのタグ情報更新



なお、イベントやクエリは RPC 経由の CLI でも操作できる。外部の Serf CLI は、Serf クラスタに参加していなくてもイベントを発生可能。その場合は次のように実行する。

```
$ serf event -rpc-addr=102.168.39.3:7373 test aaa
```

また、イベントのデータを取得するには、環境変数 ${SERF_EVENT}を使って処理を分岐させる。


## エージェント起動時オプション

### 設定方法
`serf` コマンドの引数で指定するか、JSON 形式の設定ファイルを用いてエージェントの振る舞いを設定する。たとえばホスト名を設定するとき、コマンドラインでの指定は、次のようにする。

```
$ serf agent -name=node1
```

あるいは、JSON 形式のファイルでも指定可能。

```lang:host.json
{
    "node_name": "node1"
}
```

Serf の起動時には、JSON ファイルを `-config-file=` で指定する。

```
$ serf agent -config-file=./host.json
```
どちらも実行結果は同一である。
また、`-config-file=` を指定する代わりに、ディレクトリを `-config-dir=` で指定することも可能。この場合、対象ディレクトリ内の `.json` 形式のファイルのみ、設定ファイルとして読み込む。

### 優先度
設定ファイルの優先度は、
1. コマンドラインの後方で指定したオプション
2. コマンドラインの前方で指定したオプション
3. 設定ファイルのオプション
4. ディレクトリ内のファイルはアルファベット順
で決定される。
### 設定の反映
設定を反映するためには Serf エージェントを再起動する。kill すると障害検知されてしまうので、通常は `-HUP` または `-1` を Serf の PID に対して実行する。

```
$ killall -HUP serf
$ killall -1 serf
````

## オプション一覧
書式：**ファイルのオプション** `エージェント(CLI)のオプション`

* **node_name** `-node` ... ノード名称を指定。serf members 等で参照
* **tags** `-tag` ... ノードのタグを指定。タグは `key=value`形式
* **bind** `-bind` ... Serf エージェントが内部通信で使用する IP アドレス割り当て
* **interface** `-iface` ... 複数のネットワークインターフェース混在時に明示可能
* **advertize** `-advertize` ... 自身のノードの IP アドレスを明示。bind と違い、実 IP アドレスの必要がない
* **discover** `-discover` ... mDNS (Multicast DNS) 利用可能な環境で、クラスタを自動検出
* **encrypt_key** `-encrypt` ... 暗号化秘密鍵を指定。鍵生成は `serf keygen`
* **leg_level** `-log-level` ... エージェントが表示するログレベルの指定。`trace` `debug` `info` `warn` `error` の5種
* **profle** `profile` ... Serf ノード間の障害検出用に使用。チェックのタイミングを変更。通常デフォルトのまま
* **protocol** `-protocol` ... 新旧のバージョンが混在する環境で、プロトコルのバージョンを明示。
* **rpc_addr** , **rpc_auth** `-rpc-addr` ... 通常はポート 7373 をバインドするが、変更可能
* **event_handlers** `-event-handler` ... イベント発生時に実行するコマンドを明示
* **start_join** `-join` ... agent 起動時のジョイン先を明示。複数指定可
* **replay_on_join** `-replay` ... start_join と同様の機能だが、過去のイベントも遡って取得する
* **snapshot_path** `-snapshot` ... スナップショットは停止後の復旧時、過去イベントを重複受診しないようポインタを指定
* **leave_on_terminal** ... agent を `kill -TERM` か `kill -15` シグナルで停止したときの振る舞い。標準は `false`。このオプションを `true` にすると、停止時に leave 扱いとなり、障害発生時もステータスを `left`にする
* **skip_leave_on_interrupt** ... Serf エージェント稼働時 `Ctrl+C` で中断したときの振る舞い。デフォルトは `false` で、中断すると `left` になる。`true` にすると、中断時の処理は `failed` 扱いになる
* **reconnect_interval** ... エージェントの fail 検出後、何秒毎に復帰したか確認する
* **reconnect_timeout** ... failed になってから、復帰を諦めるまでの時間（デフォルトは 24h）で、経過すると `member-reap` イベントが自動発行
* **tombstone_timeout** ... left してから情報を保持する時間。reconnect_timeout と違い、復旧するかどうかのチェックは行わない
* **disable_name_resolution** ... 名前解決を行わない

オプション詳細については、別エントリを参照のこと。

* Serf設定オプションまとめ
 * http://pocketstudio.jp/log3/2014/03/29/serf_configuration_quick_guide/


設定ファイルサンプル

```lang./etc/serf.json
{
  "node_name": "miku3",
  "tags": {
    "role": "develop",
    "network": "local"
  },
  "interface": "eth1",
  "discover": "mikusan",

  "encrypt_key": "o6Md8LBVhwPi2UnbJBAwNA==",
  "log_level": "debug",

  "leave_on_terminate": true,
  "skip_leave_on_interrupt": true,

  "reconnect_interval": "5s",
  "reconnect_timeout": "30m",
  "tombstone_timeout": "30m",

  "event_handlers": [
    "./event.sh",
    "user:deploy=./deploy.sh"
  ]
}
```



## 参考
* http://www.serfdom.io ... 本家ドキュメント
* [Serf 虎の巻](http://deeeet.com/writing/2014/03/23/serf-basic/) ... @deeeet さんの素晴らしいまとめ