☰
Kubernetes集群DNS故障排查实战:CoreDNS解析超时定位与修复
2026/10/9 8:24:20 网站建设 项目流程

凌晨两点,手机震动。业务群里开始刷屏:接口超时、域名解析失败。我打开终端,第一件事不是去看 Prometheus 大盘,而是先kubectl get pods -n kube-system—— 在这个时间点,凭经验判断,最可能出问题的组件是 CoreDNS。果然,进业务 Pod 执行nslookup,直接卡住超时;再看集群里的kube-dnsService,也是不通的。这类故障在 Kubernetes 里相当典型:现象是 nslookup 超时、Service 不通,但根因可能藏在 Endpoint、kube-proxy 规则、CoreDNS 配置甚至 conntrack 表里。这篇文章就把我完整走过一遍的排查流程拆开来讲,从链路原理到每个命令的判断依据,再到修复验证,希望帮你下次遇到类似问题时少走弯路。适合刚接手集群的运维、SRE,也包括在自己的测试环境里被 Pod 域名解析折腾过的开发同学。

1. 解析链路全貌:从应用到 CoreDNS,nslookup 超时意味着哪一环断了

1.1 CoreDNS 和 kube-dns:为什么组件名和 Service 名对不上

很多人第一次排查时会懵:我明明看的是 CoreDNS,为什么 Service 叫 kube-dns?这其实是历史遗留。Kubernetes 从早期版本开始就用 kube-dns 作为集群 DNS 的名称,后来 CoreDNS 在 1.13 版本成为默认 DNS 组件,但 Service 对象名没有变。原因很现实:集群里所有 Pod 的/etc/resolv.conf都把nameserver指向这个 Service 的 ClusterIP,改名字会造成大范围配置文件变更,官方干脆保留了 kube-dns 这个名字。

所以排障时要分清楚两个对象:

  • CoreDNS:实际干活的一组 Pod,以 Deployment 方式部署在 kube-system 命名空间。
  • kube-dns Service:集群内暴露 DNS 服务的入口,是一个 ClusterIP 类型的 Service,selector 默认是k8s-app: kube-dns。

我们说的“kube-dns Service 不通”,通常是指 Service IP 的 53 端口没有响应;说“CoreDNS 挂了”,则是指 Deployment 或 Pod 状态异常。这是两个层面,不能混在一起判断。这个区分是后续所有排查的基础。

1.2 DNS 请求在集群里的七步走向

要知道 nslookup 超时是哪一环断了,得先把 DNS 请求在集群里的完整路径画出来。用一个生活类比:Pod 想解析域名,相当于给总机打电话,总机转接到分机,分机查不到再问外部黄页。

具体链路是这样的:

  1. 业务进程调用getaddrinfo发起 DNS 查询,glibc 读取容器内/etc/resolv.conf。
  2. resolv.conf里的 nameserver 指向 kube-dns Service 的 ClusterIP,默认是10.96.0.10。
  3. DNS 请求从 Pod 的 eth0 出去,经过 veth 对到达宿主机。
  4. 宿主机 netfilter 的 PREROUTING/OUTPUT 链命中 kube-dns 对应的 KUBE-SVC 规则。
  5. 规则通过负载均衡选一个后端,做 DNAT 成具体 CoreDNS Pod 的 IP。
  6. CoreDNS Pod 收到查询,按 Corefile 配置逻辑处理:集群内域名走kubernetes插件,外部域名走forward插件发给上游 DNS。
  7. 查询结果按原路径返回给业务进程。

这条链路里,步骤 2 到 5 属于网络转发层,步骤 6 属于服务自身逻辑层。任何一层出问题,表面现象都可能是一模一样的 nslookup 超时,但修复方法完全不同。这也是为什么我一再强调先定位故障面,再动手操作。

1.3 从报错形态反推链路断点

