kubernetes中的DNS

kubernetes中dns系統是怎樣運行的?

dns爲什麼要以插件的形式部署在k8s中,這意味着dns插件不是必須的?可以不部署嗎?

kube-dns 介紹

Kubernetes DNS在集羣中運行着一個pod和一個service。在啓用kubernetes dns之後,必須重新配置kubelet,設置cluster-dns及cluster-domain兩個參數。

集羣中定義的所有服務(包括DNS服務本身)都被賦予了一個DNS名。默認情況下,一個Pod的DNS搜索域包括Pod自身命名空間和集羣的默認域。

從 Kubernetes v1.3 版本開始,使用 [cluster add-on 插件管理器回自動啓動內置的 DNS。

Kubernetes DNS pod 中包括 3 個容器:

  • kubedns:kubedns 進程監視 Kubernetes master 中的 Service 和 Endpoint 的變化,並維護內存查找結構來服務DNS請求。
  • dnsmasq:dnsmasq 容器添加 DNS 緩存以提高性能。
  • sidecar:sidecar 容器在執行雙重健康檢查(針對 dnsmasq 和 kubedns)時提供單個健康檢查端點(監聽在10054端口)。

DNS pod 具有靜態 IP 並作爲 Kubernetes 服務暴露出來。該靜態 IP 分配後,kubelet 會將使用 --cluster-dns = 標誌配置的 DNS 傳遞給每個容器。

DNS 名稱也需要域名。本地域可以使用標誌 --cluster-domain = 在 kubelet 中配置。

Kubernetes集羣DNS服務器基於 SkyDNS 庫。它支持正向查找(A 記錄),服務查找(SRV 記錄)和反向 IP 地址查找(PTR 記錄)

kube-dns 支持的 DNS 格式

kube-dns 將分別爲 service 和 pod 生成不同格式的 DNS 記錄。

Service

A記錄:生成my-svc.my-namespace.svc.cluster.local域名,解析成 IP 地址,分爲兩種情況:

  • 普通 Service:解析成 ClusterIP
  • Headless Service:解析爲指定 Pod 的 IP 列表

SRV記錄:爲命名的端口(普通 Service 或 Headless Service)生成 _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local 的域名

Pod

A記錄:生成域名 pod-ip.my-namespace.pod.cluster.local

在 pod 中可以通過 service_name.namespace.svc.domain 來訪問任何的服務,也可以使用縮寫 service_name.namespace,如果 pod 和 service 在同一個 namespace,甚至可以直接使用 service_name。

測試kube-dns是否工作

可以創建一個簡單的pod,在該pod內執行dns查找,如果能夠查到,則kube-dns工作正常。

kubectl exec -ti busybox -- nslookup kubernetes.default
Server:    192.168.3.10
Address 1: 192.168.3.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 192.168.3.1 kubernetes.default.svc.cluster.local

如果能在pod內能找到kubernetes.default這個服務,則kube-dns工作正常。

爲什麼需要自定義 DNS

DNS 是 Kubernetes 的核心功能之一,Kubernetes 通過 kube-dns 或 CoreDNS 作爲集羣的必備擴展來提供命名服務,通過 DNS 擴展,每一個 Service 都會產生一個獨一無二的 FQDN(Fully Qualified Domain Name)名稱。

在大多數使用場景下,我們並不會太關心 DNS 插件的內部運作細節,直接使用 Kubernetes 預設的 DNS 配置和策略就可以滿足需求。然而隨着使用場景越來越複雜,譬如跟 NFV(Network Function Virtualization)相關的場景,我們的應用(Pod)可能就會需要更加個性化的 DNS 配置。

接下來使用下面這張架構圖來說明可能的使用場景:

在這裏插入圖片描述

一般的使用場景下,我們的 Kubernetes 集羣的使用方式就像圖中紫色/粉紅色(Pod3)區域一樣,所有的 Pod 如果有任何要存取 DNS 的需求,都會透過集羣內的的 k8s DNS 來處理對應的請求與回覆。

然而在 NFV 的使用場景下,網絡變成一個很重要的區域,整體的性能都取決於該應用的設計與集羣的網絡架構設計。這部分應用通常都會追求高輸出或是低延遲,爲了得到更好的性能,需要避免這些流量跟其他無關的流量使用相同的網絡線路進行傳輸。

在這種情況下,通常就會把整個集羣的網絡設計成兩種架構,分別是 Control Network 和 Data Network 這兩個不同用途的網絡架構。在 Kubernetes 中,Control Network 就類似於圖中的 Cluster Network,負責整個集羣之間的溝通。圖中綠色/橘色(Pod1,Pod2)這兩個區域就是所謂的 Data Network,其網卡本身也被獨立出來,不會與本來的 Kubernetes 集羣發生衝突,它們之間的流量通過獨立的網絡進行傳輸。

存在於獨立出來的網絡架構中的這些特殊的 Pod 基本上沒法跟 Kubernetes 集羣內的 DNS 互連,而且這些應用還有可能在外部有自己的 DNS Server,所以在這種場景下,我們希望這些應用(Pod1/Pod2)能夠使用自定義的 DNS Server。

如何自定義 DNS

爲了讓用戶更容易控制 Pod 中的 DNS 設置,Kubernetes v1.9 引入了一項新的 Alpha 特性(在 v1.10 中處於 Beta 階段)。該特性在 v1.10 中被默認啓用,在 v1.9 中如果想要啓用此功能,集羣管理員需要在 apiserver 和 kubelet 上啓用 CustomPodDNS 特性,例如:“–feature-gates=CustomPodDNS=true,…”。啓用了該特性之後,用戶可以將 Pod 的 dnsPolicy 字段設置爲 “None”,並且可以在 Pod.Spec 中添加新的字段 dnsConfig。

其中 dnsConfig 用來自定義 DNS 參數,而 dnsPolicy 用來給 Pod 選取預設的 DNS。接下來就看看可以通過哪些手段自定義 DNS。

dnsConfig

dnsConfig 可以讓操作者延伸到 Pod 內部關於 DNS 的配置,這邊需要特別注意的是,我使用的字眼是 延伸 而不是 配置,這是因爲通過下一節的 dnsPolicy,每個 Pod 都會有一組預設的 DNS 配置。通過 dnsConfig 我們可以繼續往上疊加相關的 DNS 參數到 Pod 之中。

目前總共支持三個參數,分別是:

nameservers
searches
options

這三個參數對應的就是大家熟悉的 /etc/resolv.conf 裏面的三個參數,這裏就不針對 DNS 進行詳細解釋了,不熟悉的朋友可以自行去 Google 學一下這些參數的意思。

在 Kubernetes 裏面,這三個參數都包含在 dnsConfig 配置項中,而 dnsConfig 包含在 PodSpec 配置項中,因爲 Pod 內所有的容器都共享相同的 Network Namespace,所以網絡方面的配置都會共享。

這邊提供一個簡單的 yaml 示例:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-setting
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

通過上述 yaml 創建 Pod 之後,通過下面的命令可以觀察到容器中 DNS 配置文件中會出現額外的配置。

$ kubectl exec ubuntu-setting cat /etc/resolv.conf

nameserver 10.254.0.2
nameserver 1.2.3.4
search default.svc.cluster.local svc.cluster.local cluster.local ns1.svc.cluster.local my.dns.search.suffix
options ndots:2 edns0

可以看到 nameserver 多了一個 1.2.3.4,而 search 則多了 ns1.svc.cluster.local my.dns.search.suffix 這兩個自定義的值,最後 options 則增加了我們示例中指定的 ndots:2 edns0。

dnsConfig 非常簡單直觀,如果你需要自定義 DNS 參數,就可以通過這個字段來指定。

dnsPolicy

前面提過,dnsConfig 提供的是延伸 Pod 內預設的 DNS 配置,而 dnsPolicy 就是決定 Pod 內預設的 DNS 配置有哪些。

目前總共有四個類型可以選擇:

None
Default
ClusterFirst
ClusterFirstHostNet
接下來針對這四個類型分別介紹。

None

None 表示會清除 Pod 預設的 DNS 配置,當 dnsPolicy 設置成這個值之後,Kubernetes 不會爲 Pod 預先載入任何自身邏輯判斷得到的 DNS 配置。因此若要將 dnsPolicy 的值設爲 None,爲了避免 Pod 裏面沒有配置任何 DNS,最好再添加 dnsConfig 來描述自定義的 DNS 參數。

使用下面的示例來進行測試:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-none
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always
  dnsPolicy: None
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

通過上述 yaml 創建 Pod 之後,通過下面的命令可以觀察容器中的 DNS 配置文件,可以觀察到跟之前的 dnsConfig 的結果有一點差異,這裏只有我們在 yaml 裏配置的那些參數,而沒有加入集羣預設的 DNS 配置。

$ kubectl exec ubuntu-none cat /etc/resolv.conf

nameserver 1.2.3.4
search ns1.svc.cluster.local my.dns.search.suffix
options ndots:2 edns0

Default

Default 表示 Pod 裏面的 DNS 配置繼承了宿主機上的 DNS 配置。簡單來說,就是該 Pod 的 DNS 配置會跟宿主機完全一致。

使用下面的示例來進行測試:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-default
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always
  dnsPolicy: Default

首先,我們先觀察 Node 上面的 DNS 配置:

$ cat /etc/resolv.conf

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.0.2.3

可以觀察到,Node 上面的 DNS 配置得很簡單,只有單純的 10.0.2.3。 接下來我們觀察該 Pod 內的 DNS 配置:

$ kubectl exec ubuntu-default cat /etc/resolv.conf

nameserver 10.0.2.3

可以看到這兩個的 DNS 配置完全一致,該 Pod 內的 DNS 配置已經直接繼承 Node 上面的配置了。

ClusterFirst

相對於上述的 Default,ClusterFirst 是完全相反的操作,它會預先把 kube-dns(或 CoreDNS)的信息當作預設參數寫入到該 Pod 內的 DNS 配置。

Note
ClusterFirst 是預設的行爲,若沒有在 Pod 內特別描述 PodPolicy, 則會將 dnsPolicy 預設爲 ClusterFirst。

使用下面的示例來進行測試:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-clusterfirst
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always
  dnsPolicy: ClusterFirst

通過上述 yaml 創建 Pod 之後,通過下面的命令觀察容器中的 DNS 配置文件:

$ kubectl exec ubuntu-clusterfirst cat /etc/resolv.conf

nameserver 10.254.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

可以看到這裏使用的是 k8s DNS 的設置。

此外,ClusterFirst 還有一個衝突,如果你的 Pod 設置了 HostNetwork=true,則 ClusterFirst 就會被強制轉換成 Default。

HostNetwork

使用下面的示例來進行測試:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-hostnetwork-policy-default
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  hostNetwork: true
  restartPolicy: Always
  dnsPolicy: ClusterFirst

通過上述 yaml 創建 Pod 之後,通過下面的命令觀察容器中的 DNS 配置文件:

$ kubectl exec ubuntu-hostnetwork-policy-default cat /etc/resolv.conf

nameserver 10.0.2.3

可以觀察到,Pod 裏面的 DNS 配置直接繼承了宿主機上的 DNS 配置。

這邊稍微來解釋一下這個設計上的原理以及流程:

因爲設置了 HostNetwork=true, 會讓該 Pod 與該節點共用相同的網路空間(網卡/路由等功能)。
預設的 k8s DNS 是使用 ClusterIP 的 kubernetes serivce. 這種情況下,只有屬於 Cluster 內的 Pod 可以獲取該 ClusterIP。
所以設置了 HostNetwork=true 的 Pod 就沒有辦法獲取該 ClusterIP。
於是預設就會將對應的 DNS 配置改回 Default 的形式,從節點繼承其 DNS 配置信息。
這種情況下,就會有人想要問,如果我刻意想要這樣設置不行嗎?

原先的設計中,是沒有辦法刻意處理的,原因是當 Pod yaml 配置文件被髮送出去後,在發現沒有設定 dnsPolicy 的情況下,會自動幫你把該 dnsPolicy 補上 ClusterFirst 的數值。

然後最後面的程序處理邏輯中,其實並沒有辦法分別下列兩種情況:

HostNetwork:我希望走 Host DNS
HostNetwork & dnsPolicy=ClusterFirst:我希望走 ClusterIP DNS
上述兩種情況對於後端的程序來看都長得一樣,完全沒有辦法分辨,我們可以直接從 Kubernetes 源碼 來閱讀一下其運作流程:

func getPodDNSType(pod *v1.Pod) (podDNSType, error) {
	dnsPolicy := pod.Spec.DNSPolicy
	switch dnsPolicy {
	case v1.DNSNone:
		if utilfeature.DefaultFeatureGate.Enabled(features.CustomPodDNS) {
			return podDNSNone, nil
		}
		// This should not happen as kube-apiserver should have rejected
		// setting dnsPolicy to DNSNone when feature gate is disabled.
		return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v: custom pod DNS is disabled", dnsPolicy))
	case v1.DNSClusterFirstWithHostNet:
		return podDNSCluster, nil
	case v1.DNSClusterFirst:
		if !kubecontainer.IsHostNetworkPod(pod) {
			return podDNSCluster, nil
		}
		// Fallback to DNSDefault for pod on hostnetowrk.
		fallthrough
	case v1.DNSDefault:
		return podDNSHost, nil
	}
	// This should not happen as kube-apiserver should have rejected
	// invalid dnsPolicy.
	return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v", dnsPolicy))
}

這邊可以看到一旦是 DNSClusterFirst 的情況下,若設置了 HostNetwork, 最後就會直節回傳 podDNSHost 節點的 DNS 設定回去。

爲了解決上述的問題,所以引進了一個新的類型 ClusterFirstHostNet。

ClusterFirstWithHostNet

ClusterFirstWithHostNet 用途非常簡單,我希望滿足使用 HostNetwork 同時使用 k8s DNS 作爲我 Pod 預設 DNS 的配置。

根據上面的源碼也可以觀察到:

case v1.DNSClusterFirstWithHostNet:
	return podDNSCluster, nil

只要將 dnsPolicy 設置爲 ClusterFirstWithHostNet, 就會一律返回 k8s DNS 的 clusterIP 這種形式。

使用下面的示例來進行測試:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-hostnetwork-policy
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  hostNetwork: true
  restartPolicy: Always
  dnsPolicy: ClusterFirstWithHostNet

通過上述 yaml 創建 Pod 之後,通過下面的命令觀察該 Pod 的狀態:

$ kubectl exec ubuntu-hostnetwork-policy cat /etc/resolv.conf

nameserver 10.254.0.2
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

可以發現這時候的 DNS 就會配置成 k8s DNS 的 ClusterIP 了。

kube-dns和coredns有什麼區別?各有什麼優勢,分別在什麼場景下使用?

  • kube-dns是老版DNS插件,CoreDNS是用來取代kube-dns的新版DNS插件。

  • CoreDNS在Kubernetes 1.11版本中,已實現GA,是kubeadm1.11的默認選項。

  • kube-dns裏面有3個容器,分別爲 kubedns查找DNS,dnsmasq緩存,sidecar 健康檢查。coredns裏面只有一個容器coredns。

  • kube-dns在可靠性、靈活性和安全性上有一些缺點

  • CoreDNS解決了kube-dns所遇到的問題,並提供了許多獨特的功能,可以解決各種各樣的用例。

  • 儘可能使用CoreDNS。

參考

https://www.yangcs.net/posts/kubernetes-dns/
https://cizixs.com/2017/04/11/kubernetes-intro-kube-dns/
https://blog.csdn.net/k8scaptain/article/details/81033095
https://jimmysong.io/posts/configuring-kubernetes-kube-dns/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章