コンテンツにスキップ

CoreDNS@ネットワーク系

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. CoreDNS (新kube-dns)

CoreDNSとは

Node内の権威DNSサーバーとして、Kubernetesリソースの名前解決を実行する。

kubernetes_coredns


01-02. マニフェスト

マニフェストの種類

CoreDNSは、Deployment (CoreDNS) 、Service (kube-dns) 、ConfigMap (coredns-configmap) などのマニフェストから構成される。

$ kubectl get pod -n kube-system

NAME                        READY   STATUS    RESTARTS   AGE
coredns-558bd4d5db-hg75t    1/1     Running   0          1m0s
coredns-558bd4d5db-ltbxt    1/1     Running   0          1m0s

...


$ kubectl get service -n kube-system

NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   1m0s

...


Deployment

▼ CoreDNS

Podからの問い合わせに対して、名前解決を実行する。


Service

▼ kube-dns

CoreDNSに対する問い合わせを受信し、CoreDNSへルーティングする。


ConfigMap

▼ coredns-configmap

ConfigMapの.Corefileキーに、Corefileファイルの設定値を定義する。

*実装例*

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns-configmap
  namespace: kube-system
data:
  Corefile: |
    .:53 {
      ...
    }


02. Serviceの名前解決

Serviceの名前解決の仕組み

▼ アーキテクチャ

coredns_service-discovery

CoreDNSは、kube-apiserverから必要なKubernetesリソース (Service、Endpoints) を取得する。

Podのスケジューリング時に、kubeletはPod内のコンテナの/etc/resolv.confファイルに 権威DNSサーバー (CoreDNSのService) のIPアドレスを設定する。

Pod内のコンテナは、自身の/etc/resolv.confファイルを使用して、CoreDNSのServiceにリクエストを送信する。

また、CoreDNSから、宛先のPodに紐づくServiceのIPアドレスを正引きする。

このServiceのIPアドレスを指定し、Podにリクエストを送信する。

▼ 確認方法

# Pod内のコンテナに接続する。
$ kubectl exec -it <Pod名> -c <コンテナ名> -- bash

# コンテナのresolv.confファイルの中身を確認する
[root@<Pod名>] $ cat /etc/resolv.conf

# 権威DNSサーバーのIPアドレス (ここではCoreDNSのServiceのIPアドレス)
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
# 名前解決時のローカルドメインの優先度
options ndots:5
# CoreDNSを権威DNSサーバーとして使用している場合
$ kubectl get service -n kube-system

NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   1m0s


DNSレコードタイプ別の完全修飾ドメイン名

▼ DNSレコードタイプ別の完全修飾ドメイン名とは

Clusterネットワーク内の全てのServiceに完全修飾ドメイン名が割り当てられている。

DNSレコードタイプごとに、完全修飾ドメイン名が異なる。

A/AAAAレコードの場合

対応する完全修飾ドメイン名は、『<Service名>.<Namespace名>.svc.cluster.local』である。

通常のServiceの名前解決ではCluster-IPが返却される。

一方でHeadless Serviceの名前解決では、PodのIPアドレスが返却される。

svc.cluster.local』は省略でき、『<Service名>.<Namespace名>』のみを指定しても名前解決できる。

また、同じNamespace内でパケットを送受信する場合は、さらに『<Namespace名>』も省略でき、『<Service名>』のみで名前解決できる。

SRVレコードの場合

対応する完全修飾ドメイン名は、『_<ポート名>._<プロトコル>.<Service名>.<Namespace名>.svc.cluster.local』である。

Serviceの.spec.ports.nameキー数だけ、完全修飾ドメイン名が作成される。


名前解決の仕組み

▼ Pod内からServiceに対する正引き名前解決

Pod内のコンテナから宛先のServiceに対して、nslookupコマンドの正引きする。

Serviceに.metadata.nameキーが設定されている場合、Serviceの完全修飾ドメイン名は、.metadata.nameキーの値になる。

