KubernetesのDNSについて動作確認してみるよ～

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

今回はKubernetesのDNSについて勉強がてら動作の確認をしてみましたので
それを書き記しておこうと思います。

# 環境
環境はいつも通り、Proxmox VEというハイパーバイザ上にubuntu22.04のVMを4台立ち上げ、そこにmicrok8sをインストール。master×1台 + worker×3台というKubernetesクラスタ構成となります。
```
root@k8s-master:~/yaml# kubectl get node -o wide
NAME           STATUS   ROLES    AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-worker01   Ready    <none>   3m58s   v1.26.4   192.168.2.31   <none>        Ubuntu 22.04.2 LTS   5.15.0-71-generic   containerd://1.6.15
k8s-worker03   Ready    <none>   2m37s   v1.26.4   192.168.2.33   <none>        Ubuntu 22.04.2 LTS   5.15.0-71-generic   containerd://1.6.15
k8s-master     Ready    <none>   47h     v1.26.4   192.168.2.30   <none>        Ubuntu 22.04.2 LTS   5.15.0-71-generic   containerd://1.6.15
k8s-worker02   Ready    <none>   47h     v1.26.4   192.168.2.32   <none>        Ubuntu 22.04.2 LTS   5.15.0-71-generic   containerd://1.6.15
```

