概要
CoreOS 上でのサービスディスカバリに SkyDNS2 を利用するという話。SkyDNS2 は Docker コンテナとして動かす。
SkyDNS2 とは
DNS サーバの一つ。データの保存先に etcd を使うことで分散 DNS サーバを実現してる。例えば、 5 台の CoreOS マシンからなるクラスタがあるとき、各マシンで SkyDNS2 を起動させておく。etcd にデータを置いているので、どのマシンの SkyDNS2 を利用しても同じ結果になる。
そして、後述する SRV レコードを返すことで単なる DNS サーバというよりはサービスディスカバリーを実現するためのサービスという位置づけになっている。
サービスディスカバリーに SRV レコードを使う
SRV レコードのポイントは
- ポート情報まで持ってること
- 負荷分散できること
ポート情報まで持っていること
DNS は通常名前 <---> IP の解決をするだけなので、あるサービス使いたい場合、「どの IP のどこのポートに繋げば良い?」みたいな場合に利用出来ない。が SRV
レコード を使うと、IP+ポート情報まで持たせられるのでサービスディスカバリーに利用出来る。
# 例
$ dig SRV 1.rails.skydns.local
;; ANSWER SECTION:
1.rails.skydns.local. 3600 IN SRV 10 100 8080 1.rails.skydns.local.
# ANSWER SECTION は以下の内容が書かれている
# service name, environment, TTL, priority, weight, service port, host name
さらに、 SRV レコードは負荷分散にも利用できる。
負荷分散できること
SRV レコードは、
- Priority
- Weight
という情報も持たせられる。 Priority の値の小さい方から優先的に通信される。 Weight は同じ優先順位のホスト郡での振り分け具合を決められる。
SkyDNS2 を使ったサービスディスカバリ
SkyDNS2 の Docker イメージ作成
# on CoreOS machine
core@core-01 ~ $ git clone https://github.com/skynetservices/skydns.git
core@core-01 ~ $ cd skydns
同梱されている Dockerfile
は language stack を使っていない。なるだけ language stack ベースのイメージを使いたいので以下のように編集してビルドした。
# Dockerfile
FROM golang:1.3.3
MAINTAINER Miek Gieben <miek@miek.nl> (@miekg)
ADD . /go/src/github.com/skynetservices/skydns
RUN go get github.com/skynetservices/skydns
EXPOSE 53
ENTRYPOINT ["skydns"]
core@core-01 ~ $ docker build -t skydns2 .
SkyDNS のデフォルト設定を etcd にセット
core@core-01 ~ $ curl -L -XPUT http://172.17.8.101:4001/v2/keys/skydns/config -d value='{"nameservers": ["8.8.8.8:53","8.8.4.4:53"]}'
SkyDNS2 コンテナ起動
# SkyDNS2 の UDP 53 ポートをホストの Private IPの 53 ポートに bind
core@core-01 ~/skydns $ docker run \
-d \
--name skydns2 \
-p 172.17.8.101:53:53/udp \
-e ETCD_MACHINES='172.17.8.101:4001,172.17.8.102:4001,172.17.8.103:4001' \
skydns2:latest \
-addr=0.0.0.0:53
core@core-01 ~ $ docker logs skydns2
[skydns] Oct 14 03:26:33.702 INFO | ready for queries on skydns.local. for tcp://0.0.0.0:53 [rcache 0]
[skydns] Oct 14 03:26:33.702 INFO | ready for queries on skydns.local. for udp://0.0.0.0:53 [rcache 0]
[skydns] Oct 14 03:27:51.679 INFO | no nameservers defined or name too short, can not forward
[skydns] Oct 14 03:27:51.679 INFO | no nameservers defined or name too short, can not forward
これで Core-01 の Private IP (172.17.8.101) を DNS nameserver として利用出来るになる
etcd にサービス情報をセット
Core-01 上に起動している logspout を logspout.1
のように、3 台登録することにする
logspout.1.json
{
"host":"172.17.8.101",
"port":8080
}
logspout.2.json
{
"host":"172.17.8.102",
"port":8080
}
logspout.3.json
{
"host":"172.17.8.103",
"port":8080
}
etcd にセット
core@core-01 ~ $ curl -L -XPUT http://172.17.8.101:4001/v2/keys/skydns/local/skydns/logspout/1 --data-urlencode value@logspout.1.json
{"action":"set","node":{"key":"/skydns/local/skydns/logspout/1","value":"{\n \"host\":\"172.17.8.101\",\n \"port\":8000\n}\n","modifiedIndex":9000,"createdIndex":9000},"prevNode":{"key":"/skydns/local/skydns/logspout/1","value":"","modifiedIndex":8995,"createdIndex":8995}}
core@core-01 ~ $ curl -L -XPUT http://172.17.8.101:4001/v2/keys/skydns/local/skydns/logspout/2 --data-urlencode value@logspout.2.json
{"action":"set","node":{"key":"/skydns/local/skydns/logspout/2","value":"{\n \"host\":\"172.17.8.102\",\n \"port\":8000\n}\n","modifiedIndex":8887,"createdIndex":8887}}
core@core-01 ~ $ curl -L -XPUT http://172.17.8.101:4001/v2/keys/skydns/local/skydns/logspout/3 --data-urlencode value@logspout.3.json
{"action":"set","node":{"key":"/skydns/local/skydns/logspout/3","value":"{\n \"host\":\"172.17.8.103\",\n \"port\":8000\n}\n","modifiedIndex":9022,"createdIndex":9022}}
CoreOS マシン上のプライマリ DNS サーバを SkyDNS2 に変更する
SkyDNS2 での設定のとおり、ドメインの権威が無い問い合わせが届いた時は 8.8.8.8:53 と 8.8.4.4:53 に転送するので、SkyDNS2 プライマリ DNS サーバとして扱える。
core@core-01 ~ $ cat /etc/resolv.conf
# This file is managed by systemd-resolved(8). Do not edit.
#
# Third party programs must not access this file directly, but
# only through the symlink at /etc/resolv.conf. To manage
# resolv.conf(5) in a different way, replace the symlink by a
# static file or a different symlink.
#nameserver 10.0.2.3
nameserver 172.17.8.101 # -> 書き換え
(直に書き換えるのは良くはない)
CoreOS マシン(コンテナホスト)から ping してみる
ping できるか確認してみる
(CoreOS には、dig が入ってない。)
core@core-01 ~ $ ping 1.logspout.skydns.local
PING 1.logspout.skydns.local (172.17.8.101) 56(84) bytes of data.
64 bytes from 172.17.8.101: icmp_seq=1 ttl=64 time=0.113 ms
64 bytes from 172.17.8.101: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 172.17.8.101: icmp_seq=3 ttl=64 time=0.039 ms
64 bytes from 172.17.8.101: icmp_seq=4 ttl=64 time=0.032 ms
64 bytes from 172.17.8.101: icmp_seq=5 ttl=64 time=0.048 ms
# Core-02 の logspout に振り分けされた
core@core-01 ~ $ ping logspout.skydns.local
PING logspout.skydns.local (172.17.8.102) 56(84) bytes of data.
64 bytes from 172.17.8.102: icmp_seq=1 ttl=64 time=0.369 ms
64 bytes from 172.17.8.102: icmp_seq=2 ttl=64 time=0.437 ms
64 bytes from 172.17.8.102: icmp_seq=3 ttl=64 time=0.409 ms
64 bytes from 172.17.8.102: icmp_seq=4 ttl=64 time=0.429 ms
# 今後は Core-03 に振り分けされた。
# 接続するごとに接続先ホストが変わる = 負荷分散されてることがわかる
core@core-01 ~ $ ping logspout.skydns.local
PING logspout.skydns.local (172.17.8.103) 56(84) bytes of data.
64 bytes from 172.17.8.103: icmp_seq=1 ttl=64 time=0.290 ms
64 bytes from 172.17.8.103: icmp_seq=2 ttl=64 time=0.417 ms
64 bytes from 172.17.8.103: icmp_seq=3 ttl=64 time=0.477 ms
ruby コンテナから SRV レコードを取得してみる
#!/usr/local/bin/ruby
# get_srv.rb
require 'resolv'
require 'pp'
resolver = Resolv::DNS.new(:nameserver => ['172.17.8.101'], :search => ['skydns.local'], :ndots => 1)
hosts = resolver.getresources('logspout.skydns.local', Resolv::DNS::Resource::IN::SRV)
pp hosts
puts ""
pp hosts.map{|h| (h.target.to_s + ":" + h.port.to_s) }
$ ruby get_srv.rb
[#<Resolv::DNS::Resource::IN::SRV:0x007f47882f6620
@port=8000,
@priority=10,
@target=#<Resolv::DNS::Name: 1.logspout.skydns.local.>,
@ttl=3600,
@weight=33>,
#<Resolv::DNS::Resource::IN::SRV:0x007f47882f5518
@port=8080,
@priority=10,
@target=#<Resolv::DNS::Name: 2.logspout.skydns.local.>,
@ttl=3600,
@weight=33>,
#<Resolv::DNS::Resource::IN::SRV:0x007f47882f45c8
@port=8080,
@priority=10,
@target=#<Resolv::DNS::Name: 3.logspout.skydns.local.>,
@ttl=3600,
@weight=33>]
["1.logspout.skydns.local:8000",
"2.logspout.skydns.local:8080",
"3.logspout.skydns.local:8080"]
ubuntu:14.04 コンテナから ping してみる
# on CoreOS
core@core-01 ~ $ docker run -it --rm --dns=172.17.8.101 ubuntu:14.04 /bin/bash
# コンテナ内から ping してみる
root@91fcca071b27:/$ ping 1.logspout.skydns.local
PING 1.logspout.skydns.local (172.17.8.101) 56(84) bytes of data.
64 bytes from 172.17.8.101: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from 172.17.8.101: icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from 172.17.8.101: icmp_seq=3 ttl=64 time=0.063 ms
64 bytes from 172.17.8.101: icmp_seq=4 ttl=64 time=0.053 ms
ubuntu:14.04 コンテナから dig してみる
image ubuntu:14.04 では dig が入ってないので入れる
# in ubuntu:14.04, install dig
root@952708774c41:/$ sudo apt-get update && sudp apt-get install dnsutils
skydns.local
のドメイン情報取得
root@41ea3b47bc69:/$ dig skydns.local
; <<>> DiG 9.9.5-3-Ubuntu <<>> skydns.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29922
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;skydns.local. IN A
;; ANSWER SECTION:
skydns.local. 3600 IN A 172.17.8.101
;; Query time: 17 msec
;; SERVER: 172.17.8.101#53(172.17.8.101)
;; WHEN: Tue Oct 14 09:44:11 UTC 2014
;; MSG SIZE rcvd: 46
1.logspout.skydns.local
の SRV 情報を取得。先ほど etcd にセットした /skydns/local/logspout/1
の情報が返ってくる。
root@41ea3b47bc69:/# dig SRV 1.logspout.skydns.local
; <<>> DiG 9.9.5-3-Ubuntu <<>> SRV 1.logspout.skydns.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51547
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; QUESTION SECTION:
;1.logspout.skydns.local. IN SRV
;; ANSWER SECTION:
1.logspout.skydns.local. 3600 IN SRV 10 100 8000 1.logspout.skydns.local.
;; ADDITIONAL SECTION:
1.logspout.skydns.local. 3600 IN A 172.17.8.101
;; Query time: 13 msec
;; SERVER: 172.17.8.101#53(172.17.8.101)
;; WHEN: Tue Oct 14 10:33:28 UTC 2014
;; MSG SIZE rcvd: 100
wildcard も利用できる。
*.logspout.skydns.local
の SRV 情報を取得。先ほど etcd にセットした /skydns/local/logspout/*
の情報が返ってくる。
root@41ea3b47bc69:/$ dig SRV *.logspout.skydns.local
; <<>> DiG 9.9.5-3-Ubuntu <<>> SRV *.logspout.skydns.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2579
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3
;; QUESTION SECTION:
;*.logspout.skydns.local. IN SRV
;; ANSWER SECTION:
*.logspout.skydns.local. 3600 IN SRV 10 33 8000 1.logspout.skydns.local.
*.logspout.skydns.local. 3600 IN SRV 10 33 8080 2.logspout.skydns.local.
*.logspout.skydns.local. 3600 IN SRV 10 33 8080 3.logspout.skydns.local.
;; ADDITIONAL SECTION:
1.logspout.skydns.local. 3600 IN A 172.17.8.101
2.logspout.skydns.local. 3600 IN A 172.17.8.102
3.logspout.skydns.local. 3600 IN A 172.17.8.103
;; Query time: 12 msec
;; SERVER: 172.17.8.101#53(172.17.8.101)
;; WHEN: Tue Oct 14 10:28:25 UTC 2014
;; MSG SIZE rcvd: 224
root@41ea3b47bc69:/$ dig SRV logspout.skydns.local
; <<>> DiG 9.9.5-3-Ubuntu <<>> SRV logspout.skydns.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44941
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3
;; QUESTION SECTION:
;logspout.skydns.local. IN SRV
;; ANSWER SECTION:
logspout.skydns.local. 3600 IN SRV 10 33 8000 1.logspout.skydns.local.
logspout.skydns.local. 3600 IN SRV 10 33 8080 2.logspout.skydns.local.
logspout.skydns.local. 3600 IN SRV 10 33 8080 3.logspout.skydns.local.
;; ADDITIONAL SECTION:
1.logspout.skydns.local. 3600 IN A 172.17.8.101
2.logspout.skydns.local. 3600 IN A 172.17.8.102
3.logspout.skydns.local. 3600 IN A 172.17.8.103
;; Query time: 13 msec
;; SERVER: 172.17.8.101#53(172.17.8.101)
;; WHEN: Tue Oct 14 10:28:59 UTC 2014
;; MSG SIZE rcvd: 222
サービス情報の自動登録
CoreOS 上の Docker コンテナのサービス情報を registrator を使って SkyDNS2 に自動登録する を参照
メモ
クライアント側で SRV レコードを使うようにしないといけない
SkyDNS2 で「あるサービスが、このホスト郡のこのポートで起動してるよ」というのは取得できるようになったが、クライアント側で、 SRV レコードを利用して接続するようにしないといけない。
負荷分散できるの便利
最初は、1.rails.skydns.local
でサービス情報がとれても、実際には、rails コンテナはその時その時で何台あるかわからない状態。番号まで指定して情報を取得するのは大変だなと思っていたが、*.rails.skydns.local
という感じでサービス情報郡を取得でき、さらに負荷分散までできる。クライアント側で SRV レコードを使うようにしさえすれば非常に便利だと思った。
REF
- skynetservices/skydns
- SKYDNS VERSION 2
- Docker container won't connect to etcd in another container on same host #42
- SkyDNS2のセットアップ方法
- etcd API
- Setting a key from a file
- Some application clients use SRV lookups, a few (to their embarrassment) do not.
- RFC2782の日本語訳(和訳)
- SRV record lookups returning more than 4 results are not queryable by Ruby DNS resolver