深度复盘coredns的疑难杂症

Kubernetes DNS 疑难杂症:从意外的114到nslookup的陷阱,深度排查与修复之路

前言

在 Kubernetes 集群中,DNS 解析是服务发现和内部通信的基石。如果 DNS 不工作,那么整个集群就如同“断网”一般,服务无法互相访问,外部应用也无法正常请求集群内部资源。通常,Kubernetes 的 DNS 配置相对稳定,但有时会遇到一些令人费解的异常情况。

本文将详细记录一次从 Pod /etc/resolv.conf 中出现意外的外部 DNS 服务器 IP (114.114.114.114),到 CoreDNS 拒绝服务,再到最终发现 nslookup 工具误报的漫长而曲折的排查和修复过程。这不仅是对问题本身的记录,更是对 Kubernetes DNS 机制、系统工具行为以及复杂问题排查思路的一次深入探讨。

问题背景

说明:k8s集群删除flannel插件,安装新的cni插件之calico时,重启集群,发现各节点的coredns组件不正常工作了,然后就把coredns组件的资源清单-o yaml下来,删去原来的pod指定为hostnetwork类型的ds资源,然后打上污点进行apply,然后再进行安装calico,calico安装完毕后,把原来的coredns资源清单,部署一遍注意和k8s集群对应,然后再calico目录下apply测试的ubuntu镜像进行测试,看是否coredns的功能正常。其中注意coredns的rbac和calico的rbac规则。

问题初现:诡异的 114.114.114.114

最初,Pod 内部的 /etc/resolv.conf 文件出现了异常:

nameserver 10.200.0.10
search default.svc.oldboyedu.com svc.oldboyedu.com oldboyedu.com 114.114.114.114
options ndots:5

其中,114.114.114.114 是一个公共 DNS 服务器,它不应该出现在集群内部的 search 路径中。这导致 Pod 在尝试解析集群内部域名(如 kuberneteskube-dns)时,可能会携带这个外部搜索域去查询 CoreDNS,或者在集群内部解析失败后,尝试向 114.114.114.114 进行查询,从而导致不必要的延迟或错误。

第一次排查:标准路径的检查

我们首先怀疑是 Kubelet 在生成 Pod 的 resolv.conf 时,从某个配置源读取了错误的 DNS 信息。

  1. 宿主机 /etc/resolv.conf/run/systemd/resolve/resolv.conf
    这是 Kubelet 默认或配置的上游 DNS 信息来源。
    检查结果: 宿主机上的这两个文件都很“干净”,不包含任何 search 行,只包含正常的 nameserver 配置(例如 nameserver 114.114.114.114nameserver 223.5.5.5)。这排除了宿主机 resolv.conf 直接被污染的可能性。

  2. Kubelet config.yaml (/var/lib/kubelet/config.yaml):
    这是 Kubelet 的主要配置来源。
    检查结果:

    • clusterDNS: - 10.200.0.10 (正确,指向 CoreDNS 的 Cluster IP)
    • clusterDomain: oldboyedu.com (正确,自定义的集群域名)
    • resolvConf: /run/systemd/resolve/resolv.conf (指向的宿主机文件是干净的,理论上也正确)
      这看起来一切正常,更加令人困惑。
  3. Kubelet 进程启动参数:
    通过 ps aux | grep kubelet 检查,没有发现通过命令行参数显式设置 --cluster-domain--resolv-conf,表明 Kubelet 确实完全依赖 config.yaml

  4. kubeadm-config ConfigMap:
    这是 kubeadm 工具初始化集群时生成的核心配置。
    检查结果: networking.dnsDomain: oldboyedu.com (正确)。

到这里,我们陷入了僵局:所有明面上的配置都显示是正确的,但 Pod 的 resolv.conf 依然被污染。

柳暗花明:隐藏的网卡 search 域污染

经过反复排查,最终发现了一个非常隐蔽的污染源:宿主机的网络接口配置中,可能存在隐式的 search 域设置!

例如,在使用 netplan 配置工具的系统中,虽然 /etc/resolv.conf/run/systemd/resolve/resolv.conf 看起来很干净,但 netplan 在实际配置网络时,可能将某个 nameserver IP (如 114.114.114.114) 错误地也添加为 search 域。某些版本的 systemd-resolved 或 Kubelet 在与这些底层网络配置交互时,可能会将这些隐式或错误的 search 域意外地混入到 Pod 的 resolv.conf 中。

