Kubernetesの名前解決を確認する
最近ではドキュメントも充実した感のあるKubernetesですが、実際の挙動ってどうなっているの?という部分に関しては ドキュメントを漁って読むに加えて、実際に手を動かして調べてみることでだいぶ理解が深まっていく感覚があります。 というわけで今回のテーマはKubernetesにおけるPodの名前解決はどのように動作しているのか、です。
公式ドキュメントのDNS for Services and Pods - Kubernetesを実際に手を動かしながら確認していきます。
Serviceリソースの名前解決
それでは早速シンプルな構成のk8s環境を用意して、その挙動を確認していきます。
Node: 1つ Master: 1つ
環境はGKEでも何でもいいのですが、今回はKOPSを使ってAWS上にk8s環境を用意することにしました。 Kopsでクラスタを構成すると以下の様にpodが作成されます。
$ kubectl get pods -l k8s-app=kube-dns -n kube-system NAME READY STATUS RESTARTS AGE kube-dns-56cf5cb57d-7gghr 3/3 Running 0 4d kube-dns-56cf5cb57d-n47jl 3/3 Running 0 4d
kube-dnsというpodが作成されていることがわかります。まずは適当なpodを作ってみます。 以下のbusybox-pod.yamlを用意しました。
apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox restartPolicy: Always
$ kubectl create -f busybox-pod.yaml $ kubectl get pods NAME READY STATUS RESTARTS AGE busybox 1/1 Running 0 2m
またServiceリソースも作成しておきます。
apiVersion: v1 kind: Service metadata: name: busybox-svc labels: app: busybox-svc spec: ports: - protocol: TCP name: busybox-svc-port port: 1234 targetPort: 5678 selector: app: busybox
$ kubectl create -f busybox-svc.yaml
以下のように作成されました。
$ kubectl get svc --all-namespaces NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default busybox-svc ClusterIP 100.64.233.34 <none> 6379/TCP 1m default kubernetes ClusterIP 100.64.0.1 <none> 443/TCP 5d kube-system kube-dns ClusterIP 100.64.0.10 <none> 53/UDP,53/TCP 5d
このpodに入って諸々の設定を確認してみます。
$ kubectl exec -it busybox -- /bin/sh / # cat /etc/hosts # Kubernetes-managed hosts file. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet fe00::0 ip6-mcastprefix fe00::1 ip6-allnodes fe00::2 ip6-allrouters 100.103.66.147 busybox / # cat /etc/resolv.conf nameserver 100.64.0.10 search default.svc.cluster.local svc.cluster.local cluster.local ap-northeast-1.compute.internal options ndots:5
/etc/hostsにある100.103.66.147 busybox
はこのpodのIPアドレスとホスト名(pod名)ですね。
resolv.confに書いてあるnameserver 100.64.0.10
はkube-dnsのserviceのClusterIPとなります。
searchには、namespace名.svc.cluster.localとAWSのリージョン別のドメイン名が定義されていることがわかります。
searchに設定したドメインはその名前をつけてサーチしてくれるという意味となるので、
例えばnslookup hogehogeとした場合には
を順に検索してくれるということになります
それでは、作成したserviceリソースを名前解決してみます。
busyboxとは別のpodを作ります。
apiVersion: v1 kind: Pod metadata: name: dnsutils labels: name: dnsutils spec: containers: - name: dnsutils image: tutum/dnsutils command: - sleep - "3600"
$ kubectl create -f dnsutil.yaml $ kubectl exec -it dnsutils -- /bin/sh
/# nslookup busybox-svc Server: 100.64.0.10 Address: 100.64.0.10#53 Name: busybox-svc.default.svc.cluster.local Address: 100.64.233.34
busybox-svc
で設定されている100.64.233.34
が返されました。
これでIPアドレスまでは特定できましたが、PORTはどのように通知されるのでしょうか。
kubernetesではkube-dnsにサービス名、protocol, port名, プロトコルを指定してSRVレコードを問い合わせるとポート番号が返されます。
/# nslookup > set type=SRV > _busybox-svc-port._tcp.busybox-svc.default.svc.cluster.local Server: 100.64.0.10 Address: 100.64.0.10#53 _busybox-svc-port._tcp.busybox-svc.default.svc.cluster.local service = 10 100 1234 busybox-svc.default.svc.cluster.local.
service = 10 100 1234 busybox-svc.default.svc.cluster.local.
が返ってきました。
これはservice = [Priority] [Weight] [Port] [Target]
という定義なのでPortが1234となり、
Serviceで指定したPortとなっていることがわかります。
ここまでをまとめると、
- resolve.confに定義されるnameserverはkube-dnsのserviceリソースのCluster-IPが設定される
- resolve.confに定義されるsearchにはnamespace名.svc.cluster.local...が設定される
- nslookupでサービス名を問い合わせると、kube-dnsからIPアドレスを返される
- nslookupでサービス名、protocol, port名, プロトコルを指定してSRVレコードを問い合わせるとポート名が返されます。
ということがわかりました。
別のnamespaceでの名前解決
今まではnamespace: defaultでの名前解決を実行していましたが、別のnamespaceではどうでしょうか。
新しい名前空間を作ります。
$ kubectl create ns hogehoge
先ほどのyamlを書き換えて、podとsvcを同じように作成します。
apiVersion: v1 kind: Pod metadata: name: busybox-hogehoge namespace: default spec: containers: - image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox-hogehoge restartPolicy: Always
busybox-svc-hogehoge.yaml apiVersion: v1 kind: Service metadata: name: busybox-svc-hogehoge labels: app: busybox-svc-hogehoge namespace: hogehoge spec: ports: - protocol: TCP name: busybox-svc-hogehoge-port port: 1234 targetPort: 5678 selector: app: busybox
そして、このような状態となります。
$ kubectl get svc -n hogehoge NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE busybox-svc-hogehoge ClusterIP 100.64.81.224 <none> 1234/TCP 1m $ kubectl get pod -n hogehoge NAME READY STATUS RESTARTS AGE busybox-hogehoge 1/1 Running 0 2m
この状態でnamespace: defaultにあるdnsutilで名前解決をしてみます。
/# nslookup busybox-svc-hogehoge Server: 100.64.0.10 Address: 100.64.0.10#53 ** server can't find busybox-svc-hogehoge: NXDOMAIN
別のnamespaceの名前解決はできません。 下のようにサービス名にnamespaceをくっつけると返ってくるようになりました。
root@dnsutils:/# nslookup busybox-svc-hogehoge.hogehoge Server: 100.64.0.10 Address: 100.64.0.10#53 Name: busybox-svc-hogehoge.hogehoge.svc.cluster.local Address: 100.64.81.224
これは、前述のresolve.conf
の設定でsearchの項目で設定されている値を考えれば、こうなるのも頷けます。
つまり
- サービス名 + namespacesで他のnamespaceのサービスの名前解決ができる
- resolve.confのsearchの項目で制御している
ということがわかります。
Podの名前解決
ここまではKubernetesのServiceの名前解決を見ていましたが、続いてpodの名前解決をみていきます。
podのDNSのAレコードは以下のように登録されています。
pod-ip-address.my-namespace.pod.cluster.local
例えば、1.2.3.4
のpodがnamespace: default
に置かれていた場合、
1-2-3-4.default.pod.cluster.local
という名前で登録されています。
実際に確認してみます。先ほどのdnsutilに入って、podの名前解決をしてみます。
$ kubectl exec -it dnsutils -- /bin/bash # nslookup 100-103-66-157.default.pod.cluster.local Server: 100.64.0.10 Address: 100.64.0.10#53 Name: 100-103-66-157.default.pod.cluster.local Address: 100.103.66.157
以上のようにPodのIPが返ってくることがわかりました。
PodのDNSポリシー
Kubernetesでは現在、以下の方式でPod単位でDNSの設定することができます。 これらのDNSポリシーはPodのspec:dnsPolicyフィールドで指定します。 それぞれのDNSポリシーを確認します。
- "Default": Podが動作しているNodeから名前解決の設定を継承する。
- "ClusterFirst": kube-dnsに設定されているstubDomainsやupstreamNameserversを参照する。そうでなければ、Nodeから名前解決の設定を継承する
- "ClusterFirstWithHostNet": NodeとPodを同じサブネットにおくhostNetworkが有効な場合はこれを設定する必要がある
- "None": Kubernetes1.9から追加された機能でPodのDNS設定はdnsConfigフィールドのみを使用して提供される。
※ Defaultは、デフォルトDNSポリシーではありません。DNSポリシーを指定しない場合、 "ClusterFirst"が選択されます。
実際にそれぞれのDNSポリシーを適用してみます。それぞれのポリシーを設定したPodを用意してみます。 まずはDefaultから。
apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox dnsPolicy: Default restartPolicy: Always
$ kubectl create -f busybox-default.yaml
中に入って確認してみます
$ kubectl exec -it busybox-default -- /bin/sh / # cat /etc/resolv.conf nameserver XX.XX.XX.2 (AWSのVPCのネームサーバのIPアドレス) search XXXX.compute.internal
このように確かにpodが存在しているNodeと同じ設定が入っているようです。 そして、resolve.confの設定が書き換わっていますので、クラスター内部の名前解決はできない状態となってしまいます。
/ # nslookup kubernetes.default Server: XX.XX.XX.2 Address 1: .XX.XX.XX.2 <nodeのhostname>.<zone>.internal nslookup: can't resolve 'kubernetes.default' / # / # / # nslookup busybox-svc.default.svc.cluster.local Server: XX.XX.XX.2 Address 1: .XX.XX.XX.2 <nodeのhostname>.<zone>.internal nslookup: can't resolve 'busybox-svc.default.svc.cluster.local'
続いてClusterFirstですが、こちらは何も設定しなければこのポリシーが適用されるため、 前回のKubernetes内部の名前解決と同じ設定が反映されます。確認は割愛します 前述したこちらの設定が含まれることになります。
$ kubectl exec -it busybox -- /bin/sh / # cat /etc/hosts # Kubernetes-managed hosts file. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet fe00::0 ip6-mcastprefix fe00::1 ip6-allnodes fe00::2 ip6-allrouters 100.103.66.147 busybox / # cat /etc/resolv.conf nameserver 100.64.0.10 search default.svc.cluster.local svc.cluster.local cluster.local XXXX.compute.internal options ndots:5
続いて、ClusterFirstWithHostNetです。まずそもそもHostNetworkをtrueにする必要があります。HostNetwork設定とは、Nodeのネットワークインターフェースを直接参照するための設定です。 例えば、以下のようにHostNetworkをtrueにして、dnsPolicyをClusterFirstWithHostNetに設定してみます。
apiVersion: v1 kind: Pod metadata: name: busybox-clusterfirstwithhostnet namespace: default spec: containers: - image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox-clusterfirstwithhostnet dnsPolicy: ClusterFirstWithHostNet hostNetwork: true restartPolicy: Always
podを作成してみます。
$ kubectl create -f busybox-clusterfirstwithhostnet.yaml $ kubectl get pods -o wide
PodのIPがNODEのIPと同一のものになっていることがわかります。 このpodのDNSの設定はどのようになっているのかを確認してみましょう。
$kubectl exec -it busybox-clusterfirstwithhostnet -- /bin/sh/ # cat /etc/resolv.conf nameserver 100.64.0.10 search default.svc.cluster.local svc.cluster.local cluster.local XXXX.compute.internal options ndots:5
ClusterFirstと同じ設定が入っています。 確かにこちらの[コード](https://github.com/kubernetes/kubernetes/blob/317853c90c674920bfbbdac54fe66092ddc9f15f/pkg/kubelet/network/dns/dns.go#L268をみてみると、ClusterFirstと同じ設定が入るようになっています。
そして最後にdnsPolicyがNoneの場合です。
この場合、クラスタ内のkube-dnsのconfigmapにstub-domainとupstream DNS serversを設定することができます。 また、pod個別にDNSへ設定することもできます。
例として、以下のようなConfigMapとなります。
apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"acme.local": ["1.2.3.4"]} upstreamNameservers: | ["8.8.8.8", "8.8.4.4"]
pod個別に設定する場合は以下のようになります。
apiVersion: v1 kind: Pod metadata: namespace: default name: busybox-none spec: containers: - name: busybox-none image: busybox dnsPolicy: "None" dnsConfig: nameservers: - 1.2.3.4 searches: - ns1.svc.cluster.local - my.dns.search.suffix options: - name: ndots value: "2" - name: edns0
実際にみてみます。
$ kubectl exec -it busybox -- cat /etc/resolv.conf nameserver 1.2.3.4 search ns1.svc.cluster.local my.dns.search.suffix options ndots:2 edns0
この設定では、acme.localを持ったDNS要求は1.2.3.4でlistenされているDNSに転送されます。 それ以外は8.8.8.8と8.8.4.4に転送されるという設定となります。
dnsPolicyによってそれぞれのresolve.confが書き換わるということがわかりました。
終わりに
KubernetesのDNS設定は多岐に渡り、それがyamlファイルに抽象化されていてわかりにくいところがありましたが、 実際に確認してみますと、結局のところ、podでのconfを書き換えているだけということを理解することができました。 このほかにもKuberenetesのドキュメントには多くの設定項目がありますが、理解が難しい時にはこのように手を動かして確認して行きたいですね。