はじめに
こんにちわ。@mikitです。
これは NTTコミュニケーションズ Advent Calendar 2017 の 18日目の記事です。
何気に Qiita 初投稿でドキドキしています。。!
consul を使って Docker コンテナでのさまざまな名前解決をしつつ、外部に対して公開していない内部ドメインも検索対象に入れたいと思ったことはありませんか?
consul 単体ではそれを行うことができませんが、unbound と組み合わせることで TLD 毎の参照先を自由にコントロールすることができるようになります。
ニッチすぎて誰かの役に立つのか不安ではありますが、実際にうちのチームではこの構成で名前解決をしていますので、メモを兼ねて。
用語解説
- unbound
- 権威DNSサーバ
- 名前に対する IPアドレスの組み合わせや階層構造などを保持し、公開するためのサーバ。
- キャッシュDNSサーバ
- 端末に「DNSサーバ」として設定したり、サーバの
/etc/resolv.conf
に設定したりするアレです。 - 権威DNSサーバの階層を渡り歩き、端末やサーバからの名前解決要求に答えます。
- 端末に「DNSサーバ」として設定したり、サーバの
前提条件
-
内部ドメイン名
internal.example.
- このドメインの情報を保持する権威DNSサーバの IPアドレス:
192.0.2.53
-
内部ドメインに設定されているデータ (抜粋)
www.internal.example. IN A 192.0.2.80
ns.internal.example. IN A 192.0.2.53
sv.internal.example. IN A 192.0.2.1
-
今回設定するサーバのIPアドレス:
192.0.2.1
-
consul 起動 yml 例
-
ports
で port 8600 を port 53 に bind しています。 - consul 自身で解決できない名前解決要求については
9.9.9.9
で行っています。
-
docker-compose.yml
version: '2.1'
services:
consul:
container_name: consul
image: consul:0.9.3
command: agent -server -bootstrap-expect 3 -recursor 9.9.9.9 -ui -client 0.0.0.0
ports:
- "8300:8300"
- "8301:8301/udp"
- "8301:8301/tcp"
- "8302:8302/udp"
- "8302:8302/tcp"
- "8400:8400"
- "8500:8500"
- "53:8600/udp" # DNS
- "53:8600/tcp" # DNS
hostname: consul
network_mode: "bridge"
restart: always
- サーバ全体での DNS 参照先は 127.0.0.1 となっており、現在は consul が名前解決を担当しています。
/etc/resolv.conf
nameserver 127.0.0.1
- この状態ですと、DNS の階層構造に定義されていない
internal.example.
については9.9.9.9
は知ることができないため、名前解決できません。
解決方法
-
unbound の設定をします。
-
参考までに Dockerfile を載せますが、dockerhub の適当なイメージを利用されても構いません。
Dockerfile
FROM alpine:latest
MAINTAINER Miki TAKATA "mikit@example.jp"
RUN apk add --update unbound drill openssl \
&& rm -rf /var/cache/apk/*
COPY unbound.conf /etc/unbound/
EXPOSE 53/tcp
EXPOSE 53/udp
CMD ["/usr/sbin/unbound", "-d"]
- 最低限必要な unbound の設定
- 解説
-
access-control
にて名前解決要求を許可する IPアドレス帯を設定します。 -
stub-zone
にて、ドメイン配下の名前解決要求については指定した権威DNSサーバに問い合わせを行い、クライアントに答えを返すように設定します。.consul
ドメインの場合は192.0.2.1
の port 8600、internal.example
の場合は192.0.2.53
。 -
forward-zone
にて、stub-zone
以外の名前解決要求が来た時に、問い合わせを転送するよう設定します。
-
unbound.conf
server:
interface: 0.0.0.0
access-control: 192.0.2.0/24 allow
use-syslog: no
log-time-ascii: yes
log-queries: yes
val-log-level: 2
root-hints: /etc/unbound/root.hints
rrset-roundrobin: yes
stub-zone:
name: "consul"
stub-addr: 192.0.2.1@8600
stub-zone:
name: "internal.example"
stub-addr: 192.0.2.53
forward-zone:
name: "."
forward-addr: 9.9.9.9
この状態で適宜 build します。
> docker build -t unbound .
- docker-compose.yml
- unbound 起動用の ymlです。
- なぜか
network_mode: "host"
にしないと意図通り動いてくれなかったのでおまじない的に入れています。ヘタレですみません。。教えてえらい人!
docker-compose.yml
version: '2.1'
services:
unbound:
container_name: unbound
hostname: unbound
image: unbound
ports:
- "53:53/tcp"
- "53:53/udp"
volumes:
- /etc/localtime:/etc/localtime:ro
network_mode: "host"
- unbound 起動
-
start of service
を確認
-
> docker-compose up
Dec 17 21:23:01 unbound[1:0] notice: init module 0: validator
Dec 17 21:23:01 unbound[1:0] notice: init module 1: iterator
Dec 17 21:23:02 unbound[1:0] info: start of service (unbound 1.6.7).
- consul の DNS ポート番号を変更して起動
- その他、この時点で、consul へ飛んでくる名前解決要求は
.consul
に限定されるので-recursor 9.9.9.9
オプションは外してもいいはず。
- その他、この時点で、consul へ飛んでくる名前解決要求は
docker-compose.yml
ports:
- "8600:8600/udp" # DNS
- "8600:8600/tcp" # DNS
確認
-
internal.example
配下の名前解決
> dig www.internal.example @192.0.2.1 +short
192.0.2.80
- consul の名前解決
> dig consul-sv.node.dc1.consul @192.0.2.1 +short
192.0.2.1
- その他ドメインの名前解決
> dig qiita.com @192.0.2.1 +short
54.249.227.135
46.51.250.143
それぞれ名前解決できました :)
おまけ
- consul は名前解決の結果をキャッシュしない。
-
-recursor 9.9.9.9
を指定している場合、名前解決要求が来るたびに9.9.9.9
に問い合わせを行い、結果を待つことになり効率が悪い。 - unbound を間に挟むことで、その他ドメインの問い合わせについては結果をキャッシュするようになるので、その他ドメインの問い合わせが多い環境では効率がよくなるはず。
-
9.9.9.9
はQuad9
といって IBM のやってる Public DNS のサービス。https://www.quad9.net/
まとめ、雑感
- 今回、consul と unbound の組み合わせを取り上げたけれど、他の DNS を使うサービスディスカバリのソフトウェアとの連携でも同じような手法を使えると思うので、色々やってみてほしいです。
- 自分の中では割と当然にあるものでも、こうやってまとまった形でアウトプットするのは思った以上に大変だった。
- アドベントカレンダーに誘ってくれた @iwashi に感謝