Edited at

オーケストレーションツールとしてのConsulの使い方

More than 3 years have passed since last update.


はじめに

Consul 0.4 がリリースされ、大幅な機能追加が行われました。

また下記のリリース記事で、Consulは次のようなのものであると言っています。


Consul is a solution for service discovery, configuration, and orchestration.


https://www.hashicorp.com/blog/consul-0-4.html

以前までは、オーケストレーションツールであるとは述べていなかったと思いますが、今回のリリースによってSerfのイベントシステムをConsulから使えるようになり、オーケストレーションが行えるまでになったと思います。

本記事では0.4で追加された機能に注力して利用法について紹介していきます。


Consulとは

公式のドキュメントで丁寧に解説されていますので参照して下さい。

http://www.consul.io/intro/index.html


動作環境の準備

Consulの動作確認の検証のために複数台のマシンが必要になります。

1台1台VMを建ててもいいのですが、今回は手っ取り早く検証できるようにDockerを使って環境を整えます。


Dockerの環境がある場合

手前味噌ですがイメージ作成したのでご活用下さい。

docker pull foostan/consul


ない場合

Macをお使いの場合はboot2dockerですんなり入ります。

またvagrantを使うと比較的楽に環境が整いますので今回はこちらの方法を紹介します。

git clone https://github.com/foostan/docker-consul.git

cd docker-consul
vagrant up

イメージを既にダウンロードしているかどうかによって異なりますが、だいたい10分ぐらいでVMが立ち上がってくると思います。

Vagrantfileを読んで頂くとわかると思いますが、VM起動後にdockerのインストールとfoostan/consulをpullしています。


基本的な使い方


コンテナの起動

何台起動してもいいのですが、Consulのようにクラスタ組むようなものは台数を増やしたほうがその凄さがわかるし、何より楽しいです。

今回は説明用に4台(server*2, client*2)使います。

それぞれホスト名つけたので、どのコンテナで作業しているかはホスト名で判断してください。

vagrant@vagrant-ubuntu-trusty-64:~$ docker run -h server1 -i -t foostan/consul

root@server1:/#

vagrant@vagrant-ubuntu-trusty-64:~$ docker run -h server2 -i -t foostan/consul

root@server2:/#

vagrant@vagrant-ubuntu-trusty-64:~$ docker run -h client1 -i -t foostan/consul

root@client1:/#

vagrant@vagrant-ubuntu-trusty-64:~$ docker run -h client2 -i -t foostan/consul

root@client2:/#


agentの起動

クラスタにjoinする際に起点となるサーバのIPを知っている必要があるので、予め調べておきます。

root@server1:/# ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
37: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 9e:0b:9f:19:7e:e3 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::9c0b:9fff:fe19:7ee3/64 scope link
valid_lft forever preferred_lft forever

今回の場合はserver1のIPは172.17.0.2であることがわかりました。


server1の起動(-bootstrap-expectオプション使用)

server1を-bootstrap-expectオプションを指定して起動します。

このオプションを指定すると、指定した数のサーバがクラスタにJoinしたタイミングで自動的にbootstrapingが開始されます。

bootstrapping の詳細はhttp://www.consul.io/docs/guides/bootstrapping.html を参照して下さい。

なお0.4から-bootstrapオプションによるマニュアル操作は非推奨となっています。

root@server1:/# consul agent -data-dir=/tmp/consul -server -bootstrap-expect 2 &

==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Consul agent running!
Node name: 'server1'
Datacenter: 'dc1'
Server: true (bootstrap: false)
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
Cluster Addr: 172.17.0.2 (LAN: 8301, WAN: 8302)
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

-以下省略-

-bootstrap-expectの値は 2 に指定しています。

指定する値については下記のQuorum Sizeを参考にして下さい。

http://www.consul.io/docs/internals/consensus.html#toc_4

なお自動bootstrappingは一度でもconsulを起動したことがあると(raftのログが残っていると)開始されません。

