追踪 Kubernetes 中的 DNS 查询

2023年 7月 18日 29.9k 0

在过去的文章中,我们曾 追踪过 Kubernetes 中的网络数据包[1],这篇文章将追踪 Kubernetes 中的 DNS 查询。

让我们以在 Pod 中解析 Service 完全限定域名(FQDN) foo.bar.svc.cluster.local 为例。

在开始之前,先回顾下 DNS 的解析流程。

DNS 的解析流程

图片图片

简化版的 DNS 处理流程:

  • DNS 客户端(如浏览器、应用程序或者设备)发送域名 example.com 的查询请求。
  • DNS 解析器收到请求,查询本地缓存,如果本地有记录且未过期会返回本地的记录。
  • 如果本地缓存未命中,DNS 解析器将从 DNS 根服务器开始向下查询,首先是顶级域名(Top Level Domain, TLD) DNS 服务器(这里是 .com),一直向下直到可以解析 example.com 的服务器。
  • 能够解析 example.com 的服务器成为权威 DNS 名称服务器(Authoritative DNS name server),解析器访问该服务器并收到 IP 地址等相关信息,然后返回给给客户端。解析完成。
  • 从流程来看非常重要的一项配置就是上游 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

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论