CoreDNS + etcd を使ってSRVレコードを解決する

# 概要

[Kubernetes 1.11 ](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.11.md#kubernetes-111-release-notes)からkube-dnsに変わってデフォルトで有効となったCoreDNSとetcdを利用してSRVレコードが解決できるようにしてみました。

## SRVレコードとは
SRVレコードはサービスを登録できるレコードです。例えばAレコードだと特定のドメインに対してIPを定義するわけですが、SRVレコードだとHost, Port番号, Priority, ロードバランシングの重み付け等より詳細な情報を定義できます。

## CoreDNS と etcd の組み合わせ
SRVレコード自体は上記の組み合わせでないと実現できないわけではありません。
しかし、CoreDNSはPluggableな実装になっているので、デフォルトで用意されているetcdプラグインを使うことで、etcdに登録したデータを元にレコードを解決できます。なので、サーバ or プロセスが起動したタイミングでetcdにレコードを登録するように設定しておけば、自動的にDNSで名前解決ができるようになるため、容易にService Discoveryが実現できます。


## 実行環境
- CoreDNS 1.2.0
- etcd 3.3.0
- etcdctl 3.3.0

## 環境構築
### CoreDNS

- Corefile作成

```shell:Corefile
example.local:1053 {
    etcd example.local {
        path /skydns
        endpoint http://localhost:2379
    }
    errors
    log
}
```

```shell:
$ ./coredns -conf Corefile
example.local.:1053
2018/09/15 11:36:26 [INFO] CoreDNS-1.2.0
2018/09/15 11:36:26 [INFO] darwin/amd64, go1.10.3, 2e322f6
CoreDNS-1.2.0
darwin/amd64, go1.10.3, 2e322f6
```


### etcd

```shell:
$ ./etcd
2018-09-15 11:32:43.537699 I | etcdmain: etcd Version: 3.3.0+git
2018-09-15 11:32:43.537737 I | etcdmain: Git SHA: ec5ff10
2018-09-15 11:32:43.537746 I | etcdmain: Go Version: go1.10.3
2018-09-15 11:32:43.537755 I | etcdmain: Go OS/Arch: darwin/amd64

///// 省略 /////

2018-09-15 11:32:43.633125 I | embed: listening for peers on 127.0.0.1:2380
{"level":"info","ts":1536978765.001604,"caller":"raft/raft.go:872","msg":"8e9e05c52164694d is starting a new election at term 3"}
{"level":"info","ts":1536978765.001651,"caller":"raft/raft.go:690","msg":"8e9e05c52164694d became candidate at term 4"}
{"level":"info","ts":1536978765.0016952,"caller":"raft/raft.go:770","msg":"8e9e05c52164694d received MsgVoteResp from 8e9e05c52164694d at term 4"}
{"level":"info","ts":1536978765.0017211,"caller":"raft/raft.go:727","msg":"8e9e05c52164694d became leader at term 4"}
{"level":"info","ts":1536978765.0017483,"caller":"raft/node.go:306","msg":"raft.node: 8e9e05c52164694d elected leader 8e9e05c52164694d at term 4"}
2018-09-15 11:32:45.012364 I | embed: ready to serve client requests
2018-09-15 11:32:45.012760 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379]} to cluster cdf818194e3a8c32
2018-09-15 11:32:45.013018 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged!
```

- SRVレコード登録

例として、webサーバとアプリケーションサーバ用のレコードを登録する

```shell:
// srv records for web server
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_web/web-server01 '{"host":"1.1.1.1","port":80}'
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_web/web-server02 '{"host":"1.1.1.2","port":80}'
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_web/web-server03 '{"host":"1.1.1.3","port":80}'

// srv records for application server
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_app/app-server01 '{"host":"1.1.2.1","port":8080}'
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_app/app-server02 '{"host":"1.1.2.2","port":8080}'
$ ETCDCTL_API=3 ./etcdctl put /skydns/local/example/_tcp/_app/app-server03 '{"host":"1.1.2.3","port":8080}'
```

## 名前解決できるか確認
applicationとwebの両方を名前解決してみます。それぞれ登録したサーバが取得できます。

```shell:
$ dig +short @localhost -p 1053 SRV _app._tcp.example.local
10 33 8080 app-server01._app._tcp.example.local.
10 33 8080 app-server02._app._tcp.example.local.
10 33 8080 app-server03._app._tcp.example.local.

$ dig +short @localhost -p 1053 A _web._tcp.example.local
1.1.1.1
1.1.1.2
1.1.1.3

$ dig +short @localhost -p 1053 SRV _web._tcp.example.local
10 33 80 web-server01._web._tcp.example.local.
10 33 80 web-server02._web._tcp.example.local.
10 33 80 web-server03._web._tcp.example.local.

$ dig +short @localhost -p 1053 A _app._tcp.example.local
1.1.2.1
1.1.2.2
1.1.2.3
```

## まとめ
etcdのv2からv3への以降でetcdctlのコマンドがかなり変更されているため注意が必要ですが、比較的簡単にSRVレコードがひけるようになりました。APIで簡単にレコードが登録できるので、Kubernetes以外でも有効利用できます。
今回はSRVレコードだけしか記載してないが当然Aレコードとかも登録できるので、社内のシステムとかでも使るし、ベアメタル環境でService Discoveryするなどのユースケースも考えられます。