コンテンツにスキップ

CoreDNS@DNS系ミドルウェア

はじめに

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


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 アドレスとポート番号の動的な検出を組み合わせることにより、サービス検出を実装できる。