Carpe Diem

備忘録

Dockerの埋め込みDNSサーバを使ったService Discovery

概要

Docker 1.10からユーザ定義のネットワークでは内蔵DNSサーバが用意され、名前解決ができるということを以下の記事で述べました。

christina04.hatenablog.com

実は単に名前解決ができるだけでなく、Service Discoveryとしても機能するのでその紹介をします。

環境

  • Docker 18.03.1-ce
  • docker-compose 1.21.1

3種類のservice discovery

dockerのドキュメントには以下のように書いてあります。

embedded DNS server which provides built-in service discovery for any container created with a valid name or net-alias or aliased by link.

ref: Embedded DNS server in user-defined networks | Docker Documentation

つまり

  • サービス名
  • net-alias名
  • link名

を引けばServiceDiscoveryができるということですね。

検証

nameを使ったService Discovery

まずはサービス名を使った検証を行います。

docker-compose.yml

localnetというネットワーク名でfront-proxyとbackendサーバを用意します。
backendの方にbackendというnetwork aliasを付ける。

version: '2'
services:

  front-proxy:
    image: nginx
    container_name: front-proxy
    networks:
      - localnet

  backend:
    image: nginx
    networks:
      - localnet

networks:
  localnet: {}

front-proxyからbackendをdig

デフォルトのnginx-alpineにはdigがないので、インストールします。

# apt-get install dnsutils

digでbackendを引いてきます。

# dig backend

; <<>> DiG 9.10.3-P4-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58740
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;backend.         IN  A

;; ANSWER SECTION:
backend.      600 IN  A   172.19.0.3

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Mon Jun 04 14:28:03 UTC 2018
;; MSG SIZE  rcvd: 48

1台用意されていたバックエンドのnginxのIPが返りました。

スケールしてdig

docker-composeのscale機能でバックエンドのサーバを増やします。

$ docker-compose up --scale backend=3 -d

先程のようにdigしてみます。

# dig backend

; <<>> DiG 9.10.3-P4-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54302
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;backend.         IN  A

;; ANSWER SECTION:
backend.      600 IN  A   172.18.0.3
backend.      600 IN  A   172.18.0.5
backend.      600 IN  A   172.18.0.4

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Mon Jun 04 14:38:47 UTC 2018
;; MSG SIZE  rcvd: 94

3台すべて引くことができました。
このように動的にサーバのIPを引けるのはまさしくServiceDiscoveryの機能ですね。

ちなみにこの機能、実は次で説明するnet-aliasの機能を使っています。inspectするとaliasが設定されていることが分かります。

            "Networks": {
                "test_localnet": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "1f192383c18c",
                        "backend"
                    ],
                    "NetworkID": "1a962438f19eb63582d7ca65cb1249dd2520f0ff15dd4846960f084d99bda530",
                    "EndpointID": "b7351c7b02ebaa188fab9d94cc3f3072e7aea26d2fad48e9ca7360197eb3054b",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:04",
                    "DriverOpts": null
                }
            }

net-aliasを使ったService Discovery

--network-aliasというオプションを付けると、そのalias名でIPを引くことができます。
これは先程のサービス名のケースに加えて複数のサービスに同じaliasをつけることができ、そのalias名でDNSを引くと複数のサービスのコンテナIPが返ってきます。

backend2というサービスを追加して、同じaliasにします。

version: '2'
services:

  front-proxy:
    image: nginx
    container_name: front-proxy
    networks:
      - localnet

  backend:
    image: nginx
    networks:
      localnet:
        aliases:
          - backend  # here
  backend2:
    image: nginx
    networks:
      localnet:
        aliases:
          - backend  # here

networks:
  localnet: {}

digしてみます。

# dig backend

; <<>> DiG 9.10.3-P4-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18933
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;backend.         IN  A

;; ANSWER SECTION:
backend.      600 IN  A   172.18.0.3
backend.      600 IN  A   172.18.0.4

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Mon Jun 04 23:46:02 UTC 2018
;; MSG SIZE  rcvd: 71

このように別のサービスでもIPを引けるようになります。

linksを使ったService Discovery

links機能によってサービス名によるServiceDiscoveryと同じことができます。スケールさせてもそのlinks名で引けばちゃんと複数IPとれます。
ではnet-aliasのように複数サービスに同じlink名だとどうでしょうか?

docker-compose.yml

linksで複数のサービスに同じaliasをつけてみます。

version: '2'
services:

  front-proxy:
    image: nginx
    container_name: front-proxy
    networks:
      - localnet
    links:
      - web1:backend
      - web2:backend

  web1:
    image: nginx
    networks:
      - localnet
  web2:
    image: nginx
    networks:
      - localnet

networks:
  localnet: {}

digしてみる

# dig backend

; <<>> DiG 9.10.3-P4-Debian <<>> backend
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17331
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;backend.         IN  A

;; ANSWER SECTION:
backend.      600 IN  A   172.18.0.3

;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Tue Jun 05 00:29:09 UTC 2018
;; MSG SIZE  rcvd: 48

これもnet-aliasと同じようにできるのでは、と期待しましたがweb2のみでした。上書きされるのかもしれません。
Networkのaliasも設定はされません。

$ docker inspect test_web_1
...
            "Networks": {
                "test_localnet": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "1fcde37f3bc8",
                        "web1"  // web1だけ。backendは付かない
                    ],
                    "NetworkID": "dd9189045bd4a0a5b3261c18f301f2075e19a94ad62957d112a9dbe8c7848c4d",
                    "EndpointID": "2aca15273b355bfb6d005a0b0cfc64e2afd8e581c74364b52a5c0fefa8d3b6a3",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:02",
                    "DriverOpts": null
                }
            }

またlinks機能は設定したコンテナでしか参照できないので、あまり使い勝手はよくありません。

まとめ

Dockerの埋め込みDNSを使ったService Discoveryを紹介しました。
net-alias機能によって、スケールさせたり別サービスだとしてもまとめてIPを引けることが分かりました。 わざわざConsulなど外部のService Discoveryを用意しなくても、場合によってはdockerのみで対応できそうです。

ソース