K8sクラスター内で複数の(Core)DNSを使用する
クラスター内で複数のDNSを使用する
クラスター内で複数のDNSを使いたいケースがあるのかあまりわかりませんが、どうやったら出来るのか気になったので調べてみました。DNSの挙動テストなどでわざわざ新しいクラスターを用意できない/既存のアプリを使用してテストしたいみたいなときに使えるかもしれませんね。。。
背景
自分は今container platformの開発チームに所属しています。主な業務はkubernetes clusterの開発・運用ですが、業務の一部としてtenantのtrouble shootingを手伝うことがそれなりにあります。例えばtenant側のapplicationでwebAPIとの通信に異常が見られるときhost machineやcniに異常が無いかなどの調査依頼を受けます。
そういった依頼のひとつに、tenantのアプリでかなり低頻度で名前解決に失敗する異常がありCoreDNS側の不具合を調べて欲しいというものがありました。たとえばこれが特定のversionのCoreDNSに起因しているかをtestするために、現在使用しているCoreDNSと別のversionのCoreDNSをdeployしてテストすることは可能なのだろうか?と疑問に思ったのがきっかけです。
Kubernetes での名前解決
そもそもkubernetesでの名前解決がどのように行われているのか見てみましょう。
containerがkubernetes.default.cluster.localにアクセスするときもちろんCoreDNSにリクエストが行くわけですが、containerはどうやってCoreDNSのendpointを見つけるのでしょうか?
CoreDNSのエンドポイントを確認しておきます。
$ k get svc -nkube-system kube-dns
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 26d
それでは任意のcontainer内の/etc/resolv.con
を見てみます。
$ cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
kube-dns(CoreDNS)のendpointがnameserverに指定されています。つまりkubernetesコンポーネントの誰かがこのファイルをcontainer内に作ることで各pod/containerはCoreDNSで名前解決を行えるようになっています。
実際にはkubeletがこの役割を担っており、--cluster-dns flagにて指定されたaddressがnameserverとして指定されるようになっています。
さて、ではCoreDNSはこれらの(in-clusterな)recordをどうやって取得しているのでしょうか?
これはCoreDNSがendpointslicesオブジェクトを監視することで取得しています。(ソースとなるページが見つけられなかったのでどなたか知っている方いたら教えてください)
ちなみにin-clusterでないドメインはデフォルトではCoreDNSが走っているhost machineの/etc/resolv.conf
にforwardされるようになっています。(corednsのconfigmapを参照)
実際にやってみる
とりあえず現状で使用しているCoreDNSのマニフェストをそのままパクッてきて名前とかだけを変えて二個目のCoreDNSを立ててみます。
configmapやserviceaccountは既存のものを使いまわしてdeploymentとserviceだけ新しくします。(deploymentすら使いまわしでもok)
$ k get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
coredns 2/2 2 2 26d
coredns-2 2/2 2 2 109m
deployの方は名前を変えるだけでなくmatchLabelsも新しくするのを忘れないようにしてください。
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 26d
kube-dns-2 ClusterIP 10.96.0.20 <none> 53/UDP,53/TCP,9153/TCP 108m
serviceの方は名前を変えるだけでなく新しくclusterIPを指定するのを忘れないようにしてください。今回は10.96.0.20を使用します。
これで二個目のDNSの準備ができました。
container内の/etc/resolv.conf
の書き換えはkubeletの--cluster-dns flagを使うとnode内のpod丸ごと影響受けてしまうので今回は特定のpodだけ二個目のDNSを使うようにします。dnsPolicyとdnsConfigによってpodのマニフェスト内でDNSの指定ができるようになっているのでそれを使用します。
apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
command:
- sleep
- "infinity"
imagePullPolicy: IfNotPresent
restartPolicy: Never
dnsPolicy: None
dnsConfig:
nameservers:
- 10.96.0.20 # 新しいCoreDNSのエンドポイント
こんな感じのpodを用意して実際に新しいDNSが使えるかどうかnslookupしてみます。
$ k exec dnsutils -- nslookup google.com
Server: 10.96.0.20
Address: 10.96.0.20#53
Non-authoritative answer:
Name: google.com
Address: 216.58.220.142
できました。ちゃんとServerが新しいDNSのものになっています。
もちろんin-clusterなドメインも解決できます。
$ k exec dnsutils -- nslookup kubernetes.default.svc.cluster.local
Server: 10.96.0.20
Address: 10.96.0.20#53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
おわりに
kubernetesでの名前解決の実装がかなりシンプルで分かりやすかったですね。
CoreDNSのconfig mapではカスタムドメインの登録とかできるので自分用のCoreDNSをカスタマイズして遊べそうですね。endpoineslicesのlist/watchのrbacが必要なので、admin的なuserで無い人は自namespace内だけの名前解決とかになっちゃいそうですが。。。おわり。
Discussion