ぺーぺーSEのブログ

備忘録・メモ用サイト。

Consul入門

Consulとかいうツールが流行っているようだが結局何ができるかよくわからなかったのでここを斜め読みして軽くまとめる。
ここではあえてAtlasについては触れない。
ちなみにTerraformについては以下に書いた。

blog.pepese.com

Consulの特長。

  • Service Discovery
    • サービスの発見(サービス検出)
    • サービスとは何等かのWebサービスやデータベースといった高レベルのサービスのこと
  • Helth Checking
    • 上記のサービス検出と併せて、そのサービスが動作していること(HTTP Status 200 OKとか)やメモリ使用率のチェックができる
    • ヘルスチェックというか「監視」のイメージ(個人的な感想)
  • Key/Value Store
    • Consul Agent間でkey-valueデータを共有・保持できる
  • Multi Datacenter
    • 別ネットワークとも連携が可能
    • DRように別々のデータセンター間でもConsul Agent同士は連携できる

Consulでは各ノードにConsul Agentを配備し、そのノード(自分自身)のヘルスチェックを行う。 ConsulはSerfの上位に位置する。
(Serfが持つgossip protocolを使用するという意味で。基本別モノ。)

Serfとは?

SerfもまたHashicorp社のツールで、gossip protocolを使用し、Membership(クラスタ構成)故障検出イベントのブロードキャストの機能を提供する。
Serfでは各ノードにAgentが存在し、gossip protocolでUDP通信による無作為なコミュニケーションを実施する。
AgentにはServer AgentClient Agentがある。
gossip protocolにはLAN Gossip(LAN・データセンタ内)とWAN Gossip(LAN・データセンタ間、ただしServer Agentのみ)がある。
イメージについてはここを参照。
Serfで実現するクラスタ構成や故障検出は、Serf Agentの生存如何で実現する。
Consulが高レベルなヘルスチェック(サービスレベル)ができるとすると、Serfは低レベルなヘルスチェックという表現になる。

ConsulもまたSerfと同様、クラスタ構成を取る。

インストール

ここからプラットフォームに合ったバイナリを落とす。 zipなので好きなところに展開する。
1つ実行ファイルが出てくるのでPATHを通す。
コマンドで「consul」をたたいて動くかどうか確認する。

# DOA
C:\tools\consul_0.5.2>consul
usage: consul [--version] [--help] <command> [<args>]

Available commands are:
    agent          Runs a Consul agent
    configtest     Validate config file
    event          Fire a new event
    exec           Executes a command on Consul nodes
    force-leave    Forces a member of the cluster to enter the "left" state
    info           Provides debugging information for operators
    join           Tell Consul agent to join cluster
    keygen         Generates a new encryption key
    keyring        Manages gossip layer encryption keys
    leave          Gracefully leaves the Consul cluster and shuts down
    lock           Execute a command holding a lock
    maint          Controls node or service maintenance mode
    members        Lists the members of a Consul cluster
    monitor        Stream logs from a Consul agent
    reload         Triggers the agent to reload configuration files
    version        Prints the Consul version
    watch          Watch for changes in Consul

Starting the Agent

Consul Agentはサーバモードクライアントモードで実行できる。
Multi Datacenterで実行する場合、各データセンタ(LAN)には少なくとも1つ以上のConsul Serverが必要で、 1つのデータセンタ(LAN)には3~5ノードのConsul Serverが存在することが推奨される。
(Consul Serverが1ノードだと、障害時にデータロスするかもしれない)
Consul Agentをサーバモードで起動するには「 consul agent -server」コマンドを使用する。

# sh
$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul
==> WARNING: BootstrapExpect Mode is specified as 1; this is the same as Bootstrap mode.
==> WARNING: Bootstrap mode enabled! Do not enable unless necessary
==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Consul agent running!
       Node name: 'Armons-MacBook-Air'
      Datacenter: 'dc1'
          Server: true (bootstrap: true)
     Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
    Cluster Addr: 10.1.10.38 (LAN: 8301, WAN: 8302)