解决办法:

  1. 直接修改宿主机网络配置(例如 netplan 配置): 移除或修正任何可能导致 114.114.114.114 作为 search 域出现的配置项。
  2. (可选,但强烈推荐)强制 Kubelet 使用指定干净的 resolv.conf 即使宿主机网络配置修复,为了确保万无一失,我们可以强制 Kubelet 启动时使用一个明确的、已知干净的 resolv.conf 文件。
    • 编辑 Kubelet 的 Systemd Drop-in 文件:sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
    • ExecStart 行的末尾添加 --resolv-conf=/etc/resolv.conf (假设 /etc/resolv.conf 是一个干净的符号链接或文件):
      ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS --resolv-conf=/etc/resolv.conf
      
    • 重载 Systemd 配置并重启 Kubelet:sudo systemctl daemon-reload && sudo systemctl restart kubelet
    • 验证 Kubelet 进程参数中是否包含此项:ps aux | grep kubelet

结果: 经过此番操作,Pod 内部的 /etc/resolv.conf 终于恢复正常,不再包含 114.114.114.114

nameserver 10.200.0.10
search default.svc.oldboyedu.com svc.oldboyedu.com oldboyedu.com 
options ndots:5

第二阶段问题:CoreDNS 拒绝服务和短域名解析失败

114.114.114.114 的问题解决后,新的问题浮现:Pod 内部的 nslookup 短域名解析仍然失败,比如 nslookup kube-dns.kube-system 返回 REFUSED (拒绝) 或 NXDOMAIN (域名不存在)。然而,FQDN (完全限定域名) 如 nslookup kube-dns.kube-system.svc.oldboyedu.com 却可以解析成功(尽管 nslookup 输出依然有些奇怪)。

  1. 检查 CoreDNS Pod 状态和日志:
    检查结果: CoreDNS Pod 运行正常,但日志中出现了关键错误:
    [ERROR] plugin/errors: 5 kube-dns.kube-system. AAAA: concurrent queries exceeded maximum 1000
    这直接解释了 REFUSED 错误,表明 CoreDNS 达到了内部并发查询限制,拒绝处理请求。

  2. 检查 CoreDNS Corefile 配置:

    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes oldboyedu.com  {
           pods insecure           # <--- 潜在问题点
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf { # <--- 潜在问题点
           max_concurrent 1000
        }
        cache 30
        reload
        loadbalance
    }
    

    发现了两个可能的问题点:

    • pods insecure: 允许 CoreDNS 解析 Pod 的 IP 地址,这增加了 CoreDNS 的负担和复杂度,可能导致内部并发问题。
    • forward . /etc/resolv.conf: 宿主机的 /etc/resolv.conf127.0.0.53 (systemd-resolved stub resolver),再转发到上游 DNS。这增加了 CoreDNS 查询外部域名的层数,可能引入延迟和并发压力。直接转发到上游 DNS 更高效。
  3. 版本兼容性考量:
    集群的 Kubernetes 版本是 1.23.17,而 CoreDNS 版本是 1.12.0Kubernetes 1.23.x 通常推荐搭配 CoreDNS 1.8.x1.9.x。版本差距可能导致某些不兼容或意料之外的行为,从而引发并发问题。

解决办法:

  1. 优化 CoreDNS Corefile:

    • 移除 pods insecure
    • forward . /etc/resolv.conf 修改为 forward . 223.5.5.5 (或你的实际外部 DNS 服务器 IP)。
    kubectl edit cm coredns -n kube-system
    

    修改后的 Corefile 片段:

    kubernetes oldboyedu.com {
       fallthrough in-addr.arpa ip6.arpa
       ttl 30
    }
    prometheus :9153
    forward . 223.5.5.5 { # 直接转发到外部 DNS
       max_concurrent 1000
    }
    

    保存后 CoreDNS Pod 会自动重启。

  2. 降级 CoreDNS 版本:
    考虑到版本兼容性,将 CoreDNS 从 1.12.0 降级到与 Kubernetes 1.23 更匹配的 1.9.2

    kubectl get deploy coredns -n kube-system -o yaml > coredns-deploy.yaml
    

    编辑 coredns-deploy.yaml 文件,将 image 字段修改为 registry.k8s.io/coredns/coredns:1.9.2 (或其他兼容版本)。

    # ...
        image: registry.k8s.io/coredns/coredns:1.9.2 # 修改这一行
    # ...
    

    应用修改:

    kubectl apply -f coredns-deploy.yaml
    

