一、先搞清楚发生了什么

想象一下这个场景:你正在公司忙着发布新版本,突然群里炸了锅,说线上服务互相调不通了。你赶紧登录到服务器上,执行 kubectl get pods,发现 CoreDNS 的 Pod 正在疯狂重启,一会儿 Running,一会儿 CrashLoopBackOff。这个时候,你心里肯定咯噔一下,因为 CoreDNS 是整个集群的“域名解析中枢”,它一出事,所有服务之间的通信都会跟着瘫痪。

但请注意一个关键点:CoreDNS 频繁重启,不一定就是 CoreDNS 自己的锅。很多时候,它只是受害者。因为 CoreDNS 要正常工作,依赖底层Pod网络能通、kubelet 能健康检查、节点能转发流量等等。如果这些底层环节断了,CoreDNS 就会像被掐住脖子的水管工,拼命想工作却干不动,最后被系统判“不健康”,反复重启。

所以,当遇到这种问题时,咱们不能只盯着 CoreDNS 本身,而是要从上到下,一层一层地剥开,找到真正的“断裂点”。

二、从集群最外层开始诊断

2.1 先看全局状态

在动手之前,先给整个集群拍个 X 光片。执行下面这些命令,确认故障的范围有多大。


# 查看所有节点是否Ready,如果有节点NotReady,后面一切免谈
kubectl get nodes

# 查看CoreDNS所在命名空间的Pod状态和重启次数
kubectl -n kube-system get pods -l k8s-app=kube-dns

# 查看具体CoreDNS Pod的详细事件,比如为什么被杀死
kubectl -n kube-system describe pod <coredns-pod-name>

这里用一个实际例子来说明:假设你发现一个 CoreDNS Pod 的 RESTARTS 列显示 25 次,那你就得去看它的上次退出原因。执行 describe 后,在输出底部会有一段类似这样的内容:

Events:
  Warning  Unhealthy  3m  kubelet  Readiness probe failed: HTTP probe failed with statuscode: 503
  Warning  BackOff    1m  kubelet  Back-off restarting failed container

看到没?kubelet 说“就绪检查失败了”。问题来了:CoreDNS 的容器本身可能没崩溃,只是它对外提供的 DNS 服务没法正常响应。那为什么没法响应?这就得钻进 Pod 的内部去看。

三、钻进Pod内部查容器网络

3.1 进入Pod的视角

咱们不能只看 Pod 在“外面”的状态,得“进去”看看它内部的网络状况。CoreDNS 启动后会监听 53 端口(TCP/UDP),但容器里到底能不能正常监听?能不能收到包?咱们用 kubectl exec 进去做个检查。


# 进入CoreDNS容器内部执行命令
kubectl -n kube-system exec -it <coredns-pod-name> -- /bin/sh

# 在容器内查看监听端口
netstat -tuln | grep 53

# 如果netstat没有,就改用ss(取决于镜像基础)
ss -tuln | grep 53

正常情况下,你会看到类似这样的输出:

tcp   LISTEN  0       128             0.0.0.0:53            0.0.0.0:*
udp   UNCONN  0       0               0.0.0.0:53            0.0.0.0:*

如果你发现端口根本没在监听,说明 CoreDNS 进程自己出了问题,比如配置文件写错导致启动失败。那咱们就要去检查它的 Corefile 配置了。

3.2 检查Corefile是否正常

CoreDNS 的配置是一个叫 Corefile 的东西,它定义了 DNS 解析的规则。如果配置里有语法错误,CoreDNS 会直接拒绝启动。你可以执行:


# 查看CoreDNS当前生效的配置
kubectl -n kube-system get configmap coredns -o yaml

一个典型的 Corefile 长这样:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        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
    }

注意这一段配置里,forward . /etc/resolv.conf 的意思是:集群里解析不了的域名,转发到节点的 /etc/resolv.conf 里配置的上游 DNS。如果这个上游 DNS 不可达,那 CoreDNS 虽然能启动,但对外解析会超时,健康检查照样失败。

