16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

手元 resolver と Docker Embedded DNS Server を共存する

Last updated at Posted at 2018-08-03

tl;dr

  • /etc/docker/daemon.json には DNS 設定("dns": ['192.168.1.1'] など) を書かない
  • /etc/resolv.conf には non-localhost な自分を指すアドレスを指定する
    • あるいは、127.0.0.1 と、自分以外の resolver も指定する

解決したいこと

手元に resolver(unbound) を立てている状況で、docker build あるいは docker run で起動したコンテナ内では、外部の名前解決が可能でしたが、docker-compose up で起動したコンテナでは、外部の名前解決ができない状況でした。

この記事の前提

  • Linux 環境
  • Docker 1.10 以降
  • docker-compose

手元 resolver を建てたい経緯

かなり有名ですが、nginx-proxy という label を書くだけで手軽に proxy をしてくれる便利な image があります。

docker-compose.yml
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  whoami:
    image: jwilder/whoami
    environment:
      - VIRTUAL_HOST=whoami.local

(README より)

上記の docker-compose.yml の場合、 http://whoami.local でアクセスした時に nginx-proxy が HTTP Host Header を見て whoami コンテナに proxy してくれます。
これを local 環境で実行しようとする場合、 whoami.local が 127.0.0.1 で解決できる必要があります。
別解としては、ターミナルから curl --header 'HOST: whoami.local' 127.0.0.1 とするとアクセスだけなら可能ですが、ブラウザから見ようとすると Chrome の場合、Chrome Extension を入れたりしないといけません。

whoami.local を local 環境で 127.0.0.1 に解決するには、/etc/hosts に書くのが手っ取り早いですが、.local なサブドメインが今後大量に増えるかもしれない、あるいは別のドメインを使いたい場合、その都度 /etc/hosts に足すのは面倒です。

そこで .local をワイルドカードドメインとして 127.0.0.1 に解決したいという欲求が生まれます。
ちなみに /etc/hosts にはワイルドカードは書けないようです(自分が見つけれなかっただけかもしれません)。

手元 resolver で .local を解決する

例として手元に unbound を立てて、

/etc/unbound/unbound.conf
server:
  use-syslog: yes
  username: "unbound"
  directory: "/etc/unbound"
  trust-anchor-file: trusted-key.key
  interface: 0.0.0.0
  interface: ::0
  access-control: 0.0.0.0/0 allow

  local-zone: "local" redirect
  local-data: "local. IN A 127.0.0.1"

とすると、*.local127.0.0.1 で解決できるようになります。

手元環境でこの unbound を使う場合、ホスト側の /etc/resolv.conf に書く必要がありますが、

/etc/resolv.conf
nameserver 127.0.0.1

とすると、 docker は デフォルトで ホストの /etc/resolv.conf を、コンテナの /etc/resolv.conf にマウントするため、コンテナ自身が参照されてしまいコンテナ内でうまく名前解決ができません。

無理やりな解決方法(間違い)

そこで、私は /etc/docker/daemon.json で、コンテナのサブネットを固定し、 dns を default gateway に向けることで無理やりホスト側 unbound を使っていました。

/etc/docker/daemon.json
{
  "bip": "192.168.1.5/24",
  "fixed-cidr": "192.168.1.5/25",
  "dns": ["192.168.1.1"]
}

docker builddocker-compose build は docker network の default bridge を使うため、この仕組みでうまく行ってしまいますが、docker-compose up で上げたコンテナ内部から外部にアクセスしようとすると名前解決ができません。

docker-compose だと名前解決が出来ない理由

https://docs.docker.com/v17.09/engine/userguide/networking/configure-dns/
Docker 1.10 以降、container optionとして name, net-alias, link などを使うと、コンテナ内の resolver は 127.0.0.11 に立ち上がる Docker Embedded DNS Server が内部的に使われるようになりました。

この際、/etc/docker/daemon.json に書いたfixed-ciderサブネットは使われず、ネットワークは別に払い出されるもの (user-defined brige network など) が割り当てられます。(手元では 172.17.0.0/8)
また、コンテナ内の /etc/resolv.conf には、ホストの /etc/resolv.conf は使われず 127.0.0.11 が書かることになります。

docker-compsoe を使う場合、(おそらく)内部的に docker コマンドとしての linkname が使われているため、docker-compose up で起動したコンテナとネットワークは、resolver として Docker Embedded DNS が使われ fixed-cider とは別のサブネットに配置されます。

ここで問題になるのが、/etc/docker/daemon.json に書いた dns: ["192.168.1.1"] の記述です。
この場合、Docker Embedded DNS Server は、自身と 192.168.1.1 の resolver が指定されることとなり、内部向けの名前解決は自分で出来ますが、外部の名前解決は 192.168.1.1 を使うため名前解決ができない状況が発生します。

また、/etc/docker/daemon.jsondns: ["192.168.1.1"] を指定しなかったとしても、/etc/resolv.conf には nameserver 127.0.0.1 と書かれているため、この場合でも外部の名前解決は出来ません。

In the absence of the --dns=IP_ADDRESS..., --dns-search=DOMAIN..., or --dns-opt=OPTION... options, Docker uses the /etc/resolv.conf of the host machine (where the docker daemon runs). While doing so the daemon filters out all localhost IP address nameserver entries from the host’s original file.

公式Doc に書かれていますが、

Note: If you need access to a host’s localhost resolver, you must modify your DNS service on the host to listen on a non-localhost address that is reachable from within the container.

localhost の resolver を使う場合は non-localhost なアドレスを指定する必要が有ります。

正しい解決方法

Docker Embedded DNS Server に、ホストの /etc/resolv.conf を読んでもらうために、dns, dns-search, dns-opt 相当のオプションは、/etc/docker/daemon.json から削除します。

その上で、/etc/resolv.confnameserver には

  1. non-loopback な自分のアドレスを書く
  2. 127.0.0.1 の loopback アドレス + それ以外の resolver アドレスを書く

の どちらかをする必要があります。

参考

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?