nslookup 的报错是一个很好的“探针”,不同类型的报错能把问题缩小到不同范围。我平时基本只看三种:

报错类型含义优先排查方向
connection timed out; no servers could be reached请求发出后没收到响应网络转发层、CoreDNS 是否存活、conntrack 是否满
server can't find xxx: NXDOMAINDNS 服务活着,但查不到记录Service/域名是否写错、namespace 是否正确
SERVFAILDNS 服务处理不了该查询CoreDNS 上游 forward 异常、kubernetes 插件查询失败

特别提醒:如果业务 Pod 里执行 nslookup 直接超时,但换一个 Pod 或者换一个节点就正常,那说明不是全局性故障,大概率是指定节点上的 kube-proxy 或网络问题。这个判断在后面章节会反复用到。

2. 故障现场取证:先分清 NXDOMAIN、SERVFAIL 和超时,再决定下一步

2.1 进入业务 Pod 执行 nslookup,记录报错类型

排查第一步永远是复现并取证,而不是凭猜断。找到业务方报告异常的那个 Pod,进去执行:

kubectl exec -it <pod-name> -n <namespace> -- nslookup kubernetes.default.svc.cluster.local

如果 Pod 里没有 nslookup,可以用 busybox 临时起一个诊断 Pod:

kubectl run dns-test --image=busybox:1.36 --restart=Never -it --rm -- nslookup kubernetes.default.svc.cluster.local

不过 busybox 的 nslookup 输出比较简陋,我更习惯用带 dig 的镜像,诊断信息更完整:

kubectl run dig-test --image=networkstatic/dig --restart=Never -it --rm -- dig @10.96.0.10 kubernetes.default.svc.cluster.local +time=3 +tries=1

执行完后,把报错类型记录下来。这一步看起来简单,但很多人会忽略“记录”这个动作。我见过太多人看到超时就急着重启 CoreDNS,重启完了问题还在,却连最初的报错长什么样都没留住,使排障陷入被动。

2.2 检查 kube-dns Service、Endpoint 与 Pod 状态

拿到 Pod 内的报错后,回到控制面检查三个对象:

kubectl get svc -n kube-system kube-dns kubectl get endpoints -n kube-system kube-dns kubectl get pods -n kube-system -o wide | grep coredns

一个健康的状态应该是:

  • Service 有正常的 ClusterIP,端口显示53/UDP。
  • Endpoints 列表里至少有 1 到 2 个 IP。
  • CoreDNS Pod 是 Running,且 READY 显示1/1。

这三个条件任何一个不满足,问题基本就锁定在这一层了。尤其是 Endpoints 为空但 Pod 却正常,这是下一章会详细展开的坑。

这里还要注意一个容易误判的场景:如果集群部署了 NodeLocal DNSCache,Pod 里的/etc/resolv.conf可能指向169.254.20.10这类本地缓存地址,而不是直接指向 kube-dns 的 ClusterIP。这时候你查 kube-dns Service 看起来很健康,但业务 Pod 的解析流量根本没经过它。遇到 nslookup 超时,先进业务 Pod 看一眼:

cat /etc/resolv.conf

确认 nameserver 到底指向哪里,再决定排查主线。

2.3 从宿主机探测 ClusterIP 与 Pod IP:排除网络层干扰

有一种常见误区:用 ping ClusterIP 来判断 Service 通不通。在 Kubernetes 里,很多云厂商或网络插件会丢弃对 ClusterIP 的 ICMP 包,ping 不通不代表 53 端口不通。正确的做法是直接探测端口或者用 dig。

在宿主机上执行:

timeout 3 nc -vz 10.96.0.10 53 timeout 5 dig @10.96.0.10 kubernetes.default.svc.cluster.local +time=3 +tries=1

如果宿主机上 dig 正常,但 Pod 内 nslookup 超时,说明问题很可能出在 Pod 网络或 CNI 层;如果宿主机 dig 也超时,那链路断点就在节点转发层或 CoreDNS 自身。

