docker
consul

Consulを使ってDockerの名前解決を簡単に実現する

More than 3 years have passed since last update.

Dockerでサービス群をまとめて立ち上げる時はIPは毎回変わっちゃうので、名前解決が重要になるわけですが、これが結構面倒。

元々、figを使ってたので、linksに書いてればその名前でhost名を書き換えてくれてたので重宝してたんですが、これ起動順も制御してるっぽいので、双方向通信にすると循環参照でエラーに成って起動しません><

なので、手軽にやる方法を色々調べてみたのですが、Consulを使うのが簡単そうなので、そちらを試してみました。

追記:

CentOS7で動かす環境も作ってみたので、読むのメンドいから、取りあえず動かしたいって人はこっちをどうぞ。


Consulって何?

まず、そもそもConsulって何? ってとこなのですが、


  • Service Discovery

  • Health Check

  • Key/Value Store

を提供してくれるオーケストラレーションの一種です。私もまだしっかりとは理解できてないのですが、今回この"Service Discovery"が最初からDNSインタフェースを提供しているということで採用しました。


コンテナ上のresolved.confの参照を変更

Consulを準備する前に、Dockerコンテナ上の名前解決の設定を変更します。これを変更することで、後述する方法で、docekr0にバインドしたDNSをすべてのDockerコンテナから利用出来るようになります。

Linux(RH7系)とMacOSでboot2dockerを使った場合で、設定方法に差分があるので両方記載。

ちなみに、RH7系だといくつかのサイトで説明してある/etc/default/dockerじゃなくて、/etc/sysconfig/dockerを見るようなので要注意。

※ どうもRH7だと、セキュリティの設定が違うのか、Docker0にforwardしたサーバを探せないようなのでRUNした時点でresolve.confを変更しないと動きません。この辺、詳しい方居ましたらアドバイス頂ければと...


Linux(CentOS 7)の場合

$ echo "OPTIONS=--selinux-enabled --dns 172.17.42.1 --dns `grep nameserver /etc/resolv.conf|head -1|awk '{print $2}'` --dns-search service.consul" > /etc/sysconfig/docker

$
$ # 上記設定を変更するために再起動
$ sudo systemctl stop docker
$ sudo systemctl start docker


MacOSでboot2dockerを使ってる場合

$ boot2docker ssh sudo "ash -c \"echo EXTRA_ARGS=\'--dns 172.17.42.1 --dns $(scutil --dns | awk -F ': ' '/nameserver/{print $2}' | head -1) --dns-search service.consul\' > /var/lib/boot2docker/profile\""

$
$ # 上記設定を変更するために再起動
$ boot2docker stop
$ boot2docker up

これでDockerコンテナのresolve.confにdocker0が追加されるようになる。


Consulサービスを起動

つづいて、Consulのサービスをクラスタとして起動する。

まずは、Consul組込み済みのコンテナを取得。

$ docker pull progrium/consul

取得が終わったら、1台目のノードを立ち上げ。管理用ポート(HTTP)はlocalhostに、DNSはdocker0にバインドする。

$ docker run -d -p 8400:8400 -p 8500:8500 -p 172.17.42.1:53:53/udp --name node1 -h node1 progrium/consul -server -bootstrap-expect 3

2台目のノードからは、この1台目のノードに接続する形になるので、IPアドレスを取得。

$ JOIN_IP="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' node1)"

$ echo $JOIN_IP

2台目以降のノードをConsulサービスのクラスタに追加。1つのクラスタに対して推奨は3から5くらいのノードらしいので、とりあえず3つ追加。

$ docker run -d -h node2 progrium/consul -server -join $JOIN_IP

$ docker run -d -h node3 progrium/consul -server -join $JOIN_IP

下記の通り、HTTPベースでノードの状態が確認できる。

$ curl localhost:8500/v1/catalog/nodes

[{"Node":"node1","Address":"172.17.0.2"},{"Node":"node2","Address":"172.17.0.3"},{"Node":"node3","Address":"172.17.0.4"}]