https://github.com/hashicorp/consul/blob/master/consul/serf.go#L196-L201

もし自動bootstrappingをやり直す際は、-data-dirで指定したディレクトリ内のraftディレクトリごと削除して下さい。

またagentは検証しやすいように&をつけてバックグラウンドで起動しています。


server2の起動(-joinオプション使用)

-joinオプションを使用してserver1のクラスタにJoinします。

root@server2:/# consul agent -data-dir=/tmp/consul -server -join 172.17.0.2 &

==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Joining cluster...
Join completed. Synced with 1 initial agents
==> Consul agent running!
Node name: 'server2'
Datacenter: 'dc1'
Server: true (bootstrap: false)
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
Cluster Addr: 172.17.0.3 (LAN: 8301, WAN: 8302)
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

-以下省略-

server2がjoinしたタイミングでserver1側で出力されるログを見ると、bootstrappingが行われ、クラスタ内のリーダが選出されたことがわかります(server1がリーダとして選出された)。

-以上省略-

2014/09/14 04:48:29 [INFO] serf: EventMemberJoin: server2 172.17.0.3
2014/09/14 04:48:29 [INFO] consul: adding server server2 (Addr: 172.17.0.3:8300) (DC: dc1)
2014/09/14 04:48:29 [INFO] consul: Attempting bootstrap with nodes: [172.17.0.2:8300 172.17.0.3:8300]
2014/09/14 04:48:30 [WARN] raft: Heartbeat timeout reached, starting election
2014/09/14 04:48:30 [INFO] raft: Node at 172.17.0.2:8300 [Candidate] entering Candidate state
2014/09/14 04:48:30 [WARN] raft: Remote peer 172.17.0.3:8300 does not have local node 172.17.0.2:8300 as a peer
2014/09/14 04:48:30 [INFO] raft: Election won. Tally: 2
2014/09/14 04:48:30 [INFO] raft: Node at 172.17.0.2:8300 [Leader] entering Leader state
2014/09/14 04:48:30 [INFO] consul: cluster leadership acquired
2014/09/14 04:48:30 [INFO] raft: pipelining replication to peer 172.17.0.3:8300
2014/09/14 04:48:30 [INFO] consul: New leader elected: server1

-以下省略-


client1,2の起動

-serverオプションを付けずに起動するとクライアントとして起動します。

root@client1:/# consul agent -data-dir=/tmp/consul -join 172.17.0.2 &

==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Joining cluster...
Join completed. Synced with 1 initial agents
==> Consul agent running!
Node name: 'client1'
Datacenter: 'dc1'
Server: false (bootstrap: false)
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
Cluster Addr: 172.17.0.6 (LAN: 8301, WAN: 8302)
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

-以下省略-

root@client2:/# consul agent -data-dir=/tmp/consul -join 172.17.0.2 &

==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Joining cluster...
Join completed. Synced with 1 initial agents
==> Consul agent running!
Node name: 'client2'
Datacenter: 'dc1'
Server: false (bootstrap: false)
Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
Cluster Addr: 172.17.0.5 (LAN: 8301, WAN: 8302)
Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

-以下省略-


起動確認

任意のノードから consul members コマンドでクラスタ内のメンバーを確認できます。

root@server1:/# consul members

Node Address Status Type Build Protocol
client2 172.17.0.5:8301 alive client 0.4.0 2
server1 172.17.0.2:8301 alive server 0.4.0 2
server2 172.17.0.3:8301 alive server 0.4.0 2
client1 172.17.0.6:8301 alive client 0.4.0 2


execによるコマンド発行

任意のノードから exec コマンドによって全ノードに対して任意のコマンドを発行することが出来ます。

root@server1:/# consul exec "hostname"

