深度复盘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 在尝试解析集群内部域名(如 kubernetes 或 kube-dns)时,可能会携带这个外部搜索域去查询 CoreDNS,或者在集群内部解析失败后,尝试向 114.114.114.114 进行查询,从而导致不必要的延迟或错误。
第一次排查:标准路径的检查
我们首先怀疑是 Kubelet 在生成 Pod 的 resolv.conf 时,从某个配置源读取了错误的 DNS 信息。
-
宿主机
/etc/resolv.conf和/run/systemd/resolve/resolv.conf:
这是 Kubelet 默认或配置的上游 DNS 信息来源。
检查结果: 宿主机上的这两个文件都很“干净”,不包含任何search行,只包含正常的nameserver配置(例如nameserver 114.114.114.114或nameserver 223.5.5.5)。这排除了宿主机resolv.conf直接被污染的可能性。 -
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(指向的宿主机文件是干净的,理论上也正确)
这看起来一切正常,更加令人困惑。
-
Kubelet 进程启动参数:
通过ps aux | grep kubelet检查,没有发现通过命令行参数显式设置--cluster-domain或--resolv-conf,表明 Kubelet 确实完全依赖config.yaml。 -
kubeadm-configConfigMap:
这是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 中。
解决办法:
- 直接修改宿主机网络配置(例如
netplan配置): 移除或修正任何可能导致114.114.114.114作为search域出现的配置项。 - (可选,但强烈推荐)强制 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
- 编辑 Kubelet 的 Systemd Drop-in 文件:
结果: 经过此番操作,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 输出依然有些奇怪)。
-
检查 CoreDNS Pod 状态和日志:
检查结果: CoreDNS Pod 运行正常,但日志中出现了关键错误:
[ERROR] plugin/errors: 5 kube-dns.kube-system. AAAA: concurrent queries exceeded maximum 1000
这直接解释了REFUSED错误,表明 CoreDNS 达到了内部并发查询限制,拒绝处理请求。 -
检查 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.conf是127.0.0.53(systemd-resolved stub resolver),再转发到上游 DNS。这增加了 CoreDNS 查询外部域名的层数,可能引入延迟和并发压力。直接转发到上游 DNS 更高效。
-
版本兼容性考量:
集群的 Kubernetes 版本是1.23.17,而 CoreDNS 版本是1.12.0。Kubernetes 1.23.x通常推荐搭配CoreDNS 1.8.x或1.9.x。版本差距可能导致某些不兼容或意料之外的行为,从而引发并发问题。
解决办法:
-
优化 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 会自动重启。
- 移除
-
降级 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 解析库,能够正常地通过短域名访问集群内部服务。
经验总结与教训
这次排查经历为我们带来了宝贵的经验:
- DNS 问题是多层级的: Kubernetes DNS 涉及 Pod 的
resolv.conf、Kubelet 配置、宿主机网络配置、CoreDNS 配置、CoreDNS 版本以及应用程序自身的解析行为。排查时需逐层分析,不能遗漏任何环节。 - 警惕隐藏的污染源: 宿主机看似干净的
resolv.conf,并不代表其底层网络配置没有隐式地添加search域。这种隐蔽的污染源可能导致意想不到的问题。 - CoreDNS 配置至关重要: CoreDNS 的
Corefile配置应简洁高效。pods insecure这种选项在非必要情况下应避免使用。forward插件应直接指向稳定、高效的上游 DNS 服务器,而不是通过宿主机的systemd-resolved中转。 - 版本兼容性不容忽视: Kubernetes 各组件之间存在版本兼容性要求。当遇到难以解释的问题时,考虑组件版本是否匹配是一个重要的排查方向。
- 不完全信任单一诊断工具: 尤其是像 BusyBox 环境下的
nslookup这样功能精简的工具,其输出可能存在误导性。当怀疑工具问题时,应使用不同的工具(如ping、dig,或直接测试应用程序)进行交叉验证,或使用功能更完整的调试容器。ping命令在底层依赖操作系统解析器,因此在验证 DNS 解析时,它通常比nslookup更可靠。
结语
从最初诡异的 114.114.114.114,到 CoreDNS 的 REFUSED 错误,再到 nslookup 的误报,这次 Kubernetes DNS 故障排查过程充满了挑战。但通过系统性、多维度的分析和验证,我们最终成功地解决了所有问题,确保了集群内部的 DNS 正常运行。
希望这次详细的排查记录,能为你将来遇到类似问题提供一些思路和帮助。排查复杂问题如同解谜,每一次突破都充满成就感!
浙公网安备 33010602011771号