在过去的文章中,我们曾 追踪过 Kubernetes 中的网络数据包[1],这篇文章将追踪 Kubernetes 中的 DNS 查询。
让我们以在 Pod 中解析 Service 完全限定域名(FQDN) foo.bar.svc.cluster.local 为例。
在开始之前,先回顾下 DNS 的解析流程。
DNS 的解析流程
图片
简化版的 DNS 处理流程:
从流程来看非常重要的一项配置就是上游 DNS 服务器,该配置位于 Pod 中。这里 Kubernetes 的集群 DNS 服务器正是扮演上游 DNS 服务器的角色,比如 kube-dns[2]、CoreDNS[3],二者均实现了 Kubernetes 的基于 DNS 的服务发现规范[4]。对 CoreDNS 感兴趣的,可以参考上一篇文章 浅析 CoreDNS 的工作机制[5]。
图片
Pod DNS 配置
在 解析 kubelet 源码[6] 一文中,我们曾分析了 kubelet 创建 pod 的流程。kubelet 创建 pod sandbox 配置时[7] ,其中重要的一项配置就是准备 pod 的 DNS 配置[8](Pod 的 DNS 配置由 pod 的 dnsPolicy 和 dnsConfig 字段进行操作,这里不展开,下面的部分按照 dnsPolicy=ClusterFirst 情况进行说明)。
配置的内容包括如下三个部分:
- DNS 服务器 nameserver:来自 kubelet 配置(通常位于 /var/lib/kubelet/config.yaml)的 clusterDNS 字段
- 搜索域 search:包含四种域:命名空间域、服务域、集群域,以及节点 /etc/resolv.conf 中定义的搜索域。集群域来自 kubelet 配置的 clusterDomain 字段,默认为 cluster.local;命名空间域 NS.svc.cluster.local;服务域 svc.cluster.local
- 选项 options:默认为 ndots:5
然后 kubelet 调用 CRI 接口创建容器,由 CRI 的实现将 DNS 配置写入到容器文件(默认地址 /etc/resolv.conf)中,如 Containerd[9] 的 pkg/cri/server/sandbox_run_linux.go#L272[10]。
我们查看命名空间 default 下某个 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 服务器,即 dnsPolicy=ClusterFirst 的结果。
search 与 ndots
search 用于指定默认的搜索域。当你在使用不完全限定域名(例如,只提供主机名而没有域名)进行域名解析时,系统会尝试在搜索域中找到匹配的完全限定域名。搜索域按照出现的顺序进行搜索,直到找到匹配的域名或搜索完所有的域名。
ndots 用于指定在进行域名解析时,系统自动添加域名的点号个数阈值。当提供的域名中点号的个数达到或超过这个阈值时,系统会将其视为完全限定域名,而不再使用搜索域进行搜索。默认为 1,这里将其设置为 5。
注:ndots 的值大小会影响 DNS 解析的性能,为了获得较好的性能,建议使用 FQDN 进行服务访问,以及将 ndots 改为更小的值。
Pod DNS 解析
当在 Pod 中执行 DNS 解析时,查询请求被发到本地(pod 中)的 DNS 解析器。这个解析器先在缓存中查询,如果未命中,则会根据 /etc/resolv.conf 中的配置,将请求发到上游的 DNS 服务器,即集群 DNS 服务器 10.96.0.10 完成域名解析。
根据前面的介绍,假如我们要解析的域名是 foo.bar,会依次进行如下的查询:
- foo.bar.default.svc.cluster.local
- foo.bar.svc.cluster.local(匹配到结果)
当我们使用 foo.bar、foo.bar.svc 都可以完成解析,但 foo.bar.svc.cluster 不行,因为追加了搜索域后无法匹配到结果。假如请求方与目标服务在同一个命名空间下,只用 foo 也是可以的。
参考资料
[1] 追踪过 Kubernetes 中的网络数据包: https://atbug.com/tracing-network-packets-in-kubernetes/
[2] kube-dns: https://github.com/kubernetes/dns
[3] CoreDNS: https://coredns.io
[4] Kubernetes 的基于 DNS 的服务发现规范: https://github.com/kubernetes/dns/blob/master/docs/specification.md
[5] 浅析 CoreDNS 的工作机制: https://atbug.com/analysis-of-the-working-mechanism-of-coredns/
[6] 解析 kubelet 源码: https://atbug.com/how-kubelete-container-runtime-work-with-cni/#创建-pod
[7] 创建 pod sandbox 配置时: 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