server1: server1
server1:
==> server1: finished with exit code 0
client1: client1
client1:
client2: client2
client2:
server2: server2
server2:
==> client1: finished with exit code 0
==> client2: finished with exit code 0
==> server2: finished with exit code 0
4 / 4 node(s) completed / acknowledged


フィルタリング

コマンドの実行は


  • datacenter

  • prefix

  • node

  • service

  • tag

でフィルタリングが可能です。

例えばclient1のみで実行する場合は -node="client1" を指定します。

root@server1:/# consul exec -node="client1" "hostname"

client1: client1
client1:
==> client1: finished with exit code 0
1 / 1 node(s) completed / acknowledged

execの詳細は http://www.consul.io/docs/commands/exec.html を参照して下さい。


watchによるコマンド発行

execは任意のコマンドをこちらのタイミングで発行するものですが、watchを利用すると下記のタイミングで任意のコマンドを自動的に発行することできます。


  • key/valueストレージの内容が変化したとき

  • 任意のイベントを発行したとき

  • Service Definitionで監視しているサービスの状態が変化したとき

  • Check Definitionで監視しているノードの状態が変化したとき

  • クラスタのノードに変化があったとき


key/valueストレージの内容が変化したとき

key=foo の変化を監視して、変化があったときにその値を取得してみます。

root@client1:/# consul watch -type=key -key=foo "curl -s -X GET localhost:8500/v1/kv/foo?raw"

client1でwatchした状態で、server1でfooの値を変更してみます。

root@server1:/# curl -X PUT -d "foo" localhost:8500/v1/kv/foo

true
root@server1:/# curl -X PUT -d "bar" localhost:8500/v1/kv/foo
true
root@server1:/# curl -X PUT -d "bazz" localhost:8500/v1/kv/foo
true

すると変更される度に、指定したコマンドが実行されて、下記のように表示されます。

foobarbazz

(実際はfoo bar bazz が順番に表示される)

これにより envconsul(https://github.com/hashicorp/envconsul) で行っていたことが代替できます。


任意のイベントを発行したとき

deployイベントが発行されると 'deploy' と表示するようにしてみます(watch開始時にコマンドが一回実行されます…)。

root@client1:/# consul watch -type=event -name="deploy" "echo deploy"

client1でwatchした状態で、server1でdeployイベントを発行してみます。

root@server1:/# consul event -name="deploy"

Event ID: b38a8748-84d9-8235-8e4d-441935999af9

実行すると、client1側で'deploy'と表示されたと思います。

なお、イベント発行はexecと同等のフィルタリングが可能です。


Service Definitionで監視しているサービスの状態が変化したとき

consul serverはconsul serviceを提供しており、デフォルトでこのサービスの監視がされています。

このサービスを監視して変化があったときに 'change services' と表示するようにしてみます。

consul watch -type=service -service=consul "echo change services"

client1でwatchした状態で、server2をクラスタから抜いてみます(server2のconsul serviceが終了します)。

root@server2:/# consul leave

consul serviceの変化を検知してclient1でコマンドが実行されます。

また、server2をクラスタに再度追加してみます。

root@server2:/# consul agent -data-dir=/tmp/consul -server -join 172.17.0.2 &

同様に検知してコマンドが実行されます。


Check Definitionで監視しているノードの状態が変化したとき

Check Definitionのチェック状態が変更されるとコマンドが発行されます。

root@client1:/# consul watch -type=checks "echo change checks"


クラスタのノードに変化があったとき

ノードに変化(JoinやLeave)があったときコマンドが発行されます。

root@client1:/# consul watch -type=nodes "echo change nodes"


さいごに

Consul 0.4 で exec や watch, event といった機能が追加され、よりオーケストレーションツールとして使えるようになりました。

ただし現状では、'変化があったタイミング' でコマンド発行することしか出来ず、サービスが '終了した' のか '開始された' のか判断することがConsul単体ではできないため、イマイチ使いにくさが残ります。

とはいえConsulの開発速度は非常に早いため今後の機能強化が期待されます。