所以,咱们不仅要看 CoreDNS 配置,还要看它容器内的 /etc/resolv.conf 是不是有问题。不过,更常见的情况是,CoreDNS 自身没问题,问题是它所在的节点网络或 Pod 网络不通。那怎么判断呢?

四、验证Pod网络连通性

4.1 检查Pod的IP和节点关系

先找到 CoreDNS Pod 跑在哪个节点上,以及它的 IP 是什么。


# 查看CoreDNS Pod的IP、节点等信息
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide

假设输出显示:

NAME                       READY   STATUS    RESTARTS   AGE   IP           NODE
coredns-7d8b6c5d9-abc12   1/1     Running   0          10m   10.244.5.12   node-2

现在,咱们要判断这个 Pod 的 IP 是不是真的“能用”。怎么判断?从其他节点上的普通 Pod 去 ping 它,或者用 curl 访问它的健康检查端口。但是,如果集群里所有 Pod 之间的网络都断了,那光靠 Pod 之间测也测不明白。

所以,这里教大家一个更底层的办法:直接检查节点上的路由和 iptables 规则,看看数据包是怎么从节点转发到 Pod 的。

4.2 检查节点上的veth网卡

Kubernetes 里每个 Pod 都会有一对虚拟网卡,一头在 Pod 内部叫 eth0,另一头在节点上叫 vethxxxxx。如果这对网卡断了,那 Pod 就彻底失联了。

你可以 SSH 登录到 CoreDNS Pod 所在的节点(也就是 node-2),执行:


# 查看节点上所有veth网卡,找到对应Pod IP的(需要结合路由表判断)
ip route | grep 10.244.5.12

# 如果路由存在,继续查看网卡状态
ip link show <veth对应的名字>

正常情况下,你应该看到网卡状态是 UP,并且有流量统计。如果你看到的网卡状态是 DOWN 或者根本找不到对应的 veth,那问题就出在节点网络层面。这时候,就需要检查是不是网络插件(比如 Calico、Flannel)出了问题。

五、深入网络插件内部

5.1 检查Calico/Flannel的组件状态

目前国内用得最多的 CNI 插件就是 Calico 和 Flannel。不管是哪一个,它们都有对应的守护进程跑在节点上。如果这些插件出了问题,Pod 网络就会“断线”。

对于 Calico,你可以这样看:


# 查看Calico相关Pod状态
kubectl -n kube-system get pods -l k8s-app=calico-node

# 查看Calico的Felix日志,重点关注报错
kubectl -n kube-system logs <calico-node-pod> -c calico-node | tail -100

对于 Flannel,则是:


# 查看Flannel DaemonSet
kubectl -n kube-system get pods -l app=flannel

# 查看Flannel日志
kubectl -n kube-system logs <flannel-pod> | tail -100

常见的报错有这么几种:

  • Error: failed to create subnet for pod —— 说明节点上的 IP 池被用光了。
  • Network unreachable —— 说明节点本身与外网或管理网络都不同。
  • Failed to write to socket —— 说明 Flannel 的后端(比如 UDP、VXLAN)连接不上。

5.2 一个真实示例:IP池耗尽导致CoreDNS重启

假设你发现 Calico 日志里有这么一段:

2025-04-10 14:22:31.123 WARN  Felix: IP reservation pool exhausted, cannot assign an IP to pod

这就是很典型的问题:集群里的可用 IP 不够了。每个节点分配一个网段,网段里能用的 Pod IP 是有限的。如果你的集群有大量 Pod 被频繁创建销毁,IP 可能被占用后不能及时释放,最终导致 CoreDNS 新建的 Pod 拿不到 IP,从而启动失败,重启循环。

这时候,你需要去查 IP 池的使用情况。在 Calico 中,可以通过其 API 或命令行工具查看。


# 用calicoctl查看IP池
calicoctl ipam show

# 或者直接用kubectl查看Calico的IPAM配置
kubectl -n kube-system get ipamhandles --all-namespaces