![Image](https://user-images.githubusercontent.com/127835743/236710394-6fd2b70c-a003-44e3-98b7-a2c17fde7e83.png)

またdefaultのnamespaceには何もデプロイしておりません。
出力はされていますがデフォルトで作成されるものになりますので、気にしないでください。
```
root@k8s-master:~/yaml# kubectl get pod,svc -o wide
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/kubernetes   ClusterIP   10.152.183.1   <none>        443/TCP   47h   <none>
```

# 用語
namespace
---
>Kubernetesは、同一の物理クラスター上で複数の仮想クラスターの動作をサポートします。 この仮想クラスターをNamespaceと呼びます。

https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/namespaces/

私の今回の環境でいうと物理クラスタ：4台のnodeで構成されたもの。仮想クラスタ：default namespaceとかkube-system namespaceのこと。ということですね。物理的には1つのクラスタしかないけれど、仮想的にはそれ以上のクラスタが存在している。それを実現するためにnamespaceという技術・概念がある。ということになるかと。


今回私の検証は最終的に3つのnsが絡んできますが、仮想クラスタと物理クラスタをイメージに落とし込むと次のようになると思います。私の認識が違くなければ。

![Image](https://user-images.githubusercontent.com/127835743/236763326-d381a667-9b71-4168-b999-3276760648ec.png)



# default namespaceに検証用のpod,ds,svcをデプロイして名前解決の動作確認をする
まずは検証用のpodをデプロイします。今回用意したyamlファイルは以下となります。
```pod-deb-ubuntu.yaml
apiVersion: v1
kind: Pod
metadata:
  name: deb-ubuntu
spec:
  containers:
  - name: deb-ubuntu-con
    image: shotaohtsuka/deb-ubuntu-image:latest
    command: ['tail', '-f', '/dev/null']
```

yamlについて
- imageですが、私が自分で用意したものとなります。
ざっくり記載しますが、ubuntu:22.04のdocker imageをベースにping,ip,vi,traceroute,nslookup,curl,systemctlコマンドをインストールしたdocker imageになります。今回はこのimageをもとにデプロイしたpodにアクセスし、そこからnslookupやcurlを使ってDNSの名前解決ができるかを確認していきます。

https://qiita.com/ohtsuka-shota/items/97465435cfc0293d7eee

- commandでtail -f /dev/nullを実行するようにしておりますが、これを実行しないと以下の記事に書いている通り、pod内の稼働中のプロセスが無いことが原因で、podが勝手に停止をしてしまうので、その対策となります。

https://qiita.com/ohtsuka-shota/items/c351482e8023be0ec635


このyamlファイルをもとにデプロイします。
今回はk8s-worker03 nodeにデプロイされましたね。
```
root@k8s-master:~/yaml# kubectl create -f pod-deb-ubuntu.yaml
pod/deb-ubuntu created
root@k8s-master:~/yaml# kubectl get pod -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP            NODE           NOMINATED NODE   READINESS GATES
deb-ubuntu   1/1     Running   0          67s   10.1.39.193   k8s-worker03   <none>           <none>
```

続いてnginxのDeamonSetとそれに関連したClusterIPをデプロイしていきます。
yamlファイルは以下となります。
```nginx-ds.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ds
spec:
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      tolerations:
      - key: "env"
        operator: "Equal"
        value: "master"
        effect: NoSchedule
      containers:
      - name: nginx-container
        image: nginx:latest
```
```clusterip-nginx-ds.yaml
apiVersion: v1
kind: Service
metadata:
  name: clusterip-nginx-ds
spec:
  selector:
    name: nginx
  type: ClusterIP
  ports:
  - name: nginx
    port: 80
    protocol: TCP
    targetPort: 80
```

デプロイはいつも通り、kubectl create -fです。
デプロイした結果が以下となります。
```
root@k8s-master:~/yaml# kubectl get pod,svc -o wide
NAME                 READY   STATUS    RESTARTS   AGE    IP             NODE           NOMINATED NODE   READINESS GATES
pod/deb-ubuntu       1/1     Running   0          4m6s   10.1.39.193    k8s-worker03   <none>           <none>
pod/nginx-ds-jfzxv   1/1     Running   0          40s    10.1.235.213   k8s-master     <none>           <none>
pod/nginx-ds-v2qf5   1/1     Running   0          40s    10.1.39.194    k8s-worker03   <none>           <none>
pod/nginx-ds-xmmfq   1/1     Running   0          40s    10.1.79.65     k8s-worker01   <none>           <none>
pod/nginx-ds-pdl4d   1/1     Running   0          40s    10.1.69.196    k8s-worker02   <none>           <none>
NAME                         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/kubernetes           ClusterIP   10.152.183.1     <none>        443/TCP   47h   <none>
service/clusterip-nginx-ds   ClusterIP   10.152.183.117   <none>        80/TCP    32s   name=nginx
```

上記出力結果をイメージに落とし込むと以下となります。
緑の枠線で囲われている部分はdefault namespaceになります。
![Image](https://user-images.githubusercontent.com/127835743/236715534-04c232d2-8739-4021-9037-d8eb4cae87cd.png)

名前解決を検証していきます。まずdebug用のubuntu-pod/containerに接続し、/etc/hosts及び/etc/resolv.confを確認してみます。
デフォルトで10.152.183.10というIPアドレスがDNSサーバとして設定されており、どこに紐づいているかが気になるところです。
```
root@k8s-master:~/yaml# kubectl exec -it deb-ubuntu -c deb-ubuntu-con -- /bin/bash
root@deb-ubuntu:/# 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
10.1.39.193     deb-ubuntu
root@deb-ubuntu:/# cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.152.183.10
options ndots:5
```

k8sのDNS周りの情報を調べてみます。
どうやら上記のIPアドレスはDNSのClusterIP serviceに関連づいている様ですね。
```
root@k8s-master:~# kubectl get all -o wide --namespace=kube-system | grep dns
pod/coredns-6f5f9b5d74-pf4ld                    1/1     Running   2 (38h ago)   47h   10.1.235.206   k8s-master     <none>           <none>
service/kube-dns                    ClusterIP   10.152.183.10    <none>        53/UDP,53/TCP,9153/TCP   47h   k8s-app=kube-dns
deployment.apps/coredns                     1/1     1            1           47h   coredns                     coredns/coredns:1.9.3                                  k8s-app=kube-dns
replicaset.apps/coredns-6f5f9b5d74                    1         1         1       47h   coredns                     coredns/coredns:1.9.3                                  k8s-app=kube-dns,pod-template-hash=6f5f9b5d74
```

イメージに落とし込みます。
赤枠内がkube-system namespaceになります。このnamespaceの中にDNSのpod(正確にはdeplolyment)とそれに関連したClusterIP serviceがデプロイされています。
![Image](https://user-images.githubusercontent.com/127835743/236716863-be6ffa08-b48d-49a7-9b92-d4db9577029f.png)

ubuntuのpodに戻ります。このpodから名前解決できるか確認していきます。
具体的にはubuntuのpodと同一ns内にあるnginxのClustrIP serviceのドメイン名を名前解決できるかをnslookup及びcurlで確認していきます。
まずはnslookip。Addressの欄にnginxのClusterIP serviceのIPが出力されていることから問題ないように見られます。
```
root@deb-ubuntu:/# nslookup clusterip-nginx-ds
Server:         10.152.183.10
Address:        10.152.183.10#53
Name:   clusterip-nginx-ds.default.svc.cluster.local
Address: 10.152.183.117
```

curlを実行してみます。nginxのhtmlファイルを取得できていることから、併せて問題なさそうです。
```
root@deb-ubuntu:/# curl clusterip-nginx-ds:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

ubuntu podの名前解決のフローをイメージに落とし込むと以下のようになります。
![Image](https://user-images.githubusercontent.com/127835743/236717732-28bfde89-b446-4f52-9ec2-ecf24037f0b1.png)

# default namespaceではないnsにpod,ds,svcをデプロイして名前解決の動作確認をする

nsが違う場合の名前解決を試してみます。
現状のnsは以下となります。
```
root@k8s-master:~/yaml# kubectl get ns
NAME              STATUS   AGE
kube-system       Active   2d
kube-public       Active   2d
kube-node-lease   Active   2d
default           Active   2d
metallb-system    Active   47h
```

以下のyamlを用意し、nsを新規作成します。
このnsは作成したばかりのため、当たり前ですがなにもデプロイされていませんね。
```ns-myns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: myns
```
```
root@k8s-master:~/yaml# kubectl create -f ns-myns.yaml
root@k8s-master:~/yaml# kubectl get ns
NAME              STATUS   AGE
kube-system       Active   2d
kube-public       Active   2d
kube-node-lease   Active   2d
default           Active   2d
metallb-system    Active   47h
myns              Active   4s

root@k8s-master:~/yaml# kubectl get all --namespace=myns
No resources found in myns namespace.
```

新規作成したmynsにapacheのDeamonSetとそれに関連したClusterIP serviceをデプロイしていきたいと思います。用意したyamlファイルは以下2点です。
注意点としては、今回はdefault namespaceではなく、作成したmyns namespaceにデプロイしたいのでそれぞれのyamlファイルのmetadata.namespaceでnsを指定している点位かと思います。
```apache2-ds-myns.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: apache-ds-myns
  namespace: myns
spec:
  selector:
    matchLabels:
      name: apache
  template:
    metadata:
      labels:
        name: apache
    spec:
      tolerations:
      - key: "env"
        operator: "Equal"
        value: "master"
        effect: NoSchedule
      containers:
      - name: apache-container
        image: shotaohtsuka/my-httpd-image
```
```clusterip-apache2-ds-myns.yaml
apiVersion: v1
kind: Service
metadata:
  name: clusterip-apache2-ds-myns
  namespace: myns
spec:
  selector:
    name: apache
  type: ClusterIP
  ports:
  - name: apache
    port: 90
    protocol: TCP
    targetPort: 90
```

これらのyamlを使いデプロイします。
```
root@k8s-master:~/yaml# kubectl create -f apache2-ds-myns.yaml
daemonset.apps/apache-ds-myns created
root@k8s-master:~/yaml# kubectl create -f clusterip-apache2-ds-myns.yaml
service/clusterip-apache2-ds-myns created
```

デプロイ結果を確認します。pod,svc共に正常にデプロイできていそうです。
```
root@k8s-master:~/yaml# kubectl get pod --namespace=myns -o wide
NAME                   READY   STATUS    RESTARTS   AGE   IP             NODE           NOMINATED NODE   READINESS GATES
apache-ds-myns-9785z   1/1     Running   0          81m   10.1.235.214   k8s-master     <none>           <none>
apache-ds-myns-5tjrr   1/1     Running   0          81m   10.1.79.66     k8s-worker01   <none>           <none>
apache-ds-myns-d4f2h   1/1     Running   0          81m   10.1.39.195    k8s-worker03   <none>           <none>
apache-ds-myns-k8qfv   1/1     Running   0          81m   10.1.69.197    k8s-worker02   <none>           <none>

root@k8s-master:~/yaml# kubectl get svc --namespace=myns
NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
clusterip-apache2-ds-myns   ClusterIP   10.152.183.115   <none>        90/TCP    54s
```

この時のイメージは以下となります。
ピンクの枠線内がここで新規に作成したnsであるmynsになります。このns内にapache関連のdsとClusterIP serviceをデプロイしています。
![Image](https://user-images.githubusercontent.com/127835743/236732779-a4f7f9de-683f-47c3-9e7d-acdb54a782a4.png)

ubuntuのpodにおいて同一ns内と同じ様に、ドメイン名だけで名前解決できないことを確認します。
具体的にはdefault namespace内のubuntu podからmyns namespaceのapacheのClusterIP serviceのドメイン名で名前解決できないことを確認します。
catn't findの出力より、名前解決に失敗していることがわかりますね。
```
root@k8s-master:~/yaml# kubectl exec -it deb-ubuntu -c deb-ubuntu-con -- /bin/bash
root@deb-ubuntu:/# nslookup clusterip-apache2-ds-myns
Server:         10.152.183.10
Address:        10.152.183.10#53
** server can't find clusterip-apache2-ds-myns: NXDOMAIN
```

一方でドメインに対して".myns"のようにnsを指定してあげると名前解決ができることを確認します。
nslookupもcurlも成功していることからうまくいっていることがわかるかと思います。
```
root@deb-ubuntu:/# nslookup clusterip-apache2-ds-myns.myns
Server:         10.152.183.10
Address:        10.152.183.10#53
Name:   clusterip-apache2-ds-myns.myns.svc.cluster.local
Address: 10.152.183.115

root@deb-ubuntu:/# curl clusterip-apache2-ds-myns.myns:90
<html><body><h1>It works!</h1></body></html>
```


# ちなみにnsが違うpodの疎通って出来るんだろうか？

過去、同一のns内であればpod同士の疎通が取れる。xvlan上にpodがデプロイされるからnodeが違っても関係なく疎通が取れることは検証できた。

https://qiita.com/ohtsuka-shota/items/2e20a375e5e53c5df777

だが、今回はnsが異なる。この時どうなるのだろうか？ネットワーク的には同じ様に見えるため通信はできそうであるが確認してみることにする。ubuntuのpodからmynsのk8s-master上のpodとk8s-worker01上のpodにpingを試みる。
結果として疎通が取れた。名前解決が絡んでくるときだけ、nsが影響してくるのかな。。。？

```
root@k8s-master:~# kubectl exec -it deb-ubuntu -c deb-ubuntu-con -- /bin/bash
root@deb-ubuntu:/# ping 10.152.183.115
PING 10.152.183.115 (10.152.183.115) 56(84) bytes of data.
^C
--- 10.152.183.115 ping statistics ---
8 packets transmitted, 0 received, 100% packet loss, time 7150ms

root@deb-ubuntu:/# ping 10.1.235.214
PING 10.1.235.214 (10.1.235.214) 56(84) bytes of data.
64 bytes from 10.1.235.214: icmp_seq=1 ttl=62 time=1.39 ms
64 bytes from 10.1.235.214: icmp_seq=2 ttl=62 time=0.643 ms
64 bytes from 10.1.235.214: icmp_seq=3 ttl=62 time=0.522 ms
64 bytes from 10.1.235.214: icmp_seq=4 ttl=62 time=0.579 ms
64 bytes from 10.1.235.214: icmp_seq=5 ttl=62 time=0.547 ms
^C
--- 10.1.235.214 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4060ms
rtt min/avg/max/mdev = 0.522/0.735/1.385/0.327 ms

root@deb-ubuntu:/# ping 10.1.79.66
PING 10.1.79.66 (10.1.79.66) 56(84) bytes of data.
64 bytes from 10.1.79.66: icmp_seq=1 ttl=62 time=1.07 ms
64 bytes from 10.1.79.66: icmp_seq=2 ttl=62 time=0.636 ms
64 bytes from 10.1.79.66: icmp_seq=3 ttl=62 time=0.550 ms
64 bytes from 10.1.79.66: icmp_seq=4 ttl=62 time=0.498 ms
64 bytes from 10.1.79.66: icmp_seq=5 ttl=62 time=0.639 ms
^C
--- 10.1.79.66 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4078ms
rtt min/avg/max/mdev = 0.498/0.679/1.072/0.203 ms
```