一、事故背景
某天上午,集群监控突然开始告警:多个服务间调用延迟飙升、部分请求直接超时。起初我们以为是偶发的网络抖动(没有版本变更),但随着告警范围扩大、业务量上升,问题开始呈现“指数级爆发”。
翻查日志后,几条关键的错误信息引起注意:
plugin/errors aps-service.paas.svc.cluster.local,localdomain, A: unreachable backend: no upstream host
aps-service.paas.svc.cluster.local.localdomain. A: unreachable backend: read udp 172.21.33.4:44195->8.8.4.4:53: i/o timeout
看似只是 DNS 超时,然而在 Kubernetes 集群中,这种异常往往牵扯的不止一层。
二、故障回溯
经过排查,最终我们发现所有出问题的 Pod 都部署在新扩容的 K8s 节点上。
实际上,这个节点在扩容当天就已经有部分服务实例运行在上面,但由于集群中同类实例较多,系统整体仍能正常工作,冒烟测试也顺利通过,因此问题并未立即显现。
直到第二天业务高峰期,随着流量上升、调度器在该节点上分配了更多 Pod,DNS 异常才被集中放大,导致调用超时、告警频发,故障由此全面爆发。
三、初步分析
错误信息揭示了几个关键点:
- aps-service.paas.svc.cluster.local → 目标是集群内部服务
- unreachable backend → CoreDNS 无法找到可用后端
- no upstream host → 没有健康的上游可响应
- read udp -> 8.8.4.4:53 timeout → CoreDNS 查询外部 DNS 时超时
问题范围限定在新节点,且与 DNS 强相关。下一步,进入问题节点的 Pod 内查看 DNS 配置。
四、现场取证
在故障 Pod 内执行:
cat /etc/resolv.conf
输出如下:
search default.svc.cluster.local svc.cluster.local cluster.local local-domain
nameserver 10.245.0.10
options ndots:5
发现问题关键点:多出了一个 localdomain 搜索域。这意味着,当应用尝试解析 aps-service.paas.svc.cluster.local 时,系统会依次尝试:
- aps-service.paas.svc.cluster.local.default.svc.cluster.local
- aps-service.paas.svc.cluster.local.svc.cluster.local
- aps-service.paas.svc.cluster.local.cluster.local
- aps-service.paas.svc.cluster.local.localdomain ← ❌ 错误附加
- aps-service.paas.svc.cluster.local
最后一步解析 .localdomain 时超时,从而导致整个请求链路阻塞。
五、深挖根因
Pod 的 DNS 配置来源于 kubelet 的 --resolv-conf 参数,我们检查了新节点的配置:
cat /var/lib/kubelet/config.yaml | grep resolvConf
resolvConf: "/etc/resolv.conf"
再查看宿主机的 /etc/resolv.conf:
# Generated by NetworkManager
search localdomain
nameserver 10.228.5.240
真相浮现—— NetworkManager 自动生成的 resolv.conf 添加了 “localdomain” 搜索域, 而 kubelet 恰好继承了这份配置,污染到了所有在此节点运行的 Pod。
也就是说,问题根源是扩容节点的 DNS 被 NetworkManager 污染!
六、修复过程
步骤一:清理 NetworkManager 干扰
在服务器(K8s 节点)上,NetworkManager 主要用于桌面系统的网络自动化管理,而在生产环境中,它往往会与手动配置的网络或容器网络插件(如 CNI)产生冲突。最典型的问题就是:它会自动重写 /etc/resolv.conf,加入类似 search localdomain 的字段,进而污染 Pod 的 DNS 配置。推荐做法(稳定优先): 直接关闭 NetworkManager 服务,彻底避免其干扰。
systemctl stop NetworkManager
systemctl disable NetworkManager
同时,删除 /etc/resolv.conf 文件中的这一行:
search localdomain
这样可确保节点的 DNS 配置保持稳定,不再被系统进程自动修改。
如果出于运维统一或其他原因,不便关闭 NetworkManager: 可以通过修改其配置文件来禁用 DNS 管理功能:编辑 /etc/NetworkManager/NetworkManager.conf:
[main]
dns=none
保存后执行:
systemctl restart NetworkManager
此配置将阻止 NetworkManager 再次覆盖 /etc/resolv.conf,在保持服务运行的同时也能避免污染集群 DNS。
步骤二:修正 kubelet 配置
编辑配置文件:
vim /var/lib/kubelet/config.yaml
将:
resolvConf: "/etc/resolv.conf"
改为:
resolvConf: "/run/kubelet/resolv.conf"
新文件内容保持最简洁:
nameserver 10.245.0.10
search svc.cluster.local cluster.local
options ndots:5
保存后重启 kubelet:
systemctl restart kubelet
步骤三:验证结果
在修复节点后,重新调度 Pod 并验证:
kubectl exec -it-- nslookup aps-service.paas.svc.cluster.local
返回结果正常:
Name: aps-service.paas.svc.cluster.local
Address: 10.254.12.8
DNS 解析恢复,服务调用延迟恢复正常。
七、经验复盘
根因链条完整复盘:
节点扩容 → NetworkManager 自动生成 localdomain → kubelet 继承 /etc/resolv.conf →
Pod DNS 被污染 → CoreDNS 查询错误 → 大规模 DNS 超时
经验总结:
- Kubernetes 节点不应依赖 NetworkManager
- 关闭该服务或在系统初始化时禁用。
- 注意不要误配置
- 统一 kubelet 的 resolv.conf 配置
- 推荐使用 /run/kubelet/resolv.conf 或专门维护的模板文件。
- DNS 配置应保持最小可控
- 避免多余 search 域和外部 nameserver 污染。
- 扩容节点上线前要做健康校验
- 包括 kubelet 配置、网络路由、DNS 可达性等。
八、结语
这次看似简单的 DNS 故障,其实隐藏了扩容节点配置不一致的问题。 更值得警醒的是:问题并未立即暴露,而是在第二天业务高峰时集中爆发。在复杂的云原生体系中,稳定性从来不靠“侥幸”,而是靠每一个小细节的规范与一致。
评论区