追蹤 Kubernetes 中的 DNS 查詢
在過去的文章中,我們?cè)?nbsp;追蹤過 Kubernetes 中的網(wǎng)絡(luò)數(shù)據(jù)包[1],這篇文章將追蹤 Kubernetes 中的 DNS 查詢。
讓我們以在 Pod 中解析 Service 完全限定域名(FQDN) foo.bar.svc.cluster.local 為例。
在開始之前,先回顧下 DNS 的解析流程。
DNS 的解析流程
圖片
簡(jiǎn)化版的 DNS 處理流程:
- DNS 客戶端(如瀏覽器、應(yīng)用程序或者設(shè)備)發(fā)送域名 example.com 的查詢請(qǐng)求。
- DNS 解析器收到請(qǐng)求,查詢本地緩存,如果本地有記錄且未過期會(huì)返回本地的記錄。
- 如果本地緩存未命中,DNS 解析器將從 DNS 根服務(wù)器開始向下查詢,首先是頂級(jí)域名(Top Level Domain, TLD) DNS 服務(wù)器(這里是 .com),一直向下直到可以解析 example.com 的服務(wù)器。
- 能夠解析 example.com 的服務(wù)器成為權(quán)威 DNS 名稱服務(wù)器(Authoritative DNS name server),解析器訪問該服務(wù)器并收到 IP 地址等相關(guān)信息,然后返回給給客戶端。解析完成。
從流程來看非常重要的一項(xiàng)配置就是上游 DNS 服務(wù)器,該配置位于 Pod 中。這里 Kubernetes 的集群 DNS 服務(wù)器正是扮演上游 DNS 服務(wù)器的角色,比如 kube-dns[2]、CoreDNS[3],二者均實(shí)現(xiàn)了 Kubernetes 的基于 DNS 的服務(wù)發(fā)現(xiàn)規(guī)范[4]。對(duì) CoreDNS 感興趣的,可以參考上一篇文章 淺析 CoreDNS 的工作機(jī)制[5]。
圖片
Pod DNS 配置
在 解析 kubelet 源碼[6] 一文中,我們?cè)治隽?kubelet 創(chuàng)建 pod 的流程。kubelet 創(chuàng)建 pod sandbox 配置時(shí)[7] ,其中重要的一項(xiàng)配置就是準(zhǔn)備 pod 的 DNS 配置[8](Pod 的 DNS 配置由 pod 的 dnsPolicy 和 dnsConfig 字段進(jìn)行操作,這里不展開,下面的部分按照 dnsPolicy=ClusterFirst 情況進(jìn)行說明)。
配置的內(nèi)容包括如下三個(gè)部分:
- DNS 服務(wù)器 nameserver:來自 kubelet 配置(通常位于 /var/lib/kubelet/config.yaml)的 clusterDNS 字段
- 搜索域 search:包含四種域:命名空間域、服務(wù)域、集群域,以及節(jié)點(diǎn) /etc/resolv.conf 中定義的搜索域。集群域來自 kubelet 配置的 clusterDomain 字段,默認(rèn)為 cluster.local;命名空間域 NS.svc.cluster.local;服務(wù)域 svc.cluster.local
- 選項(xiàng) options:默認(rèn)為 ndots:5
然后 kubelet 調(diào)用 CRI 接口創(chuàng)建容器,由 CRI 的實(shí)現(xiàn)將 DNS 配置寫入到容器文件(默認(rèn)地址 /etc/resolv.conf)中,如 Containerd[9] 的 pkg/cri/server/sandbox_run_linux.go#L272[10]。
我們查看命名空間 default 下某個(gè) pod 的 DNS 配置:
cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
這里的 nameserver 正是 Service kube-dns 的 cluster IP 地址,也就是集群的 DNS 服務(wù)器,即 dnsPolicy=ClusterFirst 的結(jié)果。
search 與 ndots
search 用于指定默認(rèn)的搜索域。當(dāng)你在使用不完全限定域名(例如,只提供主機(jī)名而沒有域名)進(jìn)行域名解析時(shí),系統(tǒng)會(huì)嘗試在搜索域中找到匹配的完全限定域名。搜索域按照出現(xiàn)的順序進(jìn)行搜索,直到找到匹配的域名或搜索完所有的域名。
ndots 用于指定在進(jìn)行域名解析時(shí),系統(tǒng)自動(dòng)添加域名的點(diǎn)號(hào)個(gè)數(shù)閾值。當(dāng)提供的域名中點(diǎn)號(hào)的個(gè)數(shù)達(dá)到或超過這個(gè)閾值時(shí),系統(tǒng)會(huì)將其視為完全限定域名,而不再使用搜索域進(jìn)行搜索。默認(rèn)為 1,這里將其設(shè)置為 5。
注:ndots 的值大小會(huì)影響 DNS 解析的性能,為了獲得較好的性能,建議使用 FQDN 進(jìn)行服務(wù)訪問,以及將 ndots 改為更小的值。
Pod DNS 解析
當(dāng)在 Pod 中執(zhí)行 DNS 解析時(shí),查詢請(qǐng)求被發(fā)到本地(pod 中)的 DNS 解析器。這個(gè)解析器先在緩存中查詢,如果未命中,則會(huì)根據(jù) /etc/resolv.conf 中的配置,將請(qǐng)求發(fā)到上游的 DNS 服務(wù)器,即集群 DNS 服務(wù)器 10.96.0.10 完成域名解析。
根據(jù)前面的介紹,假如我們要解析的域名是 foo.bar,會(huì)依次進(jìn)行如下的查詢:
- foo.bar.default.svc.cluster.local
- foo.bar.svc.cluster.local(匹配到結(jié)果)
當(dāng)我們使用 foo.bar、foo.bar.svc 都可以完成解析,但 foo.bar.svc.cluster 不行,因?yàn)樽芳恿怂阉饔蚝鬅o(wú)法匹配到結(jié)果。假如請(qǐng)求方與目標(biāo)服務(wù)在同一個(gè)命名空間下,只用 foo 也是可以的。
參考資料
[1] 追蹤過 Kubernetes 中的網(wǎng)絡(luò)數(shù)據(jù)包: https://atbug.com/tracing-network-packets-in-kubernetes/
[2] kube-dns: https://github.com/kubernetes/dns
[3] CoreDNS: https://coredns.io
[4] Kubernetes 的基于 DNS 的服務(wù)發(fā)現(xiàn)規(guī)范: https://github.com/kubernetes/dns/blob/master/docs/specification.md
[5] 淺析 CoreDNS 的工作機(jī)制: https://atbug.com/analysis-of-the-working-mechanism-of-coredns/
[6] 解析 kubelet 源碼: https://atbug.com/how-kubelete-container-runtime-work-with-cni/#創(chuàng)建-pod
[7] 創(chuàng)建 pod sandbox 配置時(shí): https://github.com/kubernetes/kubernetes/blob/release-1.24/pkg/kubelet/kuberuntime/kuberuntime_manager.go#L861
[8] pod 的 DNS 配置: https://github.com/kubernetes/kubernetes/blob/release-1.24/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go#L93
[9] Containerd: https://github.com/containerd/containerd
[10] pkg/cri/server/sandbox_run_linux.go#L272: https://github.com/containerd/containerd/blob/a05d175400b1145e5e6a735a6710579d181e7fb0/pkg/cri/server/sandbox_run_linux.go#L272