Carpe Diem

備忘録

Consulで各サービスのコンテナ群のIPを取得する

概要

以前Consulの基本的な使い方を紹介しました。

christina04.hatenablog.com

このConsulに登録する各ノードに対しサービス設定をすることで、各マイクロサービスに所属しているコンテナ群のIPを取得できるようします。
それによってgRPCでの負荷分散としてのClient-side LBが、動的なコンテナに対しても可能になります。

環境

  • Consul 1.1.0
  • docker 18.03.1-ce
  • docker-compose 1.21.1

準備

githubにデモ用のdocker-composeの設定があるのでそれを利用します。

github.com

ディレクトリ構造

以下のようにConsulのサービスを設定するためのjsonを用意します。
内容は後述します。

.
├── docker-compose.yml
├── nginx-service
│   └── service.json
└── redis-service
    └── service.json

docker-compose.yml

version: '3'

services:

  nginx-agent-1: &nginx-agent
    image: consul:latest
    networks:
      - consul-demo
    command: "agent -retry-join consul-server-bootstrap -client 0.0.0.0"
    volumes:
      - ./nginx-service:/consul/config

  nginx-agent-2:
    <<: *nginx-agent

  nginx-agent-3:
    <<: *nginx-agent

  redis-agent-1: &redis-agent
    image: consul:latest
    networks:
      - consul-demo
    command: "agent -retry-join consul-server-bootstrap -client 0.0.0.0"
    volumes:
      - ./redis-service:/consul/config

  redis-agent-2:
    <<: *redis-agent

  redis-agent-3:
    <<: *redis-agent

  consul-server-1: &consul-server
    image: consul:latest
    networks:
      - consul-demo
    command: "agent -server -retry-join consul-server-bootstrap -client 0.0.0.0"

  consul-server-2:
    <<: *consul-server

  consul-server-bootstrap:
    <<: *consul-server
    ports:
      - "8400:8400"
      - "8500:8500"
      - "8600:8600"
      - "8600:8600/udp"
    command: "agent -server -bootstrap-expect 3 -ui -client 0.0.0.0"

networks:
  consul-demo:

ポイントは以下です。

nginx-service/service.json

Service Definition - Consul by HashiCorpのドキュメントに従って書きます。
nameのみ必須で、あとはoptionalです。今回はtagだけ付けました。

{
  "service": {
    "name": "nginx",
    "tags": ["dev"]
  }
}

redis-service/service.json

こちらも同様。nameredisにしてます。

{
  "service": {
    "name": "redis",
    "tags": ["dev", "cache"]
  }
}

動作検証

起動

docker-composeで起動します。

$ docker-compose up -d

Web UIで確認

起動中です。3台のConsul serverが互いに疎通し、リーダーが選出されるまではこの表示です。 f:id:quoll00:20180531124822p:plain

起動が完了しました f:id:quoll00:20180531124910p:plain

digでDNSが使えるか確認

nginx

まずはnginxのサービスに登録されているコンテナのIPを取得します

$ dig @localhost -p 8600 nginx.service.consul

; <<>> DiG 9.10.6 <<>> @localhost -p 8600 nginx.service.consul
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8981
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx.service.consul.        IN  A

;; ANSWER SECTION:
nginx.service.consul. 0   IN  A   172.18.0.2
nginx.service.consul. 0   IN  A   172.18.0.4
nginx.service.consul. 0   IN  A   172.18.0.8

;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu May 31 11:10:18 JST 2018
;; MSG SIZE  rcvd: 97
  • 172.18.0.2
  • 172.18.0.4
  • 172.18.0.8

の3つがちゃんと取れました。

redis

次にredisサービスに登録されているコンテナのIPを取得します。

$ dig @localhost -p 8600 redis.service.consul

; <<>> DiG 9.10.6 <<>> @localhost -p 8600 redis.service.consul
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19331
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;redis.service.consul.        IN  A

;; ANSWER SECTION:
redis.service.consul. 0   IN  A   172.18.0.5
redis.service.consul. 0   IN  A   172.18.0.6
redis.service.consul. 0   IN  A   172.18.0.3

;; Query time: 8 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu May 31 11:10:06 JST 2018
;; MSG SIZE  rcvd: 97
  • 172.18.0.3
  • 172.18.0.5
  • 172.18.0.6

の3つがちゃんと取れました。

tagでフィルタリング

例えばdev, stg, prdで環境分けしたい時にtagを設定することでフィルタリングすることが可能です。
先程のservice.jsonではdevcacheと言ったtagを付けました。

設定したcacheでフィルタ

$ dig @localhost -p 8600 cache.redis.service.consul

; <<>> DiG 9.10.6 <<>> @localhost -p 8600 cache.redis.service.consul
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48848
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cache.redis.service.consul.  IN  A

;; ANSWER SECTION:
cache.redis.service.consul. 0 IN  A   172.18.0.10
cache.redis.service.consul. 0 IN  A   172.18.0.2
cache.redis.service.consul. 0 IN  A   172.18.0.8

;; Query time: 3 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu May 31 17:58:16 JST 2018
;; MSG SIZE  rcvd: 103

ちゃんと出ます。

設定していないhogeでフィルタ

$ dig @localhost -p 8600 hoge.redis.service.consul

; <<>> DiG 9.10.6 <<>> @localhost -p 8600 hoge.redis.service.consul
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51300
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;hoge.redis.service.consul.   IN  A

;; AUTHORITY SECTION:
consul.           0   IN  SOA ns.consul. hostmaster.consul. 1527757102 3600 600 86400 0

;; Query time: 7 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu May 31 17:58:21 JST 2018
;; MSG SIZE  rcvd: 104

設定していないので存在しないです。

このようにtagを設定することで柔軟にフィルタ可能になります。

まとめ

Consulでサービスを設定することで、マイクロサービスのコンテナ群のIPをDNSで取得することができました。
Kubernetesであれば

christina04.hatenablog.com

のようにheadless serviceを使えば実現できますが、AWSのECSではできないのでこのようにConsulと組み合わせて構築する必要があります。

ソース