他的回复:
先说结论:报错 lookup xxx.default.svc.cluster.local on 10.247.3.10:53: read udp i/o timeout 的意思是,Pod 在超时时间内没有收到 DNS 响应。间歇性、几秒自愈、重试成功,说明 CoreDNS 大概率没有彻底挂,而是某个环节在高峰或个别节点上丢包、排队或被限流。Running 只说明进程在,不说明查询没有排队。定位不要先扩容,要用对照实验把故障段定死:客户端、Service、CoreDNS、节点网络、上游,一段一段排除。 一、先定性:三个问题缩小范围1. 只在集群内 Service 名超时,还是外部域名也超时?前者与上游无关,查 CoreDNS 和节点链路;后者再查 forward 上游。2. 全集群抖,还是只集中在个别节点、个别 Pod?只在个别节点复现,优先查该节点网络和 conntrack,而不是 CoreDNS 全局。3. 超时是否与业务高峰、定时任务同步?同步出现多半是请求量冲高导致的排队或限流。动手前先记下故障时间、Pod 所在节点、查询域名和解析结果,后面所有对照都要和这个时间对齐。 二、客户端侧:先排除查询被放大在故障 Pod 内看 cat /etc/resolv.conf,确认 nameserver 是不是当前 kube-dns Service 的 ClusterIP。先用 kubectl -n kube-system get svc kube-dns 核实 10.247.3.10 是否仍是当前地址,集群重建或改过 Service 后地址会变,别拿着旧地址查半天。默认 ndots:5 下,短名字会按 search 域逐个拼接试解析,一次业务调用可能放大成多次查询;Go、Java 等运行时的解析器还会同时发 A 和 AAAA 两路查询。再乘以业务并发,CoreDNS 的 QPS 会被放大数倍。对照:dig 完整域名 xxx.default.svc.cluster.local(带结尾点更好)与 dig 短名字 xxx 分别测。完整域名稳定、短名字才抖,优先让业务代码用完整域名,必要时在 Pod 的 dnsConfig 里收紧 search 域或调 ndots,并在测试 Pod 先验证。另外,glibc 默认单次查询超时 5 秒、重试 2 次,这就是“失败持续几秒后又自行恢复”的来源之一:不是 DNS 自己好了,是重试赶上了空闲窗口。不要只看一次成功,要连续测 20 到 50 次看失败率和耗时分布:for i in $(seq 1 50); do dig xxx.default.svc.cluster.local +time=2 +tries=1 | grep 'Query time' || echo timeout; done 三、四组对照实验,把故障段定死1. Service IP 对 CoreDNS Pod IP:dig @10.247.3.10 xxx.default.svc.cluster.local 与 dig @ xxx.default.svc.cluster.local 对比。Service IP 超时、Pod IP 正常,查 kube-proxy、Service 端点和节点数据面;两者都超时,查 CoreDNS 负载和节点网络。2. 集群内对外部:nslookup xxx.default.svc.cluster.local 与 nslookup www.example.com。集群内域名由 CoreDNS 的 kubernetes 插件直接回答,一般不走上游;只有外部域名异常,才重点查 forward 配置和上游。3. 故障节点对正常节点:同一查询在不同节点 Pod 里各跑 50 次。只在个别节点抖,查该节点;全集群抖,查 CoreDNS 全局。4. 高峰对平峰:把超时时刻与业务 QPS、定时任务、CoreDNS 指标曲线对齐。 四、CoreDNS 本体:日志、指标、资源三件套kubectl -n kube-system get pods -o wide | grep coredns,看副本分布、重启次数、是否集中在同一节点;kubectl -n kube-system logs -l k8s-app=kube-dns --since=30m | grep -iE 'timeout|SERVFAIL|refused',看故障时段有没有超时、SERVFAIL、转发失败。节点 CPU 不高不等于 CoreDNS 不被限流,要看 Pod 自身的 CPU limit 和节流:kubectl top pod、describe pod 看 RESTARTS 和 events,Prometheus 里看 container_cpu_cfs_throttled_seconds_total 是否在故障时段抬升。关键指标(Prometheus):请求量 sum(rate(coredns_dns_requests_total[5m])) by (rcode),P99 延迟 histogram_quantile(0.99, sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le)),缓存命中率 coredns_cache_hits_total / coredns_dns_requests_total,转发失败看 coredns_forward 相关指标。延迟和 SERVFAIL 与故障时间同步抬升,才是 CoreDNS 瓶颈的实锤;指标平稳而客户端超时,问题多半在链路或客户端。副本数不要拍脑袋:按峰值 QPS 除以单副本可承受 QPS 估算,至少保留 2 副本、用反亲和打散到不同节点;节点多时可配 CoreDNS Autoscaler 按节点数自动调副本。扩容前先备份 Corefile,生产变更走 CCE 组件配置持久化,不要只 kubectl scale 临时改。 五、节点网络:conntrack、抓包、TCP 53UDP 查询量大时,conntrack 表满或 UDP 超时回收会表现为随机丢包式超时,扩 CoreDNS 副本解决不了。节点上看:cat /proc/sys/net/netfilter/nf_conntrack_count 与 nf_conntrack_max 的比值、conntrack -S、dmesg -T | grep -i 'nf_conntrack: table full'。表满要调大 nf_conntrack_max,并同步调大 nf_conntrack_buckets,只调上限会加剧哈希冲突。抓包确认请求到底有没有到 CoreDNS:在 CoreDNS Pod 所在节点 tcpdump -i any -nn port 53 -c 100。有请求无响应,看 CoreDNS;无请求,看 iptables/IPVS 转发或 Service 端点是否为空(kubectl -n kube-system get endpoints kube-dns,以及 endpointslices)。别漏 TCP 53:响应超过 512 字节被截断时会转 TCP 重查,安全组或 NetworkPolicy 只放 UDP 53、没放 TCP 53,大记录查询就会间歇失败。VPC 网络模式下还要看节点 ENI 配额与路由。 六、上游与 Corefilekubectl -n kube-system get configmap coredns -o yaml,重点看 forward、cache、errors、health、ready、loop、loadbalance、kubernetes 插件的配置与顺序。外部域名异常时,从 CoreDNS Pod 内直接 dig @上游地址> www.example.com,验证上游可达性与延迟,并确认上游地址、路由、安全组没有阻断 UDP/TCP 53。不要未经评估把 forward 改成固定公共 DNS,要用网络环境允许且经过验证的上游,并确认其具备内网域名解析能力。 七、找噪声源:是谁在打爆 CoreDNS扩容前先找噪声源,否则只是把噪声放大。临时开启 CoreDNS 的 log 插件(或用 Prometheus 按客户端聚合),把查询按来源 Pod IP 聚合排序;常见噪声源是某个 Pod 的短名字高频查询、健康检查或重试风暴。找到后先修客户端(完整域名、合理重试间隔与退避、应用层缓存),再谈扩容。 八、根治方向与验证按证据选,不要一次全改:1. search/ndots 放大:业务用完整域名,收紧 dnsConfig。2. CoreDNS 排队或单点:加副本、反亲和打散、放宽 CPU limit、配 Autoscaler。3. 跨节点 UDP 丢包或 conntrack 压力:部署 NodeLocal DNSCache(节点本地缓存,Pod 先问本机 169.254.20.10,未命中再转发 CoreDNS),可把 CoreDNS 的 QPS 降一个数量级,同时消除跨节点 UDP 查询和连接跟踪压力。4. 重复查询多:评估 cache 30 等短期缓存,TTL 不宜过长,避免服务地址变更后客户端长期拿到旧记录;负向缓存也可能延迟新建服务被发现。验证:改完不要只看一次成功,在多个节点的业务 Pod 里连续 dig 50 次,看超时率是否归零、P99 延迟是否回落;对照故障时段看 SERVFAIL 数是否回落;并给 DNS P99 延迟、SERVFAIL 率、CoreDNS CPU 节流加告警,下次抖动能第一时间定位到段。