==> Log data will now stream in as it occurs:

[INFO] serf: EventMemberJoin: Armons-MacBook-Air.local 10.1.10.38
[INFO] raft: Node at 10.1.10.38:8300 [Follower] entering Follower state
[INFO] consul: adding server for datacenter: dc1, addr: 10.1.10.38:8300
[ERR] agent: failed to sync remote state: rpc error: No cluster leader
[WARN] raft: Heartbeat timeout reached, starting election
[INFO] raft: Node at 10.1.10.38:8300 [Candidate] entering Candidate state
[INFO] raft: Election won. Tally: 1
[INFO] raft: Node at 10.1.10.38:8300 [Leader] entering Leader state
[INFO] consul: cluster leadership acquired
[INFO] consul: New leader elected: Armons-MacBook-Air
[INFO] consul: member 'Armons-MacBook-Air' joined, marking health alive

Ctrl + C」で正常終了する。

Cluster Members

別DOS/ターミナルを開いて「consul members」コマンドを実行するとクラスタ内のノード一覧が出力される。

# sh
$ consul members
Node                    Address             Status  Type    Build  Protocol
Armons-MacBook-Air      10.1.10.38:8301     alive   server  0.5.1  2

ConsulはHTTP APIも提供しており、下記のようにAPI経由でクラスタ内のノード一覧を取得できる。

# sh
$ curl localhost:8500/v1/catalog/nodes
[{"Node":"Armons-MacBook-Air","Address":"10.1.10.38"}]

さらにDNS APIも提供しており、下記のようにクラスタ内のノード一覧を取得できる。

# sh
$ dig @127.0.0.1 -p 8600 Armons-MacBook-Air.node.consul
...

;; QUESTION SECTION:
;Armons-MacBook-Air.node.consul.    IN  A

;; ANSWER SECTION:
Armons-MacBook-Air.node.consul. 0 IN    A   10.1.10.38

Defining a Service

Consulではサービスの定義をJSONで行う。
JSONファイルの保存先は「consul.d」ディレクトリ配下。
ここでは、「/etc/consul.d」とする。

# sh
$ sudo mkdir /etc/consul.d

サービスの定義はJSONの「service」要素で指定する。
ポート80番のWebサーバサービスを定義する場合は下記のようになる。

# sh
$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' \
    >/etc/consul.d/web.json

サービス定義の詳細についてはここを参照。
上記で作成したディレクトリを指定してConsul Agentを再起動する。

# sh
$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul \
    -config-dir /etc/consul.d
==> Starting Consul agent...
...
    [INFO] agent: Synced service 'web'
...

Syncedと出てればサービス定義がロードされている。
また、「SIGHUP」シグナルをConsul Agentプロセスへ投げると、再起動しなくても設定ファイルを再ロードしてくれる。

DNS API

ひとたびサービスを登録するとDNS API経由で確認できる。
サービス名は「NAME.service.consul」で定義され、NAMEの部分は自分で設定したサービス名になる。(サブドメインは変更できる)
今回はサービス定義のJSONのname要素で「web」というサービス名を指定している。

# sh
$ dig @127.0.0.1 -p 8600 web.service.consul
...

;; QUESTION SECTION:
;web.service.consul.        IN  A

;; ANSWER SECTION:
web.service.consul. 0   IN  A   172.20.20.11

アドレスやポート情報を全て表示したい場合は「SRV」を付ける。

# sh
$ dig @127.0.0.1 -p 8600 web.service.consul SRV
...

;; QUESTION SECTION:
;web.service.consul.    IN  SRV

;; ANSWER SECTION:
web.service.consul. 0   IN  SRV 1 1 80 agent-one.node.dc1.consul.

;; ADDITIONAL SECTION:
agent-one.node.dc1.consul. 0    IN  A   172.20.20.11

