一、先搞清楚发生了什么
想象一下这个场景:你正在公司忙着发布新版本,突然群里炸了锅,说线上服务互相调不通了。你赶紧登录到服务器上,执行 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.8 或 223.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。
十一、从根上预防问题
与其每次故障再救火,不如想想怎么避免。这里给你几条实操建议:
- 给 CoreDNS 设置合理的资源限制和重试策略,防止它被节点上的资源争抢拖垮。
- 把 CoreDNS 的副本数设置为 2 个以上,并且用反亲和性让它们分散在不同节点上。
- 经常检查集群的 IP 池使用率,别等到满了才处理。
- 在节点上配置好监控,对
veth网卡的错误包、丢包率做告警。 - 不要随意修改 kube-proxy 和 CNI 生成的 iptables 规则,除非你完全清楚自己在干什么。
十二、文章总结
遇到 CoreDNS 频繁重启和域名解析失效,别慌。你要做的不是马上重启 Pod,而是沿着一条清晰的路径去排查:先看节点,再看 Pod 状态,然后钻进容器内部检查监听端口,接着验证 Pod 网络连通性,再检查 CNI 组件和 kube-proxy 规则,最后检查 DNS 配置本身。每一步都有对应的命令和日志。
真正的“断裂点”往往不在 CoreDNS 本身,而在它背后的网络链路里。比如 IP 池耗尽、veth 网卡故障、iptables 规则损坏、上游 DNS 不可达,甚至回环配置问题,都可能成为压垮 CoreDNS 的最后一根稻草。
在真实生产环境中,所有服务和组件都是互相依赖的,牵一发而动全身。希望这篇博客能帮你建立一套系统的排查思路,以后遇到类似问题,你自己就能一步步找到根因,彻底解决,而不是靠重启大法苟且偷生。
评论
围绕“Kubernetes集群内CoreDNS频繁重启,服务发现失效时如何定位Pod网络与域名解析栈断裂点”参与讨论