再进一步,拿到一个 CoreDNS Pod IP,直连测试:

COREDNS_POD_IP=$(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].status.podIP}') timeout 5 dig @${COREDNS_POD_IP} kubernetes.default.svc.cluster.local +time=3 +tries=1

这里有一条重要的判断规则:

  • 直连 Pod IP 能解析,连 Service IP 超时 → CoreDNS 是好的,问题在 kube-proxy 或 iptables/ipvs 规则。
  • 直连 Pod IP 也超时 → 问题在 CoreDNS 自身、节点网络或 conntrack。

把这条规则刻在脑子里,后面章节的排查方向就清楚了。

3. Endpoint 为空或 Pod 异常:kube-dns Service 背后没有可用后端的真相

3.1 Pod 处于 Pending:资源、污点、镜像是最常见三个原因

当kubectl get endpoints kube-dns显示为空,第一件事就是看 CoreDNS Pod 状态。Pending状态说明 Pod 根本没调度起来,调度器找不到合适的节点。这种时候describe是最直接的诊断工具:

kubectl describe pod -n kube-system <coredns-pod-name> | tail -40

看 Events 部分,常见原因有三类:

  • 资源不足:0/1 nodes are available: insufficient cpu。CoreDNS 默认请求 100m CPU 和 70Mi 内存,但如果集群资源紧张,它也会调度失败。处理思路是调整 requests、扩容节点,或者在其他负载允许的情况下清理部分 Pod。
  • 污点不匹配:0/1 nodes are available: node(s) had taint。如果节点打了专用污点而 CoreDNS 没有对应 tolerations,就调度不上去。处理思路是确认节点污点和 Pod 容忍配置。
  • 镜像拉取失败:Failed to pull image。默认镜像地址是registry.k8s.io/coredns/coredns,有些网络环境下需要配置镜像仓库 mirror,或者提前把镜像推到节点上。

我的一个习惯是:看到 Pending 时先看节点资源水位和污点,不要急着去改 Deployment。因为有时候是临时资源抖动,过几分钟调度器就会把它拉起来,而改配置反而引入额外变数。

3.2 Pod 处于 CrashLoopBackOff:先看日志再动配置

CrashLoopBackOff 表示 CoreDNS 启动后就崩溃,反复重启。这时千万先看日志:

kubectl logs -n kube-system <coredns-pod-name> --previous

CoreDNS 启动崩溃常见原因有这么几个:

  • Corefile 被改坏:YAML 缩进、插件参数不合法。CoreDNS 启动时会解析 Corefile,解析失败直接退出。
  • 版本与插件不匹配:升级 CoreDNS 版本后,Corefile 里写了旧插件参数或者新版本废弃的配置,也会启动失败。
  • 节点端口被占用:如果节点上有进程占用了 53 端口,CoreDNS 绑定失败。
  • loop 插件检测到环路:CoreDNS 的loop插件专门检测解析环路,一旦发现查询在自己和上游之间无限循环,会输出plugin/loop: Loop ...并主动退出,防止集群 DNS 雪崩。这在配置 forward 指向自己的时候特别容易出现。

改了 Corefile 之后,记得滚动重启:

kubectl -n kube-system rollout restart deployment coredns

然后观察 Pod 是否稳定在 Running。

3.3 Service selector 与 Pod 标签不匹配:Endpoint 为空的经典原因

这是个非常经典、也非常隐蔽的坑:CoreDNS Pod 明明 Running 且 READY 正常,但 Endpoints 就是空的。原因通常只有一个——Service 的 selector 没选中 Pod。

标准配置里,kube-dns Service 的 selector 是k8s-app: kube-dns。如果 CoreDNS Deployment 的 Pod 标签被改过,或者创建时漏了标签,Service 就找不到后端。检查方法很简单:

kubectl get svc -n kube-system kube-dns -o jsonpath='{.spec.selector}{"\n"}' kubectl get pods -n kube-system --show-labels | grep coredns

两边一对,key/value 哪怕差一个字符,Endpoint 都生成不了。修复方式就是在 Deployment 的spec.template.metadata.labels里补上k8s-app: kube-dns。保存后一两秒内 Endpoints 会自动出现。

这里我不建议手动创建 Endpoints 对象来绕过。Endpoints 是 controller 自动管理的,手动维护一旦 Pod 漂移、IP 变化,就会出现更诡异的不一致问题,而且后续排障成本成倍增加。让机制自己恢复才是正道。

4. kube-proxy 规则与节点网络:Service “不通” 但 Pod IP 正常的隐蔽根源

4.1 iptables 模式:KUBE-SVC 链怎么读

当 CoreDNS Pod 正常、Endpoint 有 IP,但宿主机 dig @ClusterIP 超时,接下来就是 kube-proxy 的地盘。先确认 kube-proxy 的模式:

kubectl get cm -n kube-system kube-proxy -o yaml | grep -i mode

如果是 iptables 模式,DNS 请求会命中 NAT 表里的规则。查看:

iptables -t nat -L -n | grep -A 8 10.96.0.10

正常情况下你会看到类似结构:

Chain KUBE-SVC-TCO7W7NROLUPTJ6N (2 references) target prot opt source destination KUBE-SEP-L3... udp -- 0.0.0.0/0 10.96.0.10 udp dpt:53 ... Chain KUBE-SEP-L3... target prot opt source destination DNAT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53 to:10.244.1.5:53

如果对应的 KUBE-SVC 或 KUBE-SEP 链不存在,说明 kube-proxy 没有正确同步 Service 规则。这种情况看 kube-proxy 日志:

kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=100

常见的报错会有Failed to ensure that the node ... has ...这类字样,经常和 CNI 网段变化、并发同步问题有关。

4.2 ipvs 模式:ipvsadm 查看负载均衡规则

如果 kube-proxy 配置为 ipvs 模式,iptables 里看不到 DNAT,要用 ipvsadm 查:

ipvsadm -L -n | grep 10.96.0.10 -A 2

输出里会看到一条UDP 10.96.0.10:53 rr的虚拟服务器,下面挂着 CoreDNS Pod IP 的真实服务器。ipvs 模式出问题时,先检查内核模块:

lsmod | grep ip_vs

如果节点没加载ip_vs相关模块,kube-proxy 的 ipvs 模式就会异常。另一个常见问题是 kube-proxy 的clusterCIDR配置和 apiserver 不一致,导致同步规则时计算错误。

4.3 单节点不通的常见原因:kube-proxy 失步、端口占用、iptables 冲突

在实际生产里,我更常遇到的是“部分节点不通”而不是“全集群不通”。如果 A 节点上的 Pod 解析正常,B 节点上的 Pod 解析超时,重点查 B 节点自身:

  • B 节点 kube-proxy Pod 是否 Running,kubectl get pods -n kube-system -o wide | grep kube-proxy。
  • B 节点和 A 节点的 iptables 规则是否一致,分别在两台节点上抓iptables -t nat -L -n | grep 10.96.0.10对比。
  • B 节点上有没有进程占用 53 端口:ss -lunp | grep :53。

iptables 冲突是很容易被忽略的点。有些云厂商的节点初始化脚本、安全组工具会在 nat 表里插入自定义链,如果自定义链正好命中了 DNS 流量并做 DROP/REJECT,请求就会在命中 KUBE-SERVICES 之前被丢掉。判断方法是看 PREROUTING 链顺序和计数器增长:

iptables -t nat -L PREROUTING -n --line-numbers

如果自定义链排在 KUBE-SERVICES 之前且计数器增长异常,那就是冲突,需要把自定义链调整到后面或者联系云厂商确认用途。