找到问题后,解决办法一般是:清理无用 Pod、扩大节点网段、调整 IP 回收策略等等。不过这不是本文重点,咱们还是先看另一个常见断裂点:主机防火墙。

六、别忽略了节点防火墙和iptables

有时候,Pod 网络和节点网络都通,但 CoreDNS 的包就是过不去。这就是典型的“路由通,但防火墙过滤了”。很多生产集群里,管理员会在节点上开启 iptables 或 firewalld,而 Kubernetes 的 Service 和 Pod 网络都有特定的端口规则。

对于 CoreDNS,它要监听 53 端口,同时还要向 Kubernetes API Server 发起请求,所以需要保证 6443 端口(或你自定义的 API Server 端口)能从节点访问。如果被防火墙拦截,CoreDNS 就没办法刷新 Service 的映射关系,进而产生各种解析异常。

检查命令如下:


# 在节点上查看iptables规则中是否丢弃或拒绝了某些流量
iptables -L INPUT -n --line-numbers | head -50

# 或者查看nat表,确认KUBE-SERVICE链没被破坏
iptables -t nat -L KUBE-SERVICES -n

如果发现某些链规则缺失或顺序错误,可以用 kube-proxy 做一次清理和重新生成。最简单的方法是重启 kube-proxy 这个 DaemonSet:


# 重建kube-proxy Pod(如果是DaemonSet)
kubectl -n kube-system rollout restart ds kube-proxy

但请注意,重启 kube-proxy 不会自动修复破坏的 iptables 规则,它只会重新同步。有些情况下,你需要手动刷新规则,比如执行 systemctl restart kube-proxy 或者干脆重启节点。重启节点是“最后大招”,不到万不得已不要用。

七、还有一个隐秘断裂点:回环DNS配置

7.1 鸡生蛋问题

在 Kubernetes 里,普通 Pod 的 /etc/resolv.conf 会指向 ClusterIP 的 DNS 服务,比如 10.96.0.10。但 CoreDNS 自己也是一个 Pod,它的 resolv.conf 指向的是宿主机的 DNS。如果宿主机 DNS 配置有误,比如写了 nameserver 127.0.0.1,而宿主机的 53 端口又没开,那 CoreDNS 转发上游查询时就注定失败。

更头疼的是,如果 CoreDNS 的 Corefile 里把 forward . 指到了本机的某个服务,而那个服务又依赖 CoreDNS 解析自己,那就形成了死循环。比如,你把宿主机 /etc/resolv.conf 里的 nameserver 写成了 127.0.0.1,但宿主机的 53 端口根本没有监听,那 CoreDNS 一启动就会疯狂重试,最终健康检查失败,被强制重启。

7.2 如何验证这个断裂点

你可以进入 CoreDNS 容器,手动做一次 DNS 查询,同时开启调试日志:


# 进入CoreDNS容器
kubectl -n kube-system exec -it <coredns-pod-name> -- /bin/sh

# 用dig命令测试(CoreDNS镜像自带dig)
dig @127.0.0.1 kubernetes.default.svc.cluster.local

# 查看返回的解析结果,如果超时或失败,说明上游配置有问题

然后,看看节点上的 /etc/resolv.conf 内容:


# 在节点上执行
cat /etc/resolv.conf

假设你的节点是systemd-resolved管理的,它的 resolv.conf 往往是一个软链接到 127.0.0.53。对 CoreDNS 来说,访问 127.0.0.53 通常没问题,但如果你用的是某些云平台的私有 DNS,并且网络策略禁止了节点访问该 DNS,那就出事了。这时候,最保险的做法是让 CoreDNS 的 forward 直接指向一个公网 DNS,比如 8.8.8.8223.5.5.5(阿里 DNS)。但这样做要小心,集群内部域名解析不能转发给公网,否则会泄漏内部信息。

八、实战演练:逐层定位并修复

8.1 整体诊断流程

下面我给出一个完整的手动排查流程,按照这个步骤走,90% 的问题都能定位到。