完全修飾ドメイン名の設定を要求された時は、設定ミスを防げるため、.metadata.nameキーの値よりも完全修飾ドメイン名の方が推奨である。

# Pod内のコンテナに接続する。
$ kubectl exec -it <Pod名> -c <コンテナ名> -- bash

# Pod内のコンテナから宛先のServiceに対して、正引きの名前解決を実行する
[root@<Pod名>:~] $ nslookup <Service名>

Server:         10.96.0.10
Address:        10.96.0.10#53

Name:  <Serviceの完全修飾ドメイン名>
Address:  10.105.157.184

補足として、異なるNamespaceに所属するServiceの名前解決を実行する場合は、Serviceの完全修飾ドメイン名の後にNamespaceを指定する必要がある。

# Pod内のコンテナから正引きの名前解決を実行する。
[root@<Pod名>:~] $ nslookup <Serviceの完全修飾ドメイン名>

▼ Pod外からServiceに対する正引き名前解決

(1)

NginxのPodにルーティングするServiceが稼働しているとする。

$ kubectl get service

NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
nginx-service   ClusterIP   10.101.67.107   <none>        8080/TCP   3h34m
(2)

CoreDNSのPodが稼働しているとする。

ここで、CoreDNSのPodのIPアドレス (ここでは10.244.0.2) を確認しておく。

$ kubectl get pod -o wide -l k8s-app=kube-dns -n kube-system

NAME            READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
coredns-*****   1/1     Running   0          3h53m   10.244.0.2   minikube   <none>           <none>
(3)

ここで、Node内に接続する。Serviceの完全修飾ドメイン名 (ここではnginx-service.default.svc.cluster.local) をCoreDNSに正引きする。すると、ServiceのIPアドレスを取得できる。

# Node内に接続する。
$ dig nginx-service.default.svc.cluster.local +short @10.244.0.2

10.101.67.107


疎通確認

▼ 事前確認

(1)

Serviceがルーティング先のポート番号を確認する。

$ kubectl get service <Service名> -o yaml | grep targetPort:
(2)

Serviceがルーティング先のPodにて、コンテナが待ち受けるポート番号を確認する。注意点として、.spec.containers[*].portsキーは単なる仕様であり、記載されていなくとも、コンテナのポートが公開されている可能性がある。

# 先にmetadata.labelキーから、Serviceのルーティング先のPodを確認する
$ kubectl get pod -l <名前>=<値> -o wide

$ kubectl get pod <Pod名> -o yaml | grep containerPort:
(3)

両方のポート番号が一致しているかを確認する。

▼ Serviceを経由したアウトバウンド通信の送信

Serviceを介して、宛先のPodにHTTPSリクエストを送信する。

完全修飾ドメイン名またはIPアドレスを指定できる。

# Pod内のコンテナに接続する。
$ kubectl exec -it <Pod名> -c <コンテナ名> -- bash

[root@<Pod名>:~] $ curl -X GET https://<Serviceの完全修飾ドメイン名/IPアドレス>:<ポート番号>


03. Podの直接的な名前解決

Podの直接的な名前解決の仕組み

Serviceの名前解決を介さずに、特定のPodのインスタンスに対して直接的に名前解決することもできる。


DNSレコードタイプ別の完全修飾ドメイン名

A/AAAAレコードの場合

対応する完全修飾ドメイン名は、『<PodのIPアドレス>.<Namespace名>.pod.cluster.local』である。


PodのIPアドレスを固定する

Kubernetesでは、PodのIPアドレスを固定できない。

そのため、IPアドレスを固定したアプリは、Kubernetes Cluster上で稼働させるのではなく、サーバー上でコンテナあるいはプロセスとして稼働させた方が良い。

ただし、一部のCNIを使用すれば、IPアドレスを固定することはできる。


04. サービスディスカバリー

CoreDNSの名前解決と、Serviceとkube-proxyによるIPアドレスとポート番号の動的な検出を組み合わせることにより、サービスディスカバリーを実装できる。