また、タグにより出力結果をフィルタリングできる。
今回はサービス定義のJSONのtags要素で「rails」というタグを指定している。

# sh
$ dig @127.0.0.1 -p 8600 rails.web.service.consul
...

;; QUESTION SECTION:
;rails.web.service.consul.      IN  A

;; ANSWER SECTION:
rails.web.service.consul.   0   IN  A   172.20.20.11

HTTP API

DNS APIだけでなくHTTP APIでもサービスの確認ができる。

# sh
$ curl http://localhost:8500/v1/catalog/service/web
[{"Node":"agent-one","Address":"172.20.20.11","ServiceID":"web", \
    "ServiceName":"web","ServiceTags":["rails"],"ServicePort":80}]

Joining a Cluster

ここでは
* ホスト名:n1、IP:172.20.20.10
* ホスト名:n2、IP:172.20.20.11
の2台のサーバ(Vagrantで起動)がいる前提で書く。
まず、n1にログインしてagent-oneというノード名をつけてConsul Agentを起動する。

# sh
$ vagrant ssh n1
vagrant@n1:~$ consul agent -server -bootstrap-expect 1 \
    -data-dir /tmp/consul -node=agent-one -bind=172.20.20.10
...

そして、n2にログインしてagent-twoというノード名をつけてConsul Agentを起動する。

# sh
$ vagrant ssh n2
vagrant@n2:~$ consul agent -data-dir /tmp/consul -node=agent-two \
    -bind=172.20.20.11
...

この状態で新規にターミナルを起動してn1にログインし、「consul join」コマンドでn2を指定してクラスタに参加する。

# sh
vagrant@n1:~$ consul join 172.20.20.11
Successfully joined cluster by contacting 1 nodes.

ターミナルを起動してn2にログインし、「consul members」コマンドでクラスタノード一覧を表示するとクラスタに参加できていることがわかる。

# sh
vagrant@n2:~$ consul members
Node       Address            Status  Type    Build  Protocol
agent-two  172.20.20.11:8301  alive   client  0.5.0  2
agent-one  172.20.20.10:8301  alive   server  0.5.0  2

Ctrl + C」でクラスタから正常に離脱し、Consul Agentも正常に停止する。
DNS APIを使用してノードの情報を取得する際、「NAME.node.consul」や「NAME.node.DATACENTER.consul」のNAMEの部分にConsul Agent起動時に「-node」オプションで指定したノード名を使用することができる。

# sh
vagrant@n1:~$ dig @127.0.0.1 -p 8600 agent-two.node.consul
...

;; QUESTION SECTION:
;agent-two.node.consul. IN  A

;; ANSWER SECTION:
agent-two.node.consul.  0 IN    A   172.20.20.11

Defining Checks

ヘルスチェックの定義はJSONの「check」要素で指定する。

# sh
vagrant@n2:~$ echo '{"check": {"name": "ping", \
  "script": "ping -c1 google.com >/dev/null", "interval": "30s"}}' \
  >/etc/consul.d/ping.json

vagrant@n2:~$ echo '{"service": {"name": "web", "tags": ["rails"], "port": 80,\
  "check": {"script": "curl localhost >/dev/null 2>&1", "interval": "10s"}}}' \
  >/etc/consul.d/web.json

ここでは
* google.comへのpingを30秒間隔
* localhostへのcurlを10秒間隔
のヘルスチェックをscriptで定義している。
Consul AgentへSIGHUPシグナルを投げて設定を再ロードすると、Consul Agentを起動したターミナルに下記の内容が標準出力される。

==> Starting Consul agent...
...
    [INFO] agent: Synced service 'web'
    [INFO] agent: Synced check 'service:web'
    [INFO] agent: Synced check 'ping'
    [WARN] Check 'service:web' is now critical

今回は「web」のサービスを起動していないので、「critical」と表示されていて、サービスのヘルスチェックがNGであることがわかる。

Checking Health Status