# 第1步 查看节点状态
kubectl get nodes

# 第2步 查看CoreDNS Pod状态
kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide

# 第3步 查看CoreDNS日志(看有没有解析失败记录)
kubectl -n kube-system logs <coredns-pod-name> --tail=50

# 第4步 查看集群Events
kubectl -n kube-system get events --sort-by=.lastTimestamp | tail -20

# 第5步 进入CoreDNS容器测试本地监听
kubectl -n kube-system exec -it <coredns-pod-name> -- ss -tuln | grep 53

# 第6步 在CoreDNS容器内测试外部DNS(比如内网某个DNS)
kubectl -n kube-system exec -it <coredns-pod-name> -- nslookup www.baidu.com

# 第7步 在普通业务Pod内测试CoreDNS服务
kubectl -n <your-namespace> exec -it <your-pod> -- nslookup kubernetes.default.svc.cluster.local

8.2 精确定位示例:从二层到七层

为了让你有直观感觉,我模拟一个真实的“断裂点”排查过程。

假设你的集群里,CoreDNS Pod 正在重启,你用 kubectl get events 看到一条关键信息:

Readiness probe failed: HTTP probe failed with statuscode: 503

这说明 CoreDNS 的 /ready 接口返回 503。这个接口是检查 CoreDNS 是否已经能够正常提供服务。它返回 503,最常见的原因是 CoreDNS 内部的网络插件(比如 kubernetes 插件)无法连接 API Server。

咱们继续看 CoreDNS 日志:

kubectl -n kube-system logs <coredns-pod-name> --tail=10

日志片段:

[ERROR] plugin/kubernetes: failed to list services: Get "https://10.96.0.1:443/api/v1/namespaces/default/services?limit=0&resourceVersion=0": dial tcp 10.96.0.1:443: i/o timeout

看到没?CoreDNS 尝试访问 10.96.0.1 的 443 端口超时了。10.96.0.1 是 Kubernetes Service 的 “ClusterIP”,它指向 API Server。也就是说,CoreDNS 跟 API Server 之间的网络断了。

为什么会断?我们可以顺着这个线索继续查:


# 在CoreDNS容器内检查到API Server的连通性
kubectl -n kube-system exec -it <coredns-pod-name> -- curl -k https://10.96.0.1:443/healthz

如果这条命令超时,说明 CoreDNS 容器到 ClusterIP 的包被丢弃了。而 ClusterIP 的转发规则是由 kube-proxy 和节点上的 iptables 实现的。这时候,你要去检查 kube-proxy Pod 的日志:


# 查看kube-proxy日志中是否有关于Service同步的错误
kubectl -n kube-system logs -l k8s-app=kube-proxy | tail -100

如果 kube-proxy 日志里出现类似 Failed to ensure that the node's iptables rules are updated 或者 Error syncing proxy rules,那就是 iptables 坏了。你可以尝试重建整个规则链:


# 重启kube-proxy,让它重新生成iptables规则
kubectl -n kube-system rollout restart daemonset kube-proxy

有时候重启完就好了。但如果还不能解决,那就得手动清除残留的 iptables 规则。

8.3 手把手修复示例

假设你的集群用的 CNI 是 Flannel。Flannel 会在每台节点上创建一个 flannel.1 网关网卡,并且往节点的路由表里添加一条 10.244.0.0/16 via flannel.1 的路由。如果你发现这条路由没了,或者 flannel.1 网卡不见了,那就是 flannel 出了问题。

修复办法,一种是重启 flannel Pod:


# 强制删除flannel Pod,让DaemonSet重新拉起
kubectl -n kube-system delete pod -l app=flannel

另一种是手动恢复路由(不推荐,作为应急):


# 手动添加路由(注意示例,实际要按你的集群网段来)
ip route add 10.244.0.0/16 dev flannel.1

不过手动加路由过于麻烦,还是建议从源头排查。

九、应用场景与优缺点分析

9.1 应用场景

