[Kubernetes]クラスタ内DNSの動作を確認する

#はじめに
[前回](https://qiita.com/dingtianhongjie/items/82d1c56e9712e19aecb0)ClusterIPの動作を確認しました。
その際、動作確認用のPodにはClusterIPのIPアドレスを設定していました。しかし、Kubernetesで実現するマイクロサービスでは、頻繁にリソースを追加削除します。各リソースにはKubernetesが自動でIPアドレスを割り当てますので、IPアドレスはデプロイの度に変わります。そのため、アプリケーション内ではIPアドレスではなく、DNS名を使用するのが望ましいでしょう。
そこで今回は、クラスタ内でのDNSの動作を確認したいと思います。

#クラスタ内のDNS
マニュアルには以下のような記載があります。クラスタ内では、kubernetesがDNSのサービスを提供しています。

> KubernetesのDNSはクラスター上でDNS PodとServiceをスケジュールし、DNSの名前解決をするために各コンテナに対してDNS ServiceのIPを使うようにKubeletを設定します。
> [kubernetes.io](https://kubernetes.io/ja/docs/concepts/services-networking/dns-pod-service/)

提供されるサービスは以下の2つがあります。

+ Aレコード
+ SRVレコード

##Aレコード
Serviceには、「[サービス名].[ネームスペース名].svc.cluster.local」という形式のDNS Aレコードが割り当てられています。
[前回](https://qiita.com/dingtianhongjie/items/82d1c56e9712e19aecb0)作成したClusterIPを例にすると、「cluster-ip.default.svc.cluster.local」になります。

###動作確認
[前回](https://qiita.com/dingtianhongjie/items/82d1c56e9712e19aecb0)の環境で、動作確認用のPodに指定したClusterIPのIPアドレスをFQDNに書き換えました。
これで同様に動作確認してみたいと思います。

```test_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: centos
      image: centos:latest
      command:
      - sh
      - -c
      args:
      - for i in 1 2 3 4 5 ; do curl -s http://cluster-ip.default.svc.cluster.local:8080 ; sleep 5; done ;exit 0
```

このマニフェストをapplyして、30秒ほど待ってlogを確認します。

```
$ kubectl apply -f test_pod.yaml
pod/test-pod created
$ kubectl logs test-pod | grep pod
pod1
pod2
pod2
pod1
pod2
```
IPアドレスを指定した時と同様にClusterIPに対してリクエストが送られていますね。

なお、今回は正式なFQDNを指定しましたが、Podとサービスが同じネームスペースの場合には、[サービス名]だけでも名前解決ができます。
これは、デプロイされるコンテナの/etc/resolve.confにsearchから始まる一行があるため、[サービス名]だけ、ネームスペースが異なる場合には、「[サービス名].[ネームスペース名]」だけでも名前解決ができます。

```
$ kubectl exec -it test-pod /bin/bash
[root@test-pod /]# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
```

##SRVレコード
SRVレコードは、一言で言うとポート番号を含めたDNSです（と私は理解しました）。SRVレコードは以下の形式になります。

[\_Serviceのポート名].[\_ポートのプロトコル].[サービス名].[ネームスペース名].svc.cluster.local

[前回](https://qiita.com/dingtianhongjie/items/82d1c56e9712e19aecb0)作成したClusterIPの詳細を確認します。

```
$ kubectl describe svc cluster-ip
Name:              cluster-ip
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"cluster-ip","namespace":"default"},"spec":{"ports":[{"name":"clus...
Selector:          app=nginx-dep
Type:              ClusterIP
IP:                10.101.47.213
Port:              cluster-port  8080/TCP
TargetPort:        80/TCP
Endpoints:         192.168.69.234:80,192.168.79.98:80
Session Affinity:  None
Events:            <none>
```
上記の結果から、このClusterIPのSVRレコードは、以下になります。

_cluster-port._TCP.cluster-ip.default.svc.cluster.local

###動作確認
動作確認用のPodとして、以下のマニフェストのPodをデプロイします。

```test_pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: centos
      image: centos:latest
      command:
      - sh
      - -c
      args:
      - tail -f /dev/null
```
```
$ kubectl apply -f test_pod2.yaml
pod/test-pod created
```
Podにログインします。

```
$ kubectl exec -it test-pod /bin/bash
[root@test-pod /]#
```
デフォルトでは、nslookupやdigコマンドがないため、インストールします。

```
[root@test-pod /]# yum install -y bind-utils
Failed to set locale, defaulting to C.UTF-8
CentOS-8 - AppStream                                                                                                                                                           1.7 MB/s | 6.6 MB     00:03
CentOS-8 - Base                                                                                                                                                                1.8 MB/s | 5.0 MB     00:02
CentOS-8 - Extras                                                                                                                                                              6.1 kB/s | 4.9 kB     00:00
Dependencies resolved.
・・・
Installed:
  bind-utils-32:9.11.4-26.P2.el8.x86_64  bind-libs-32:9.11.4-26.P2.el8.x86_64  bind-libs-lite-32:9.11.4-26.P2.el8.x86_64  bind-license-32:9.11.4-26.P2.el8.noarch  python3-bind-32:9.11.4-26.P2.el8.noarch
  python3-ply-3.9-7.el8.noarch

Complete!
```

nslookupとdigで確認します。

```
[root@test-pod /]]# nslookup -type=srv _cluster-port._TCP.cluster-ip.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

_cluster-port._TCP.cluster-ip.default.svc.cluster.local service = 0 100 8080 cluster-ip.default.svc.cluster.local.

[root@test-pod /]# dig _cluster-port._TCP.cluster-ip.default.svc.cluster.local SRV

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> _cluster-port._TCP.cluster-ip.default.svc.cluster.local SRV
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8930
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 3127dac7c4b7e34a (echoed)
;; QUESTION SECTION:
;_cluster-port._TCP.cluster-ip.default.svc.cluster.local. IN SRV

;; ANSWER SECTION:
_cluster-port._TCP.cluster-ip.default.svc.cluster.local. 30 IN SRV 0 100 8080 cluster-ip.default.svc.cluster.local.

;; ADDITIONAL SECTION:
cluster-ip.default.svc.cluster.local. 30 IN A   10.101.47.213

;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Apr 04 12:37:15 UTC 2020
;; MSG SIZE  rcvd: 259
```

どちらの結果でも、cluster-ipサービスのポート「8080」とホスト名「cluster-ip.default.svc.cluster.local」が解決できていますね。

#まとめ
KubernetesではIPアドレスは自動で割り当てられるため、IPアドレスではなくDNS名を指定して各リソース間で通信をするのが望ましいです。マイクロサービスを開発するには、今回確認したDNSの設定と動作を理解しておく必要がありますね。

#補足
ClusterIPに割り当てられるIPアドレスをユーザーが指定することができます。指定する場合は、「spec.clusterIP」フィールドに指定するIPアドレスを記載します。
指定できるIPアドレスは、APIサーバに設定されている「service-cluster-ip-range」内のアドレスである必要があります。

```
$ kubectl -n kube-system describe pod/kube-apiserver-k8s-master | grep range
      --service-cluster-ip-range=10.96.0.0/12
```

>ユーザー所有のIPアドレスを選択する
>[kubernetes.io](https://kubernetes.io/ja/docs/concepts/services-networking/service/#%e3%83%a6%e3%83%bc%e3%82%b6%e3%83%bc%e6%89%80%e6%9c%89%e3%81%aeip%e3%82%a2%e3%83%89%e3%83%ac%e3%82%b9%e3%82%92%e9%81%b8%e6%8a%9e%e3%81%99%e3%82%8b)