HTTP APIでサービスのステータスを確認する。
今回は、ステータスが「critical」のものを取得する。

# sh
vagrant@n1:~$ curl http://localhost:8500/v1/health/state/critical
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","ServiceID":"web","ServiceName":"web"}]

Consul設定ファイルの再ロードの際と同様、「web」サービスがcriticalとして取得できていることがわかる。
また、DNS APIで確認すると、結果が取得できない、つまりヘルスチェックNGであることがわかる。

# sh
dig @127.0.0.1 -p 8600 web.service.consul
...

;; QUESTION SECTION:
;web.service.consul.        IN  A

Key-Value Store

ConsulはKey-Value Storeを持つ。
動的な設定変更、サービス調整の補助等に使用できる。
Key-Value StoreはHTTP APIで提供されている。
まずは取得してみる。

# sh
$ curl -v http://localhost:8500/v1/kv/?recurse
* About to connect() to localhost port 8500 (#0)
*   Trying 127.0.0.1... connected
> GET /v1/kv/?recurse HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8500
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Consul-Index: 1
< Date: Fri, 11 Apr 2014 02:10:28 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host localhost left intact
* Closing connection #0

今回は何も保存していないので404となった。
PUTメソッドでキーkey1key2key3に3通りの方法で値testを設定してみる。

# sh
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key1
true
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/key2?flags=42
true
$ curl -X PUT -d 'test' http://localhost:8500/v1/kv/web/sub/key3
true
$ curl http://localhost:8500/v1/kv/?recurse
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
 {"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="},
 {"CreateIndex":99,"ModifyIndex":99,"Key":"web/sub/key3","Flags":0,"Value":"dGVzdA=="}]

ここで注意なのは、値はbase64エンコードされているということ。
また、「?recurse」パラメータを付けると全てのKey-Valueペアが取得できるが、下記のように単一のキーを指定することもできる。

# sh
$ curl http://localhost:8500/v1/kv/web/key1
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="}]

DELETEメソッドで削除することができる。

# sh
$ curl -X DELETE http://localhost:8500/v1/kv/web/sub?recurse
$ curl http://localhost:8500/v1/kv/web?recurse
[{"CreateIndex":97,"ModifyIndex":97,"Key":"web/key1","Flags":0,"Value":"dGVzdA=="},
 {"CreateIndex":98,"ModifyIndex":98,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]

?recurse」を付けると指定した階層の全てのKey-Valueペアを削除することができる。
また更新の際は、PUTメソッドで且つ「?cas=」パラメータに更新対象の「ModifyIndex」を指定することで更新できる。

# sh
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
true
$ curl -X PUT -d 'newval' http://localhost:8500/v1/kv/web/key1?cas=97
false

上記でわかるように、更新すると「ModifyIndex」の値は変更される。
この更新時にModifyIndexが変更される仕組みを使って、GET時に値が変更されるまで待ってから取得する機能がある。

# sh
$ curl "http://localhost:8500/v1/kv/web/key2?index=101&wait=5s"
[{"CreateIndex":98,"ModifyIndex":101,"Key":"web/key2","Flags":42,"Value":"dGVzdA=="}]

?index=」パラメータを使用すると、ModifyIndexが指定した値以上になるまで待ってから値を取得してくれる。
また、「?wait=5s」を指定すると、最大で5秒しか待たない、という意味になる。

WEB UI

ここからWEB UI用のパッケージをダウンロードしてConsul Agentをインストールしたマシンのどこかに解凍する。
Consul Agentを再起動して、「-ui-dir」パラメータでUIを解凍したパスを指定する。

# sh
$ consul agent -ui-dir /path/to/ui
...

UIはHTTP APIと同じポートの「/ui」パスで見ることができる。
デフォルトは「http://localhost:8500/ui」。
デモページはここ

最後に

これで一通り何ができるか分かった。 各機能の詳細はここ。 ガイドラインはここ。 設定ファイルの例はここ