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画面でも確認ができる。結構便利。
登録された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!