上面这套排查思路,适用于以下场景:

  • 集群内突然大规模出现 Pod 间通信超时
  • CoreDNS 或无状态服务频繁重启
  • 新部署的 Pod 无法解析 Service 名
  • 修改了 CNI 或网络策略后出现故障
  • 节点重启后 Pod 状态异常

9.2 技术优缺点

建议使用的方法优点:

  • 从上到下逐层筛选,不会漏细节
  • 每个步骤都有明确命令,方便录屏记录和团队协作
  • 不需要额外安装工具,kubectl 和 Linux 基础命令就够了

缺点:

  • 如果故障原因不在 OSI 模型的前三层,这招就不够用了
  • 排查过程耗时较长,尤其是当节点数量很多时
  • 需要懂一定的 Linux 网络基础,比如路由和 iptables

9.3 注意事项

  • 不要一上来就重启节点,那是“核武器”,会扩大故障范围
  • 改 iptables 之前先备份(用 iptables-save
  • 不要把 CoreDNS 的副本数降到 1,否则在排查中没得用
  • 遇到问题时,先看事件(events),90% 的线索都在里面
  • 每次测试完一条链路,做下记录,避免重复操作

十、关联技术:CoreDNS 的插件机制

因为咱们聊的是 CoreDNS,我顺便给你科普一下它的核心插件。CoreDNS 本身是一个“空壳”服务器,所有功能都靠插件来提供。正是这些插件组合,决定了它的解析行为。

插件名 作用 典型配置示例
kubernetes 对接 K8s API,将 Service 名解析为 ClusterIP kubernetes cluster.local
hosts /etc/hosts 读取静态记录 hosts /etc/coredns/Hosts
forward 把未解析的请求转发给上游 DNS forward . 8.8.8.8
cache 缓存查询结果,提升性能 cache 30
reedy 提供健康检查接口 /ready ready
loop 检测 DNS 解析死循环 loop

举个例子,你要让 CoreDNS 直接解析内部服务器的 myapp.internal 这个域名,就可以在 Corefile 里加一段:


# 假设我们要解析的域名后缀是internal
internal:53 {
    hosts {
        # 定义静态主机记录
        192.168.1.10 myapp.internal
        # 继续查找/etc/hosts
        fallthrough
    }
    # 记录解析日志
    log
    # 如果没命中,返回空
    errors
}

这算是很典型的一个配置扩展,可以帮助你解决“内部短域名”的解析问题,而不需要每次都依赖外部 DNS。

十一、从根上预防问题

与其每次故障再救火,不如想想怎么避免。这里给你几条实操建议:

  1. 给 CoreDNS 设置合理的资源限制和重试策略,防止它被节点上的资源争抢拖垮。
  2. 把 CoreDNS 的副本数设置为 2 个以上,并且用反亲和性让它们分散在不同节点上。
  3. 经常检查集群的 IP 池使用率,别等到满了才处理。
  4. 在节点上配置好监控,对 veth 网卡的错误包、丢包率做告警。
  5. 不要随意修改 kube-proxy 和 CNI 生成的 iptables 规则,除非你完全清楚自己在干什么。

十二、文章总结

遇到 CoreDNS 频繁重启和域名解析失效,别慌。你要做的不是马上重启 Pod,而是沿着一条清晰的路径去排查:先看节点,再看 Pod 状态,然后钻进容器内部检查监听端口,接着验证 Pod 网络连通性,再检查 CNI 组件和 kube-proxy 规则,最后检查 DNS 配置本身。每一步都有对应的命令和日志。

真正的“断裂点”往往不在 CoreDNS 本身,而在它背后的网络链路里。比如 IP 池耗尽、veth 网卡故障、iptables 规则损坏、上游 DNS 不可达,甚至回环配置问题,都可能成为压垮 CoreDNS 的最后一根稻草。

在真实生产环境中,所有服务和组件都是互相依赖的,牵一发而动全身。希望这篇博客能帮你建立一套系统的排查思路,以后遇到类似问题,你自己就能一步步找到根因,彻底解决,而不是靠重启大法苟且偷生。