5. CoreDNS 自身状态排查:日志、Corefile、上游 forward 与 conntrack

5.1 日志里到底该看什么

如果前面的链路层都排查过了还是没结果,回头仔细看 CoreDNS 日志:

kubectl logs -n kube-system -l k8s-app=kube-dns --tail=200

常见日志和对应含义:

  • [ERROR] plugin/errors: 2 ... read udp ... i/o timeout:CoreDNS 向上游 forward 时超时,说明外部 DNS 链路有问题。
  • [ERROR] plugin/kubernetes: ...:kubernetes 插件连 apiserver 失败,或者查询失败。
  • [INFO] ...:正常查询记录,不用紧张。

有个细节很容易忽略:如果 CoreDNS 完全没有日志输出,不代表它没问题,反而说明请求可能根本没到它这一层。这时候要回到第 2 章的“从宿主机直连 Pod IP”验证,别在日志上死磕。

5.2 Corefile 配置逐行拆解:kubernetes 插件与 forward 的边界

默认的 Corefile 值得逐行看懂。一份常见的配置长这样:

.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . /etc/resolv.conf cache 30 loop reload loadbalance }

作用域是.,也就是所有域名都先经过这里处理。kubernetes cluster.local ...负责集群内域名,pods insecure允许查询 Pod IP 形式的记录,fallthrough表示查不到就继续往下走。forward . /etc/resolv.conf则把集群内查不到的域名转发给节点/etc/resolv.conf里的上游 DNS。

这段配置的边界很重要:集群内域名解析失败,问题聚焦在 kubernetes 插件或 apiserver;外部域名解析失败,问题聚焦在 forward 上游。如果外部域名解析失败,可以先确认节点/etc/resolv.conf是否可用,或者临时直接把 upstream 改为一个公共 DNS 快速验证问题是否在上游链路。正式环境不建议长期用公共 DNS,但用来做二分定位很有效。

5.3 集群内域名与外部域名分别验证,判断故障面

这个动作是我在排障时必做的,能把故障面缩小到很小的范围:

dig @10.96.0.10 kubernetes.default.svc.cluster.local +time=3 +tries=1 dig @10.96.0.10 www.baidu.com +time=3 +tries=1

组合判断:

  • 集群内正常、外部超时 → 问题在 forward 上游,和 CoreDNS 存活性无关。
  • 集群内超时、外部也超时 → 问题在 CoreDNS 本身或网络链路。
  • 集群内 NXDOMAIN → 检查 Service 名、namespace 是否写错。
  • 集群内 SERVFAIL → 看 CoreDNS 日志,通常指向 apiserver 或上游链路异常。

这种二分法排障效率很高,因为绝大多数情况下外部域名和集群内域名走的是两套完全不同的逻辑路径。

5.4 conntrack 表满与 OOM:两个容易忽略的性能坑

conntrack 表满在 DNS 超时故障里出现频率太高,值得单独拿出来说。现象是业务 Pod 里偶发性解析超时,宿主机dmesg输出里有:

nf_conntrack: table full, dropping packet

原因不复杂:DNS 是 UDP 短连接,Pod 数量多、查询频率高时,连接跟踪条目上涨很快,把表打满后新连接直接被丢包。处理命令:

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max sysctl -w net.netfilter.nf_conntrack_max=1048576

临时调大可以快速止血,持久化要写到/etc/sysctl.conf。但治本还要看是谁在产生大量连接,同时考虑 CoreDNS 的 cache 插件是否合理生效。

OOM 是另一个隐蔽问题。如果 CoreDNS Pod 的 RESTARTS 数字很大,而且 describe 里有 OOMKilled,就要调整 requests/limits。我见过有些环境把 CoreDNS 的 limits 压得很小,正常流量稍大就 OOM。给 CoreDNS 设置 limits 要基于实际查询量,别为了省资源把核心组件卡死。