また 'http://localhost:8500/ui/' にアクセスすることで、Web画面でも確認ができる。結構便利。

ss01.png


登録されたDNSの確認

続いて、Dockerコンテナ上からconsulに登録されたホスト名が引けることを確認。

$ docker run --rm -it koduki/centos7 /bin/bash

まずはノード単位。{host name}.node.consulの形式で引ける。

docker% ping node1.node.consul

PING node1.node.consul (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.091 ms
^C
--- node1.node.consul ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.055/0.073/0.091/0.018 ms
docker% ping node2.node.consul
PING node2.node.consul (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.096 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.076 ms
^C
--- node2.node.consul ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.076/0.086/0.096/0.010 msl

次はサービス名。{service name}.service.consulの形式で引ける。

docker% ping consul.service.consul

PING consul.service.consul (172.17.0.4) 56(84) bytes of data.
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.213 ms
^C
--- consul.service.consul ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.076/0.087/0.098/0.011 ms

docker% ping consul.service.consul
PING consul.service.consul (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.181 ms
^C
--- consul.service.consul ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.105/0.143/0.181/0.038 ms

こんな感じで、ラウンドロビンで複数のIPが引けてることが分かる。サービス用のDNSを毎回建てる必要無し! すばらっ!


サービスを登録

Consulサービスのクラスタは出来たので、新規にHTTPDなど独自のサービスも登録してみる。

まずは、クラスタに追加したいコンテナにconsulをインストール。

$ docker run --rm -it koduki/centos7 /bin/bash

docker# wget https://dl.bintray.com/mitchellh/consul/0.4.1_linux_amd64.zip && \
unzip 0.4.1_linux_amd64.zip && \
mv consul /usr/local/bin && \
rm -f 0.4.1_linux_amd64.zip

続いて、サービスの定義ファイルを書くディレクトリを作成。

docker# mkdir -p /etc/consul/conf.d

サービスは下記の用にJSONで指定する。

/etc/consul/conf.d/httpd.json

{"service": {"name": "tinyweb",  "tags": ["tinyweb", "web"],  "port": 8000}} 

ここまでで、準備は完了。

最後に対象のサービスを起動して、consulのクライアントとして登録する。

docker# python -m SimpleHTTPServer &

docker# consul agent -data-dir=tmp/consul -config-dir=/etc/consul/conf.d -join=consul.service.consul

これで、tinywebが新規にサービスとしてクラスタに登録された。

新規にDockerコンテナを立ち上げて適切に登録されているかを確認する。

$ docker run --rm -it koduki/centos7 /bin/bash

docker# curl consul.service.consul:8500/v1/catalog/nodes
[{"Node":"a3fcf553511f","Address":"172.17.0.5"},{"Node":"node1","Address":"172.17.0.2"},{"Node":"node2","Address":"172.17.0.3"},{"Node":"node3","Address":"172.17.0.4"}]
docker# curl consul.service.consul:8500/v1/catalog/services
{"consul":[],"tinyweb":["tinyweb","web"]}[root@538fbef09ddf /]#

新規ノードとして"a3fcf553511f"、新規サービスとして"tinyweb"が登録されてることが分かる。

もちろん、この状態でサービスのDNSは適切に登録されているので、下記のようにアクセスが出来る。

docker# curl -LI tinyweb.service.consul:8000  -o /dev/null -w '%{http_code}\n' -s

200

こんな感じで、Dockerコンテナの起動時にconsul agentを起動するように設定すれば、DNS管理は特に何もしなくてもOK! すばらっ!


まとめ

今回は、名前解決が目的だったので他のConsulの機能は基本調べてないけど、クラスタ参加時に特定の処理をしたり、サービスに対して一括してコマンド送ったりも出来たり、内蔵のKVSでデータ管理及び変更に起因するイベント管理をしたりできるので、クラウド環境でサーバが増減する環境では必須の機能かも。

オーケストレーションは、初めて触ったけど、思ったより簡単だったし、ちょっとしたUIもあるので、かなり良さ気。色々使い方を研究しないと。

それでは、Happy Hacking!


参考