结果: CoreDNS 日志中不再出现 concurrent queries exceeded 错误。外部域名 www.baidu.com 也能正常解析。

最终的真相:nslookup 的误报

尽管 CoreDNS 的错误日志消失,并且外部域名解析正常,但 Pod 内部的 nslookup kube-dns.kube-system 依然返回 NXDOMAIN。这让人再次感到困惑。

最终的验证:使用 ping 命令。
nslookup 作为一个独立的工具,在精简的 BusyBox 环境中,其 DNS 解析行为可能与系统底层库(如 getaddrinfo)有所不同,或者存在 bug。ping 命令则会使用系统底层的 DNS 解析库来解析域名。

在 Pod 内部执行:

kubectl run -it --rm debug-dns --image=ubuntu -- bash
# 在 ubuntu Pod 中:
apt update && apt install -y dnsutils iputils-ping
dig kube-dns.kube-system
ping -c 1 kubernetes

/ # ping -c 1 kube-dns.kube-system
PING kube-dns.kube-system (10.200.0.10): 56 data bytes
64 bytes from 10.200.0.10: seq=0 ttl=64 time=0.144 ms

--- kube-dns.kube-system ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.144/0.144/0.144 ms

/ # ping -c 1 kubernetes
PING kubernetes (10.200.0.1): 56 data bytes
64 bytes from 10.200.0.1: seq=0 ttl=64 time=0.632 ms

--- kubernetes ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.632/0.632/0.632 ms

结果令人振奋:ping 命令成功解析了短域名并通信!

这最终证实了:Pod 内部的 DNS 解析实际上是完全正常的,之前 nslookup 报告的 NXDOMAIN 只是其工具本身的误报或行为缺陷,而非实际的 DNS 解析问题。 应用程序使用系统底层的 DNS 解析库,能够正常地通过短域名访问集群内部服务。

经验总结与教训

这次排查经历为我们带来了宝贵的经验:

  1. DNS 问题是多层级的: Kubernetes DNS 涉及 Pod 的 resolv.conf、Kubelet 配置、宿主机网络配置、CoreDNS 配置、CoreDNS 版本以及应用程序自身的解析行为。排查时需逐层分析,不能遗漏任何环节。
  2. 警惕隐藏的污染源: 宿主机看似干净的 resolv.conf,并不代表其底层网络配置没有隐式地添加 search 域。这种隐蔽的污染源可能导致意想不到的问题。
  3. CoreDNS 配置至关重要: CoreDNS 的 Corefile 配置应简洁高效。pods insecure 这种选项在非必要情况下应避免使用。forward 插件应直接指向稳定、高效的上游 DNS 服务器,而不是通过宿主机的 systemd-resolved 中转。
  4. 版本兼容性不容忽视: Kubernetes 各组件之间存在版本兼容性要求。当遇到难以解释的问题时,考虑组件版本是否匹配是一个重要的排查方向。
  5. 不完全信任单一诊断工具: 尤其是像 BusyBox 环境下的 nslookup 这样功能精简的工具,其输出可能存在误导性。当怀疑工具问题时,应使用不同的工具(如 pingdig,或直接测试应用程序)进行交叉验证,或使用功能更完整的调试容器。ping 命令在底层依赖操作系统解析器,因此在验证 DNS 解析时,它通常比 nslookup 更可靠。

结语

从最初诡异的 114.114.114.114,到 CoreDNS 的 REFUSED 错误,再到 nslookup 的误报,这次 Kubernetes DNS 故障排查过程充满了挑战。但通过系统性、多维度的分析和验证,我们最终成功地解决了所有问题,确保了集群内部的 DNS 正常运行。

希望这次详细的排查记录,能为你将来遇到类似问题提供一些思路和帮助。排查复杂问题如同解谜,每一次突破都充满成就感!

posted on 2025-06-12 16:56  Leo_Yide  阅读(375)  评论(0)    收藏  举报