はじめてのHashiCorp Consul (第1回 インストール〜起動)
おはようございます、加藤です。Consulって名前は聞くけど、なんとなくしか出来る事を知らないなーと思っていました。 Consulのチュートリアルを見つけたので、実際に動かしながらブログにまとめていきます。
- Consul by HashiCorp
- Consulのメインページ
- Consul Curriculum - HashiCorp Learn
- Consulのチュートリアル
最初に書いた通り、Consulについて理解できていないので、Consulって何?的な説明は行いません。チュートリアルをおえて理解できたら別途書こうと思います。
やってみた
Consulのインストール
Consulを試す為にはインストールする必要がありますが、Dockerでいける気がしたのでDockerでやってみました。
イメージをPullして作業ディレクトリを作成します。
docker pull consul mkdir -p ~/tmp/consul/getting-started && cd ~/tmp/consul/getting-started
コンテナを立ち上げconsul
コマンドが使用できることを確認します。バージョンはv1.4.4でした。
docker run --rm -it consul sh
consul version # Consul v1.4.4 # Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) consul help # Usage: consul [--version] [--help] <command> [<args>] # # Available commands are: # acl Interact with Consul's ACLs # agent Runs a Consul agent # catalog Interact with the catalog # connect Interact with Consul Connect # debug Records a debugging archive for operators # 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. # intention Interact with Connect service intentions # join Tell Consul agent to join cluster # keygen Generates a new encryption key # keyring Manages gossip layer encryption keys # kv Interact with the key-value store # 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 # operator Provides cluster-level tools for Consul operators # reload Triggers the agent to reload configuration files # rtt Estimates network round trip time between nodes # services Interact with services # snapshot Saves, restores and inspects snapshots of Consul server state # tls Builtin helpers for creating CAs and certificates # validate Validate config files/directories # version Prints the Consul version # watch Watch for changes in Consul exit
Consulエージェントの実行
下記のコマンドでConsulエージェントを開発モードで起動したいです。
consul agent -dev
Dockerで動かすので下記のコマンドで実行します。
docker run --rm --name consul_getting_started consul agent -dev
フォアグラウンドで起動しているので、もう1つターミナルを立ち上げてコンテナ内に入ります。
docker exec -it consul_getting_started sh
以降、先に立ち上げたターミナルをConsulを実行している方、後に実行した方をshを実行している方と呼称します。
shを実行している方で、プロセスを確認すると-data-dir
と-config-dir
オプションが追加されていますが検証に支障はなさそうなので、そのまま続けます。
ps | grep consul 6 consul 0:03 consul agent -data-dir=/consul/data -config-dir=/consul/config -dev 30 root 0:00 grep consul
下記でdocker-entrypoint.sh
を確認するとわかりますが、この2つのオプションが追加されるのは意図した動作(このコンテナの仕様)です。
docker-consul/docker-entrypoint.sh at master · hashicorp/docker-consul
今、Consulに参加しているのは起動しているコンテナ1つだけのはずです。shを実行している方で、下記のコマンドを実行して確認してみましょう。
# members Lists the members of a Consul cluster consul members # Node Address Status Type Build Protocol DC Segment # 9d0e04963a24 127.0.0.1:8301 alive server 1.4.4 2 dc1 <all>
確認ができました。members
はConsul clusterに参加しているメンバーを列挙するオプションです。
Consulを実行している方を確認すると2019/03/25 15:25:21 [DEBUG] http: Request GET /v1/agent/members?segment=_all (646.265µs) from=127.0.0.1:53448
とリクエストが記録されています。
members
で確認できる内容はGossip Protocolというもので実装されているらしく、結果整合性です。
強整合性を求める場合はHTTP APIを使用してリクエストを行います。こちらでもコンテナ1つだけが参加している事を確認できました。
curl localhost:8500/v1/catalog/nodes [ { "ID": "1d20d85f-c1f1-dece-4f30-f5e62fad9277", "Node": "9d0e04963a24", "Address": "127.0.0.1", "Datacenter": "dc1", "TaggedAddresses": { "lan": "127.0.0.1", "wan": "127.0.0.1" }, "Meta": { "consul-network-segment": "" }, "CreateIndex": 9, "ModifyIndex": 10 } ]
DNSインターフェイスでもリクエストが可能です。${NODE}.node.consul
を名前解決してみます。
# digを実行するために、bind-toolsをインストール # NODEを環境変数に格納する為にjqをインストール apk add bind-tools jq # NODEを環境変数に格納 NODE=$(curl -s localhost:8500/v1/catalog/nodes | jq -r '.[].Node') dig @127.0.0.1 -p 8600 ${NODE}.node.consul # ;; QUESTION SECTION: # ;9d0e04963a24.node.consul. IN A # # ;; ANSWER SECTION: # 9d0e04963a24.node.consul. 0 IN A 127.0.0.1 # # ;; ADDITIONAL SECTION: # 9d0e04963a24.node.consul. 0 IN TXT "consul-network-segment=" # # ;; Query time: 1 msec # ;; SERVER: 127.0.0.1#8600(127.0.0.1) # ;; WHEN: Tue Mar 26 07:58:19 UTC 2019 # ;; MSG SIZE rcvd: 105
127.0.0.1
で名前解決が正常に行われました。
コンテナを終了します。
Consulを実行している方でCtrl + C
で終了します。shを実行している方も、コンテナが終了し端末のターミナルに戻ります。
サービスを登録する
サービスは定義ファイルを作成する or HTTP APIにリクエストする事で登録ができます。 定義ファイルを作成して登録します。
サービス定義を作成します。webという名前でタグはrails、ポートは80番の定義です。
{ "service": { "name": "web", "tags": [ "rails" ], "port": 80 } }
mkdir ./consul.d echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' \ > ./consul.d/web.json
このコンテナは/consul/config
からコンフィグを読み込む様にオプションが設定されているので、そこに作成したサービス定義をマウントします。
また、今回はバックグラウンドでコンテナを起動しています(-d
オプション)。コンテナを起動し、コンテナ内でshを起動して中に入ります。
docker run --rm --name consul_getting_started -v $(pwd)/consul.d/:/consul/config -d consul docker exec -it consul_getting_started sh
DNSでリクエストしてみます。
apk add bind-tools jq 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 127.0.0.1 # # ;; ADDITIONAL SECTION: # web.service.consul. 0 IN TXT "consul-network-segment=" # # ;; Query time: 2 msec # ;; SERVER: 127.0.0.1#8600(127.0.0.1) # ;; WHEN: Tue Mar 26 09:24:18 UTC 2019 # ;; MSG SIZE rcvd: 99 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 53304c043d9a.node.dc1.consul. # # ;; ADDITIONAL SECTION: # 53304c043d9a.node.dc1.consul. 0 IN A 127.0.0.1 # 53304c043d9a.node.dc1.consul. 0 IN TXT "consul-network-segment=" # # ;; Query time: 0 msec # ;; SERVER: 127.0.0.1#8600(127.0.0.1) # ;; WHEN: Tue Mar 26 09:25:15 UTC 2019 # ;; MSG SIZE rcvd: 147
Aレコードで問い合わせると、サービスのIPアドレスが返ってきました。
SRVレコードで問い合わせると、サービスが80番ポートで53304c043d9a.node.dc1.consul
の上で動作している事が返ってきました。
タグで問い合わせを行います。今回はrailsタグが設定されているサービスが1つしか無いため、返ってきたIPアドレスは1つですが、タグが付いている全てのサービスのIPアドレスが返ってきます。
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 127.0.0.1 # # ;; ADDITIONAL SECTION: # rails.web.service.consul. 0 IN TXT "consul-network-segment=" # # ;; Query time: 1 msec # ;; SERVER: 127.0.0.1#8600(127.0.0.1) # ;; WHEN: Tue Mar 26 09:33:49 UTC 2019 # ;; MSG SIZE rcvd: 105
HTTP APIでリクエストします。このAPIは Catalog Endpoint と言い、指定したサービスをホストしている全てのノード情報を返します(ヘルスチェックに失敗したノードも返す)。 Catalog - HTTP API - Consul by HashiCorp
curl http://localhost:8500/v1/catalog/service/web # [ # { # "ID": "2e1d9862-aef6-7652-2205-94301a804360", # "Node": "53304c043d9a", # "Address": "127.0.0.1", # "Datacenter": "dc1", # "TaggedAddresses": { # "lan": "127.0.0.1", # "wan": "127.0.0.1" # }, # "NodeMeta": { # "consul-network-segment": "" # }, # "ServiceKind": "", # "ServiceID": "web", # "ServiceName": "web", # "ServiceTags": [ # "rails" # ], # "ServiceAddress": "", # "ServiceWeights": { # "Passing": 1, # "Warning": 1 # }, # "ServiceMeta": {}, # "ServicePort": 80, # "ServiceEnableTagOverride": false, # "ServiceProxyDestination": "", # "ServiceProxy": {}, # "ServiceConnect": {}, # "CreateIndex": 10, # "ModifyIndex": 10 # } # ]
ヘルスチェックに成功しているサービスのみ探すには Health Endpoint を使用します。 Health - HTTP API - Consul by HashiCorp
curl 'http://localhost:8500/v1/health/service/web?passing' # [ # { # "Node": { # "ID": "2e1d9862-aef6-7652-2205-94301a804360", # "Node": "53304c043d9a", # "Address": "127.0.0.1", # "Datacenter": "dc1", # "TaggedAddresses": { # "lan": "127.0.0.1", # "wan": "127.0.0.1" # }, # "Meta": { # "consul-network-segment": "" # }, # "CreateIndex": 9, # "ModifyIndex": 10 # }, # "Service": { # "ID": "web", # "Service": "web", # "Tags": [ # "rails" # ], # "Address": "", # "Meta": null, # "Port": 80, # "Weights": { # "Passing": 1, # "Warning": 1 # }, # "EnableTagOverride": false, # "ProxyDestination": "", # "Proxy": {}, # "Connect": {}, # "CreateIndex": 10, # "ModifyIndex": 10 # }, # "Checks": [ # { # "Node": "53304c043d9a", # "CheckID": "serfHealth", # "Name": "Serf Health Status", # "Status": "passing", # "Notes": "", # "Output": "Agent alive and reachable", # "ServiceID": "", # "ServiceName": "", # "ServiceTags": [], # "Definition": {}, # "CreateIndex": 9, # "ModifyIndex": 9 # } # ] # } # ]
サービスを更新する
サービス定義はAgent起動後に更新されても反映されません。反映するにはSIGHUP
を送信するか、Agentを再起動する必要があります。
サービス定義を更新して、SIGHUP
送信前後でレスポンスを比較して見ました。
echo '{"service": {"name": "web", "tags": ["rails","demo"], "port": 80}}' > /consul/config/web.json dig +short @127.0.0.1 -p 8600 demo.web.service.consul kill -HUP $(pgrep -f "consul agent") dig +short @127.0.0.1 -p 8600 demo.web.service.consul # 127.0.0.1
あとがき
Consulを起動してサービス登録する所まで行えました。まだ、理解したには程遠いので引き続きチュートリアルを進めて行きまーす。