Serf ( http://serfdom.io/ ) の agent 設定オプションを整理しました。
※ 現行のバイナリ配付版 version 0.5.0 に対応。
まとめた動機は、いくつかあります。まずは、Serf に興味を持たれる方が増えてきたので、設定時の参考になるように。自分自身、設定を忘れてしまうこともあり、再整理しなくてはと思っていました(去年スライド公開用に整理した時に比べ、使えるオプションが増えています)。
そして、tcnksm @deeet さんの Serf 虎の巻 (これは素晴らしいまとめ) に触発され、自分の手許に散らばっているメモを整理しようと思い立ちました。
当記事は、オフィシャルのドキュメント、SERF AGENT Configuration の内容に準じています。
■ Serf エージェントのオプション設定は2種類
エージェントの設定オプションは、コマンドラインの引数か、設定ファイル(JSON形式)を使って指定します。動作検証や、デバッグの時は、コマンドラインで指定したほうが楽な場合があります。例えばノードのホスト名を指定するときは、次のように実行します。
$ serf agent -name=node1
一方で、指定するオプションが増えてくると、都度実行するのは大変。それに、複数のサーバで同一の設定を行いたい時は、外部の設定ファイルを使ったほうが便利でしょう。serf のオプションで ‘-config-file’ を指定します。ファイルが /etc/serf/serf.json の場合、
$ serf agent -config-file=/etc/serf/serf.json
このような指定をします。serf.json の中身は、次のように JSON 形式で記述します。
{ "node_name": "node1" }
“node_name”が、エージェントのオプション ‘-node=’ に相当します。
基本的にエージェントのオプションと、設定ファイルで指定するオプション名称は同じです。ですが ‘node_name’ のように、稀に一致しないものがあります。また、エージェントでは指定できない項目も、設定ファイルで指定可能な場合があります( reconnect_interval など )。運用にあたっては、デフォルト値は設定ファイルに記述するようにし、サーバ毎に変わる項目をエージェントで指定すると分かりやすいと思います。
個人的には、Serf に慣れるまでは agent のオプションで指定するのがオススメと思います。ファイルを編集しなくても、すぐに設定反映を確認できるからです。そのうち、都度入力が面倒になってきたり、実際に Serf を使うときが、設定ファイルに移行するタイミングではないでしょうか。
ちなみに、自分の場合は、以下のファイルをテンプレート的に使っています。
{ "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" ] }
指定項目の詳細については、下の方で改めて説明します。
■複数の設定ファイルの読み込み
いくつもの設定ファイルを読み込むには、’-config-file=’ を複数指定します。
$ serf agent -config-file=./1st.json -config=file=./2nd.json
あるいは、指定ディレクトリ内のファイルを一括指定できます。例えば /etc/serf/conf.d/ の中に設定ファイルがある場合は、’-config-dir=’ を使います。
$ serf agent -config-dir=/etc/serf/conf.d/
これで対象ディレクトリにある設定ファイルが読み込まれます。ただし、ディレクトリ指定の場合、ファイル拡張子が「.json」でないと読み込まれないので注意が必要です。
■ 設定ファイル読み込みの優先度
エージェント起動時のオプションと、設定ファイルでの指定は同時に行えます。しかし、コマンドラインで指定したオプションの優先度が高い事に注意が必要です。
- 後方で指定したオプション > 先方で指定したオプション
例:’-node=web1 -node=web2′ と指定した場合 ‘-node=web2’ が適用 - コマンドラインのオプション > 設定ファイルのオプション
例:serf agent -node=xxx のほうが、ファイルの ‘node_name’ より優先 - ディレクトリ内にあるファイル群の優先度は、アルファベット順(ls -l の順)に適用
Serf は、引数で記述した順番でオプションを読み込みます。同じオプションの指定がある場合、後方で指定したもののほうが、設定が上書き(優先)されます。ただし、ファイルよりも、コマンドラインでのオプションのほうが優先されます。
■ 設定ファイルの再読込
設定ファイルを編集したあと、設定を反映するために Serf エージェントの再起動を行う必要があります。Serf の PID を kill して再起動する方法もありますが、通常は -HUP (-1) シグナルを送るだけで、設定は再読込されます。
$ killall -HUP serf $ killall -1 serf
Serf のオプション群 ’ファイルのオプション’ ( -エージェントのオプション )
■ ‘node_name’ ( -node )
Serf のノード名称を指定します。’serf members’ コマンドで実行時にも表示されます。
$ serf -node=miku3
設定ファイルでは、’node_name’ に変わります。
"node_name": "miku3"
注意点としては、ノード名が重複してはいけません。仮に名称が重複した場合、Serf のクラスタに同じ名称では参加出来ません。設定ファイルをコピーするときは注意が必要です。あるいは、名称はエージェント起動時のオプションとして、動的に指定する方法も良いかもしれません。
■ ‘tags’ ( -tag )
ノードに対してタグを設定できます。タグは、’key=value’ 形式で設定でき、1つのノードに複数の指定が可能です。例えば ‘role=web’ というタグを設定したい場合、
$ serf agent -tag role=web
複数のタグを指定したい場合は ‘-tag’ オプションを並べます。
$ serf agent -tag role=web -tag network=local
設定ファイルでは、’tags’ として記述します。
"tags": { "role": "web", }
複数ある場合は、次のように記述します。
"tags": { "role": "web", "network": "local" }
なお、’tags’ は v.0.4.0 からサポートされました。従来の ‘role’ での指定に相当します。ですが、’role’ は廃止予定なので、今後は ‘tags’ を使うべきでしょう。
■ ‘bind’ ( -bind )
Serf エージェントがバインドし、内部通信で使用する IP アドレスを指定します。通常は指定の必要はありませんが、複数 IP アドレスが同一のネットワーク・インターフェースに割り当てられている場合など、明示する必要がある場合に使用します。
$ serf -bind=192.168.39.3
利用出来ない IP アドレスを指定しても、エラー ‘interface xxxx has no xxx.xxx.xxx.xxx address’ が出て Serf は起動できません。
設定ファイルでは、次のように指定します。
"bind": "192.168.39.3"
‘bind’ は ‘interface’ と似ていますが、IP アドレスを明示できます。シンプルな環境であれば、’interface’ で利用するインターフェースを指定する方が、IP アドレスを確認・指定しなくてよいので楽ですね。
■ ‘interface’ ( -iface)
複数のネットワークを扱う環境では、指定すると便利なオプションです。 (後述の ‘-discover’ とセットで使うと、エージェント起動時、ノードのジョイン先を明示する必要がありません)。使用頻度は高めです。
インターフェースは、eth0 と eth1 が混在しているような環境で、Serf が使うものを指定します。
$ serf agent -iface=eth1
設定ファイルでは、次のようにします。
"interface": "eth1"
■ ‘advertise’ ( -advertise )
Serf クラスタに対して、自分が使用する IP アドレスを明示するときに使います。’bind’ と似ていますが、’advertise’ は、他のノードに対して宣言するものです。複数の IP アドレスを扱う環境や、NAT 変換などを行う場合で使用します。’bind’ と違い、インターフェースがここで宣言する IP アドレスが割り当てられなくても構いません。
$ serf agent -advertise=192.168.39.1
設定ファイルでは、次のように指定します。
"advertise": "192.168.39.1"
■ ‘discover’ ( -discover )
同一ネットワーク上で、指定された名称のクラスタに対して、Serf agent は自動的に参加します。mDNS ( Multicast DNS ) の仕組みを使うため、マルチキャストをサポートしているネットワークで利用出来ます。
‘discover’ を使えば、Serf クラスタ参加時、ジョイン先を明示する必要がありません。通常、エージェント起動時は、クラスタに参加すため ‘start_join’ で参加先を明示しなくてはいけません。ジョイン先は、複数の指定が可能といえ、SPOF になり得る場所でした。
なお、同一ネットワーク内であっても、クラスタ名称によって参加先を識別できます。’discover’ で指定したクラスタ名に対してのみ、自動的にジョインします。
なお、複数のネットワーク・インターフェースを持つ環境では、-iface と同時に使います。
$ serf agent -iface=eth1 -discover=mikusan
この設定は、設定ファイル上では、次のようになります。
"interface": "eth1", "discover": "mikusan"
■ ‘encrypt_key’ ( -encrypt )
Serf のネットワーク間通信に用いる暗号向け秘密鍵を指定します(base64でエンコードされた 16 byte 文字列)。このオプションを指定した場合、同一の鍵を使うエージェント間でのみ通信が行えます。マルチテナント環境など、複数の利用者が混在するネットワークでは指定すべきでしょう(逆に言うと、指定が無いと、誰でも自由に Serf クラスタに参加できてしまいます)。
オプションを使う前には、 ‘serf keygen’ コマンドで鍵を生成します。
$ serf keygen 9cYOq4DpTkuy7ld9p9kNUA==
鍵を生成した後は、コマンドラインで次のように指定します。
$ serf agent -encrypt=9cYOq4DpTkuy7ld9p9kNUA==
設定ファイルでは、次のように指定します。
"encrypt_key": "9cYOq4DpTkuy7ld9p9kNUA=="
■ ‘log_level’ ( -log-level )
Serf エージェント起動後、エージェントが表示するログレベルを指定します。標準は “info” です。利用可能なログレベルは、”trace”、”debug”、”info”、”warn”、”err” です。”trace” はデータ量が一番多く、開発者向け。通常の動作チェックは “debug” で行い、運用後のエラー確認は “err” にして表示項目を絞ることも出来ます。
$ serf agent -log-level=debug
設定ファイルでは、次のように指定します。
"log_level": "debug"
なお、ここで指定するのは、あくまでエージェント実行時に表示されるログのレベルです。指定したオプションにかかわらず ‘serf monitor’ コマンドを実行すると、ログを参照する事が可能です。
■ ‘profile’ ( -profile )
Serf ノード間の障害検出用に使います。通常、Serf は LAN 内で利用することが想定されています。もしインターネット ( WAN ) を経由する場合、障害を誤検出してしまう恐れがあります。’lan’ や ‘local’ では問題が出る環境では、’wan’ をオプションで指定します。使用頻度は低いです。
エージェント起動時、’lan’ を明示したい場合は、
$ serf agent -profile=lan
設定ファイルでは、次のようにします。
"profile": "lan"
■ ‘protocol’ ( -protocol )
Serf が使うプロトコルのバージョンを明示します。Serf エージェントの番号によって、利用可能なプロトコルのバージョンが異なります。複数のバージョンの Serf が混在する環境で使用します。利用可能なバージョンは serf -v で確認します。
$ serf -v Serf v0.5.0 Agent Protocol: 4 (Understands back to: 2)
現在は、バージョン 2 ~ 4 を指定できます。
バージョン 3 で起動したい場合は、
$ serf agent -protocol=3
設定ファイルでは、次のように記述します。
"protocol": "3"
■ ‘rpc_addr’ ( -rpc-addr ) と ‘rpc_auth’
Serf エージェントは、RPC サーバとして ‘127.0.0.1:7373′ をバインドします。明示的にポート番号を変更したいときに、指定するオプションです。’rpc_auth’ は、RPC 接続時のトークンを指定します。リクエスト時に指定したトークンを使用しないと、接続できないようにします。具体的な使い方については、こちらにあるリモート経由のタグ操作のエントリをご覧下さい。
■ ‘event_handlers’ ( -event-handler )
Serf のイベント発生時、自動的に実行するコマンドを指定することができ、これをイベントハンドラと呼びます。イベントハンドラには、以下の種類があります。イベントハンドラの詳細については、こちらのエントリをご覧下さい。
- member-join … メンバに加わった時
- member-leave … メンバから離れた時(agent がノード離脱を宣言するとき)
- member-failed … メンバがダウン時(agent間の通信が途絶したとき)
- member-update … タグの追加/削除が行われた時
- member-reap … メンバのリープ時(デフォルトは、fail/leaveから24時間後)
- user … ユーザイベント発行時
- query … クエリ発行時
たとえば、全てのイベント発生時、常に ./foo.sh を実行するスクリプトとして指定する場合、
$ serf agent -event-handler=./foo.sh
設定ファイルでは、次のように指定します。
"event_handlers": [ "./event.sh" ]
イベントハンドラは、複数指定可能です。例えば、イベント発生時、常に date コマンドを実行し、user イベント発生時は、uptime を実行するには、次のように指定します。
$ serf agent -config-file=./serf.conf -event-handler=date -event-handler=user=uptime
エージェント起動後、’serf event’ と、ユーザイベントを実行すると、その時は uptime も実行されます。設定ファイルでの記述は、次のようになります。
"event_handlers": [ "date", "user=uptime" ]
なお、イベントハンドラは複数指定できます。次の例は、ユーザイベント uptime 時には uptime コマンドを、ユーザイベント who 時には who コマンドを実行する例です。
$ serf agent -config-file=./serf.conf -event-handler=date \ -event-handler=user:uptime=uptime -event-handler=user:who=who
設定ファイルでは、次のように指定します。
"event_handlers": [ "date", "user:uptime=uptime", "user:who=who" ]
イベントハンドラによって様々な処理を行う時は、このようにファイル上で記述するほうが分かりやすく、管理もしやすくなります。
■ ‘start_join’ ( -join )
Serf エージェント起動時、自動的に join するノードを明示します。コマンドラインでは
$ serf agent -join=192.168.39.11
このように指定します。設定ファイルでは、以下の様に複数指定することも出来ます。
"start_join": [ "192.168.39.11", "192.168.39.12" ]
しかし、このオプションも ‘interface’ や ‘discovery’ が使える環境であれば、このように起動にジョインするノードを明示する必要はありません。Serf クラスタの冗長性を考える上で、以前は重要でしたが、新しいオプションのお陰で利用頻度は下がっています。
■ ‘replay_on_join’ ( -replay )
‘start_join’ や ‘-join’ と同じような機能です。ただし、クラスタへのジョイン時に、過去のイベントも遡って実行します。何らかの処理を繰り返して行いたい場合(例:環境の再構築コマンド投入など、システム全体に一貫性を持たせる必要がある処理)に有用と思われます。
■ ‘snapshot_path’ ( -snapshot )
スナップショットのパス(ファイル名)を指定します。スナップショットは、Serf の停止・再起動時に、既に処理されたイベントを無視できるよう、どこまで処理が進行したかを記録しておくファイルです。ファイル内容はテキスト形式で、tail や cat 等で開くことが出来ます。なお、対象ファイルは serf 起動時のユーザ権限で編集できるようにする必要があります。
$ cat snapshot.dat alive: miku1 192.168.39.11:7946 clock: 1 alive: miku2 192.168.39.12:7946 clock: 2
起動時のオプションは、次のように指定します。
$ serf agent -snapshot=./snapshot.dat
設定ファイルでは、次のように指定します。
"snapshot_path": "/opt/serf/snapshot.dat"
■ ‘leave_on_terminate’
Serf エージェントを kill -TERM / kill -15 シグナル等で停止したとき、Serf クラスタ内でどのように見えるかの振る舞いを設定します。デフォルトは ‘false’ であり、エージェント停止時は serf members で見ると ‘failed’ と表示されます。
laeve_on_terminate (停止時に leave とする) を ‘true’ とした場合は、’left’ の状態になります。これは、serf エージェント稼働時、 ‘Ctrl+C’ で中断したときと同じように、正常な停止と判断されます(エージェント停止前に、クラスタに対してエージェントが shutdown する事を宣言します)。
なお、この設定にかかかわらず kill -KILL シグナル (kill -9 ) の場合で停止すると、ノードは ‘failed’ となります。
設定ファイル上で、次のように定義します。
"leave_on_terminate": true
■ ‘skip_leave_on_interrupt’
これは ‘leave_on_terminate’ に似ていますが、Serf エージェントの中断時 (Ctrl+C) の振る舞いをを決めます。デフォルトは ‘false’ で、’Ctrl+C’ でエージェントを停止すると、ノードの状態は ‘left’ になります。
‘skip_leave_on_interrupt’ (中断時に leave 処理をスキップする= shutdown 宣言を行わずエージェントを停止) に ‘true’ を明示すると、’Ctrl+C’ の中断は、ノードは ‘failed’ になります。
設定ファイル上では、次のように定義します。
"skip_leave_on_interrupt": true
なお、’l’eave_on_terminate’ と ‘skip_leave_on_interrupt’ は共存することができます。振る舞いを表にまとめたものが下表です。
+----------------------------------------------+--------------------------+ | 設定オプション | 停止手法 | +--------------------+-------------------------+--------+--------+--------+ | leave_on_terminate | skip_leave_on_interrupt | kill | kill -9| Ctrl+C | +--------------------+-------------------------+--------+--------+--------+ | true | true | left | failed | failed | | true | false | left | failed | left | | false | true | failed | failed | failed | | false | false | failed | failed | left | ※default +--------------------+-------------------------+--------+--------+--------+
これらは、何をもって異常と判断するかの設定フラグです。通常は触る必要が無いと思いますが、デバッグ時や、運用時に役立つと思われます。
■ ‘reconnect_interval’
エージェントのダウン ( fail ) を検出した場合、何秒毎に復帰したかどうかを確認する時間間隔を指定します。標準は 30 秒です。よりシビアな運用をしたい場合は、短く設定することもできます。たとえば、5 秒ごとに確認したい場合は、次のように設定ファイルに記述します。
"reconnect_interval": "3s"
■ ‘reconnect_timeout’ と ‘tombstone_timeout’
ノードがクラスタから離れてから、どの期間そのノードの事を記憶しているかを指定します。これは、ノード情報の消滅イベント ‘member-reap’ を実行するタイミングに関わります。デフォルトでは 24 時間です。実行のタイミングは、failed (障害でダウン) か、left (クラスタから離れた) によって分けることが出来ます。
- reconnect_timeout … ‘faied’ してから諦めるまでの時間。’reconnect_interval’ で指定した間隔ごとに、ノードが復帰するまで待ち続けます。ノードが起動したら、自動的にクラスタに復帰します。
- tombstone_timeout … ‘left’ してから情報を保持し続ける時間。ノードは正常に離れたと見なし、ノードが復帰するかどうかのチェックは行いません(tombstone=墓標)。
設定ファイルでは、次のように各々を指定できます。
"reconnect_timeout": "1h", "tombstone_timeout": "10m",
異常終了の場合は、1h (1時間) ノードの情報を保持します。通常のノード停止の場合は 10m (10 分) までノード情報を保持します。ノード情報が消える時 member-rep イベントを発行しますので、ロードバランサから情報を削除したり、監視システムから情報を削除するタイミングの指定などに活用することが期待出来ます。
■ ‘disable_name_resolution’
ノードの名前解決を行いません。しかし、Serf が自動的に名前解決を行えなくなると、Serf の動作がホスト名に依存しているため、問題が発生するかもしれません。オプションとしては推奨されていませんが、設定を行う場合は次のように指定します。
"disable_name_resolution": true
■ 参考情報
Configuration – Serf
http://www.serfdom.io/docs/agent/options.html
Event Handlers – Serf
http://www.serfdom.io/docs/agent/event-handlers.html