6. 修复操作与验证闭环:两个真实场景的完整处置过程

6.1 场景一:conntrack 表满导致 nslookup 超时的完整处置

把这个流程串起来看一个真实场景。假设业务反馈接口超时,我进 Pod 执行 nslookup 显示connection timed out,kubectl 检查 Service、Endpoint、Pod 都正常,宿主机 dig @ClusterIP 也超时,直连 Pod IP 有时超时。这时登上 CoreDNS Pod 所在节点:

  1. dmesg | tail,看到nf_conntrack: table full, dropping packet。
  2. sysctl net.netfilter.nf_conntrack_max和sysctl net.netfilter.nf_conntrack_count,确认当前计数接近上限。
  3. 临时调大:sysctl -w net.netfilter.nf_conntrack_max=1048576,并写入持久化配置。
  4. 在评估影响后,清理到 kube-dns ClusterIP 的条目:conntrack -D -d 10.96.0.10。这个操作在繁忙的生产环境要谨慎,最好先conntrack -L -d 10.96.0.10 | head看看规模,并选择低峰期执行。
  5. 重新执行 nslookup 验证,并持续观察 dmesg 是否还有报错。

这类问题的难点不在修,而在定位。如果一开始没有按链路走,看到超时就去重启 CoreDNS,问题不会解决,反而把 conntrack 表越积越满。

6.2 场景二:CoreDNS 标签不匹配导致 kube-dns Endpoint 为空的修复

另一个场景:nslookup 超时,kubectl get endpoints kube-dns显示<none>,但 CoreDNS Pod 是 Running 且 READY。用第 3 章的方法对比,发现 Deployment 的 Pod 标签是k8s-app: coredns,而 Service selector 要求k8s-app: kube-dns。

修复方式:

  1. kubectl edit deployment coredns -n kube-system,修改spec.template.metadata.labels为k8s-app: kube-dns。这里要注意同步调整 Deployment 的 selector,避免滚动更新时报错。
  2. 保存后 Deployment 滚动更新。
  3. 观察 Endpoints:
kubectl get endpoints -n kube-system kube-dns
  1. 重新 dig 验证。

修复本身一分钟,但定位过程需要按链路走完。如果直接把 Service 的 selector 改成k8s-app: coredns,虽然也能让 Endpoint 恢复,但会偏离标准命名约定,给后续维护埋坑。我更建议改 Deployment 标签来适配 Service,因为这是官方默认的约定。

6.3 验证闭环:从 nslookup 到业务恢复的完整检查清单

修复之后别急着告诉业务“好了”,我每次都会按固定顺序做一遍闭环验证:

  • kubectl get pods -n kube-system | grep coredns,确认 CoreDNS Ready。
  • kubectl get endpoints -n kube-system kube-dns,确认有端点。
  • 宿主机dig @10.96.0.10 kubernetes.default.svc.cluster.local。
  • 业务 Pod 内nslookup kubernetes.default.svc.cluster.local。
  • 业务 Pod 内nslookup <外部域名>。
  • 让业务方发起真实请求,观察监控和日志。

这个清单看起来简单,但能避免“貌似恢复了、等业务方真正用时又炸”的尴尬。尤其是 DNS 这类基础设施,一个小问题可能只在特定 Pod、特定节点、特定流量下才暴露,只验证一条路径远远不够。

最后再说一点个人体会。我排查 CoreDNS 这类故障最大的感触是:一定要先分清故障面,再动手。很多人看到 nslookup 超时就去重启 CoreDNS,问题没解决,还把现场破坏了。我的习惯是永远先记录现象、再按链路逐层验证。链路通了,修复往往就一句话;链路没通,盲目操作只会让排障更难。遇到这类问题,先把这份流程跟着走一遍,至少能让你从“不知道从哪下手”变成“知道哪一环还没查”。下次再遇到 CoreDNS 解析失败,不要慌,按这个链路来,稳得很。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询