☰
K8s中Pod无法解析域名?从CoreDNS到DNS策略的完整排障指南
2026/10/16 8:53:59 网站建设 项目流程

1. 先把现象拆清楚:“不能ping通域名”到底卡在哪一层

线上K8s集群最容易碰到的一类怪问题,就是Pod里ping某台外部服务器或某个内部服务的域名不通,但是直接ping对应的IP却是通的。很多同学第一反应是“CoreDNS挂了吧”,然后开始重启CoreDNS Pod。说实话,我刚开始也这么干,后来发现这样既解决不了问题,还容易把现场搞丢。正确做法是先冷静下来,把“ping域名不通”这个现象拆开,弄清楚它到底是DNS解析失败,还是ICMP网络路径不通,又或者是目标服务本身就不允许ping。

先说一个基本链路:当你在一个Pod里执行ping www.example.com时,命令首先会把域名交给C库的解析器,也就是读取Pod里的/etc/resolv.conf,向里面配置的nameserver发起DNS查询,拿到A记录以后再发起ICMP请求。所以“ping域名不通”其实包含了两个环节:域名到IP的解析,以及IP到目标的网络连通性。如果域名能解析出IP、但ping不通IP,那是路由或安全组的问题,跟CoreDNS关系不大;如果连解析都失败,比如报unknown host或Temporary failure in name resolution,那才需要把矛头指向集群内的DNS链路。

正因为这个原因,我建议进入Pod后先做两个最基础的动作:第一步,直接ping一个已知的内网IP,确认从Pod出发的网络数据面是通的;第二步,用nslookup或dig查询域名,确认解析是否正常。光靠肉眼判断“ping不通域名”就认定是CoreDNS故障,很容易被表面现象带偏。实际情况中,有相当比例的“ping域名不通”其实是目标域名原本就没有公网解析记录,或者集群里的NetworkPolicy把访问外网的流量拦了,CoreDNS本身并没有问题。把现象拆细,后面排查才会少走弯路。

1.1 “解析失败”和“网络不通”是完全不同的两回事

这里要特别强调“解析失败”和“网络不通”的差异。在K8s集群里,这个差异直接决定了你要去看CoreDNS,还是去看CNI插件的路由。我们可以用一个小实验来帮助理解:进入一个运行中的Pod,执行ping 10.96.0.10,这里的10.96.0.10是CoreDNS Service的ClusterIP。如果这个IP能通,说明Pod到集群Service网络的转发是正常的,至少数据面的承载没问题。再执行nslookup kubernetes.default.svc.cluster.local,如果能解析出ClusterIP,那起码说明Kubernetes集群内部的DNS链路是通的。

但如果反过来,ping ClusterIP也不通,问题可能出现在CNI插件、kube-proxy或者Pod网络命名空间上,这时候再去查CoreDNS就是南辕北辙。还有一个常见现象:在Pod里ping域名时,命令长时间卡住不动,最后报ping: sendmsg: Network is unreachable。这个错误更像Pod没有默认路由,或者CNI网关不可达,和DNS解析失败的表现完全不同。DNS解析失败通常报的是unknown host或者在应用日志里看到Could not resolve host: xxx。

所以我的经验是,遇到这类问题时不要急着去翻CoreDNS日志,先手动分成两条线:一条叫“数据面连通性”,负责确认Pod到各网段的IP通信是否正常;另一条叫“控制面解析”,负责确认DNS查询是否被正确处理。两条线各自定位,再汇合到一起判断。如果数据面没问题,但控制面解析异常,再把注意力放到CoreDNS和上游DNS的转发关系上。

1.2 常见的“假性DNS故障”有哪些

还有一种容易误导人的情况,它被我叫作“假性DNS故障”:Pod里解析域名偶尔变慢、或者只在特定时间段失败,你以为CoreDNS出问题了,实际上问题出在/etc/resolv.conf里的search域和ndots选项上。举个例子,集群默认的resolv.conf一般长这样:

nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5

这段配置的意思是:当你在Pod里访问一个不带点的短域名,比如mydb,解析器会尝试依次组合mydb.default.svc.cluster.local、mydb.svc.cluster.local、mydb.cluster.local等域名,最后才尝试直接查询mydb。其中ndots:5代表只要域名里的点号数量小于5,就会先把域名作为集群内部搜索域的一部分去尝试解析。这会导致一个很典型的表象:查询外部短域名时,CoreDNS收到大量带着search后缀的DNS请求,如果CoreDNS的上游转发处理超时,应用侧就会感觉“卡了好几次才成功”,看起来像是解析失败。真实原因是配置里的ndots太高,并不是CoreDNS不可用。

另一个“假性故障”是Pod镜像里的工具链太精简。比如某些基于distroless或精简busybox的镜像,连nslookup、dig、ping都可能没有,或者ping命令的行为和普通机器不一样。你进入Pod以后执行ping,它报bad address 'www.example.com',以为解析失败,其实只是这个精简版ping命令不依赖系统解析器。所以排查前先确认Pod里有哪些排查工具,没有的话就用kubectl run临时起一个带完整工具集的调试Pod,或者用kubectl debug复制共享进程命名空间。这些准备工作看着笨,却能省下大量时间。

2. 理解CoreDNS的工作方式才能快速定位问题

不少运维同学把CoreDNS看作一个黑盒:Pod/etc/resolv.conf里写了它的ClusterIP,解析不了就觉得是它挂了。但黑盒心态会让你在排查时无从下手。CoreDNS本质上是一个插件化的DNS服务器,它本身并不存储域名信息,也不具备递归解析能力,而是通过一系列插件来组合成一条完整的处理链。K8s集群内所有Pod的域名解析请求都会先落到CoreDNS,它再根据配置决定是直接回答集群内Service域名,还是转发给上游DNS服务器。理解了这条链,排查时就能按环节逐个验证。

CoreDNS在集群里的典型数据流是这样的:Pod里的应用发起DNS查询,请求通过ClusterIP转发到某一个CoreDNS Pod;CoreDNS先看查询域名是否匹配kubernetes插件的管辖范围,比如*.cluster.local,匹配的话就直接从K8s API里查Service或Endpoints信息返回;如果不匹配,就由forward插件转发给上游DNS服务器,等上游返回结果后再回给Pod。这条链上任何一环出问题,表现为Pod里的解析不通,但真正的病灶可能相差很远。

2.1 CoreDNS在集群中到底扮演什么角色

CoreDNS在K8s里最核心的职责,是把集群内部域名解析出来。你通过my-service.namespace.svc.cluster.local访问另一个服务,这个域名并没有在任何公网DNS服务器上存在,只有CoreDNS能通过Kubernetes API实时获取Service和Pod列表,并返回对应的ClusterIP或PodIP。这也是为什么它必须能稳定访问K8s API Server,并且依赖定期同步的对象数据。如果CoreDNS所在节点和API Server之间有网络抖动,或者RBAC权限被改坏,节点上的CoreDNS Pod可能出现“能起但查不了集群域名”的诡异现象。

除了集群内部域名,CoreDNS还要负责把外部域名转发给上游。默认配置里的forward . /etc/resolv.conf,意思是非集群域名的所有请求都转发给某个上游DNS。如果上游DNS是部署在宿主机上的systemd-resolved,它监听的可能是一个像127.0.0.53这样的本地地址。这里就埋了一个经典坑:CoreDNS Pod运行在容器网络里,127.0.0.53指向的是CoreDNS容器自身的loopback,而不是宿主机,所以转发出去必然失败。很多刚接触K8s的同学会遇到Pod里解析任何外部域名都失败、但集群内域名正常的现象,根源往往就在这个上游地址配置上。

所以CoreDNS的角色更像“代理”:对内部域名它直接回答,对外部域名它要找到一条能走的转发通道。这两类职责对网络环境的要求完全不同。内部域名要求CoreDNS能访问API Server,外部域名要求CoreDNS能访问上游DNS服务器,而且上游DNS服务器必须在CoreDNS的容器网络内可达。排查询题时,我们要先确认出问题的域名究竟是内部域名还是外部域名,然后分别检查对应的链路。内部域名挂了优先怀疑CoreDNS和API Server的通信,外部域名挂了优先怀疑forward目标地址和网络策略。

2.2 Corefile插件链的关键配置项

CoreDNS一切行为都体现在Corefile里,默认安装在kube-system命名空间下名为coredns的ConfigMap中。一个典型的Corefile大概长这样:

.:53 { errors health ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } forward . 10.10.10.53 { max_concurrent 1000 } cache 30 loop reload loadbalance }

这里每个插件都有讲究。errors负责把错误日志输出到标准输出,排查时必备;health会在http://:8080/health提供健康检查端点,这是Pod存活探针的基础;ready用于就绪检查,当CoreDNS完成插件初始化后才返回就绪;kubernetes插件负责监听集群Service和Pod变化,处理cluster.local相关域名,并处理反向解析的in-addr.arpa和ip6.arpa区域;forward负责把非集群域名转发给指定的上游DNS;cache给解析结果加一个TTL缓存;loop用来检查配置里是否存在解析环路;reload允许ConfigMap变更后自动加载新配置,但触发时会有短暂抖动。

forward插件里有个容易被忽略的参数max_concurrent。在大规模集群中,如果同时有大量Pod发起外部域名解析,转发请求并发过高,CoreDNS会排队,表现为解析延迟突然飙升。默认情况下max_concurrent没有限制,这意味着突发流量可能把CoreDNS的goroutine打满,甚至导致OOM。建议在Corefile里显式设置一个合理的并发上限,比如1000或500,并且给CoreDNS配置limits,避免它把节点内存吃光。设置上限后,超出部分的请求会立刻返回失败,虽然从应用角度看还是解析失败,但至少可以把故障范围控制在CoreDNS自身,而不是拖垮整个节点。

cache参数也很关键。默认是cache 30,表示对缓存结果保留30秒。如果你频繁变更某个外部域名的解析记录,CoreDNS可能在缓存周期内一直返回旧IP。某些运维同事排查时发现“明明上游已经改了,Pod里却一直解析到旧IP”,第一反应是CoreDNS配置错了,查了半天才发现是缓存策略。对于集群内部域名,kubernetes插件自身有一套缓存机制,可以通过参数调整。实战中如果要验证配置变更,可以在测试Pod里用dig +ttlid观察TTL,也可以临时把Cache关掉再查。总之,不要把影响面都归结到CoreDNS“宕机”,很多解析异常其实是插件参数造成的“软故障”。

2.3 Pod侧DNS参数:dnsPolicy、dnsConfig和ndots

除了CoreDNS自身配置,K8s还给了每个Pod几个控制DNS行为的参数,它们同样能制造“解析不正常”的假象。dnsPolicy决定Pod的resolv.conf怎么生成,默认值是ClusterFirst,意思是集群内域名走CoreDNS,外部域名也通过CoreDNS代理出去。与之相对的还有Default,让Pod直接继承所在节点的resolv.conf;None,完全忽略集群默认配置;以及ClusterFirstWithHostNet,用于hostNetwork类型的Pod。

如果Pod的dnsPolicy被设置成Default,那么Pod内部看到的nameserver就是宿主机上的DNS配置,比如127.0.0.53或者云厂商内网DNS。这种情况下,解析集群内部域名kubernetes.default.svc.cluster.local多半是失败的,因为宿主机DNS并不认识cluster.local这个私有域。还有不少同学会在Pod上通过dnsConfig手动指定nameserver,一旦填错,例如漏了/etc/resolv.conf里的search域,解析同样会失败。K8s允许dnsConfig和dnsPolicy组合使用,Pod的resolv.conf会默认带上集群的search域和ndots。修改时要格外小心,不要在YAML里随便覆盖整个resolv.conf。

ndots这个参数我在前文已经提过,它是排查“外部域名偶尔解析超时”的高频元凶。默认ndots:5对K8s内部服务是友好的,因为Service名通常带很多点,比如my-service.namespace.svc.cluster.local。但对于访问外部域名,尤其是不带点的短域名,解析器会先尝试把域名拼接上集群search域,例如www.baidu.com.default.svc.cluster.local。CoreDNS对这类查询会先查找本地集群域名,找不到再转发给上游,上游也会因为不存在返回NXDOMAIN。虽然ndots不会直接让最终解析失败,但每次外部域名解析都要多产生几次无效查询,在高并发场景下会放大CoreDNS的负载,也容易让人误判为网络问题。如果你的业务Pods大量访问短域名,可以考虑调整ndots为2,或者把search域精简到最小集合,但必须评估对集群内Service访问的影响。

3. 完整排查与修复实操:从Pod内到CoreDNS一层层往下挖

下面这套排查流程,是我在线上真实排障时沉淀下来的,几乎每次都能定位到根因。整体思路是从现象侧开始,先确认问题范围,再逐层向内检查。整个过程不需要重启任何组件,纯粹靠命令和日志就能把病灶缩小到某个具体环节,这也是排障的基础素养:先别破坏现场,能只读就只读,能临时验证就临时验证。

3.1 第一步:进入Pod内抓取第一手配置和解析结果

排查第一步,找一个正在报障的业务Pod,执行kubectl exec进入容器,先看/etc/resolv.conf内容。这个文件决定Pod内所有进程把DNS请求发给谁。我通常用下面几个命令:

kubectl exec -it <pod-name> -n <namespace> -- cat /etc/resolv.conf kubectl exec -it <pod-name> -n <namespace> -- nslookup kubernetes.default.svc.cluster.local kubectl exec -it <pod-name> -n <namespace> -- nslookup <故障域名>

如果Pod里没有nslookup,可以临时创建一个调试Pod,挂到同一个命名空间,并指定使用相同的网络命名空间。K8s提供了kubectl debug,对于临时排查很实用。比如:

kubectl -n <namespace> debug -it <pod-name> --image=busybox --target=<container-name>

这样得到的调试Pod与目标容器共享网络命名空间,直接就能看到目标Pod的resolv.conf,还能使用完整工具集。

拿到resolv.conf后,第一件事就是看nameserver是不是CoreDNS的ClusterIP。默认的ClusterIP一般是10.96.0.10或10.32.0.2,具体取决于安装K8s时的--cluster-dns参数。如果你看到的nameserver不是预期地址,说明这个Pod的dnsPolicy或dnsConfig被改过,要返回去看工作负载的YAML配置。如果nameserver正确,再用nslookup分别做两组测试:一组解析集群内部域名,一组解析故障域名。通过对比,可以快速判断问题出在集群域名解析链路,还是外部域名转发链路。

3.2 第二步:检查CoreDNS服务和Endpoints是否正常

从Pod里看到的nameserver只是一个ClusterIP,真正处理请求的是后端的CoreDNS Pod。如果Service没有关联到任何Pod,请求就会被丢弃,表现就是Pod里解析超时。所以第二步要检查Service和Endpoints:

kubectl get svc -n kube-system kube-dns kubectl get endpoints -n kube-system kube-dns kubectl get pod -n kube-system -o wide | grep -E 'coredns|kube-dns'

注意,虽然实际部署的是CoreDNS,但Service在历史版本里一直叫kube-dns,这个历史包袱导致不少新同学找错名字。只要看到Service存在,并且Endpoints列表里有CoreDNS Pod的IP,就说明Service层面的代理关系是完整的。

接着检查CoreDNS Pod的运行状态。如果Pod处于CrashLoopBackOff或Pending状态,问题就清楚了一大半,朝着对应方向排查即可。如果Pod运行正常,再看Pod的资源使用情况。我用下面命令观察:

kubectl top pod -n kube-system | grep coredns

如果CPU使用率长期接近上限,或者内存使用率超过limits一半以上,说明CoreDNS本身已经到了高负载状态。此时出现的解析失败很可能是资源瓶颈导致,而不是配置错误。有同学在CoreDNS高负载时选择重启Pod,重启之后可能短暂恢复,但很快又复现同款问题。正确做法是针对负载做扩容,或者优化Corefile里的cache和forward并发参数。

3.3 第三步:绕过ClusterIP直接打到CoreDNS Pod上验证

这一步是为了区分“Service转发问题”和“CoreDNS自身问题”。Service转发涉及kube-proxy和conntrack,偶尔会因为DNAT表项问题导致解析失败,但直接访问Pod IP却正常。验证方式很简单:先拿到一个CoreDNS Pod的IP,然后进入业务Pod,用这个IP再发起一次解析请求。

kubectl get pod -n kube-system -o wide | grep coredns kubectl exec -it <pod-name> -n <namespace> -- nslookup <故障域名> <coredns-pod-ip>

如果直接指定Pod IP解析成功,但使用ClusterIP解析失败,说明CoreDNS和上游DNS都没问题,问题出在ClusterIP的转发链路或kube-proxy规则上。这种情况可以检查对应节点上的kube-proxy规则、conntrack表项,甚至重启该节点上的kube-proxy来重建规则。如果直接指定Pod IP也解析失败,那么CoreDNS自身或它的上游转发链路有问题,继续走进下一步。

在整个排查过程中,还有一个被我反复验证过的办法:进入CoreDNS Pod,用它的工具去解析同样的域名。CoreDNS镜像里通常带有nslookup或dig。你也可以在CoreDNS Pod里直接查看它自己的resolv.conf,确认上游DNS地址是什么。这样能区分“业务Pod到CoreDNS链路正常,但CoreDNS上游异常”和“CoreDNS本身异常”两个场景。

3.4 第四步:看CoreDNS日志和开启debug输出

日志永远是最直接的证据。CoreDNS的日志默认比较简洁,如果Corefile里没配置log插件,你甚至看不到每个查询的详细记录。排查阶段可以临时修改ConfigMap,加上log和errors插件,然后等待配置自动加载,或者手动滚动重启CoreDNS Pod,让新配置生效。

kubectl edit configmap coredns -n kube-system

在Corefile的.:53块中加入:

log errors

保存后,CoreDNS会根据reload插件自动加载配置,但自动加载时可能会有短暂DNS中断,低峰期操作风险更小。改完以后,重新在业务Pod里发起一次解析请求,然后查看CoreDNS日志:

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

日志里会显示查询的域名、类型、响应码。比如SERVFAIL代表上游响应失败,NXDOMAIN代表域名不存在,NOERROR代表解析成功但可能返回了空答案。通过这些状态码,能直接判断问题发生的位置。如果日志里出现大量SERVFAIL,大概率是forward的上游DNS连不上或拒绝响应;如果日志里根本看不到这条查询,说明请求可能没到达CoreDNS,那就要回头检查Service网络和节点网络策略了。

3.5 第五步:根据根因做修复和验证

定位到根因后,修复手段各有不同。这里整理几个我在实践中用得最多的方案。

如果问题是上游DNS不可达,比如宿主机/etc/resolv.conf里的DNS对CoreDNS容器网络不可达,那么修改Corefile,把forward目标改成集群内可达的DNS地址即可。如果集群在云环境,可以优先使用VPC内网DNS地址,而不是公网DNS,因为公网DNS请求可能被云平台安全组或外部防火墙拦截。改完后注意观察CoreDNS日志,确认不再有超时或SERVFAIL。

如果问题是CoreDNS Pod资源不足,可以水平扩容。扩容CoreDNS不是简单kubectl scale就完事,还需要确认deployment的replicas、PodDisruptionBudget、PodAntiAffinity和topologySpreadConstraints是否合理。扩容前先看当前每个CoreDNS Pod的负载,如果有一个Pod的流量明显比其它Pod高,可能是kube-proxy的随机转发不够均匀,可以配合loadbalance插件或调整kube-proxy模式来改善。

如果是ndots或search域导致的雪崩式无效查询,可以从应用侧调整dnsConfig。比如给特定工作负载配置ndots:2并保留必要的search域,减少外部域名解析时一次DNS请求被拆分成多次查询的情况。这个改动需要充分测试,因为ndots:2会让集群内短域名解析时先直接查询原始域名而不是拼接search域,如果业务对集群内部短域名依赖较高,要谨慎评估。

如果是conntrack表项冲突导致偶发解析失败,可以在节点上执行:

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

但这属于临时缓解,根治还是要降低DNAT连接数,比如增加CoreDNS副本数、或者部署NodeLocal DNS缓存,让每个节点上的解析请求尽可能在本地完成,减少经过ClusterIP的DNAT连接。

无论是哪种修复,最终都要回到业务Pod里重新验证一次。不要只看一条成功的解析记录就宣布修复完成,建议连续解析10次以上,同时用curl或实际业务请求做一次完整验证。因为某些偶发问题只在高并发下才暴露,单次解析成功说明不了什么。

4. 常见问题速查与避坑经验

这一节我把自己在实际工作中积累的故障模式整理成表格,再展开讲几个特别值得注意的坑。如果你时间紧张,可以直接用这个表对照现象来定位方向。

现象可能原因排查方向
Pod解析集群内Service域名失败,外部域名正常CoreDNS到API Server链路异常 / RBAC权限问题查看CoreDNS日志,确认kubernetes插件同步状态
Pod解析外部域名失败,集群内正常forward上游地址不可达 / 上游DNS被防火墙拦截检查Corefile forward目标,从CoreDNS Pod内测试上游连通性
解析偶尔失败,时好时坏CoreDNS高负载 / conntrack冲突 / UDP丢包观察CoreDNS CPU和内存,调整副本数和并发参数,必要时部署本地缓存
域名解析非常慢,超过5秒search域和ndots过大 / cache未命中 / forward上游延迟高检查resolv.conf的search列表,测试上游DNS响应耗时
CoreDNS Pod频繁重启资源limit过低 / 健康检查失败 / 节点绑核问题kubectl describe pod 查看最后状态,调整requests和limits
改了Corefile却不生效reload插件未启用 / ConfigMap挂载延迟手动滚动重启CoreDNS Pod,并用dig验证
Pod里直接ping域名报unknown host镜像工具问题 / 解析器配置异常检查resolv.conf,用nslookup替代测试
使用hostNetwork的Pod解析异常dnsPolicy不匹配 / 继承了宿主机异常DNS检查dnsPolicy,必要时改用ClusterFirstWithHostNet

4.1 状态码不会骗人:SERVFAIL、NXDOMAIN、REFUSED的区别

很多同学在CoreDNS日志里看到状态码时,不知道该往哪个方向查。我说一下最常见的三种。SERVFAIL表示上游DNS在处理时出现了错误,可能是上游超时、拒绝服务,也可能是上游本身依赖的权威DNS无法访问。看到SERVFAIL,优先检查forward配置的上游DNS是否可达、是否有响应。有一个很容易踩的坑:上游DNS配置成了127.0.0.53或127.0.0.1这类loopback地址,CoreDNS容器里访问的是自己的回环接口,根本连不到宿主机的DNS服务,结果就是所有外部域名都SERVFAIL。解决方法是改成节点内网IP上的DNS监听地址,或者使用云厂商VPC内网DNS。

NXDOMAIN表示域名本身不存在。看到这个状态码时,要判断查询的域名是不是被错误地拼接了集群search域。比如访问www.baidu.com,但实际查询可能变成了www.baidu.com.default.svc.cluster.local,上游返回NXDOMAIN。这类现象一般不会导致最终失败,CMD会继续尝试原始域名查询,但会增加一次无效往返。如果业务对延迟极其敏感,就会表现为偶尔超时。这时候优化ndots和search域比折腾CoreDNS更有效。

REFUSED表示上游DNS拒绝响应。这个状态通常说明上游DNS的策略或者ACL限制,只允许特定网段或特定来源查询。我曾经遇到过一个集群,CoreDNS部署在节点A,上游DNS部署在节点B,两个节点网络互通,但上游DNS在网关上只放行了业务网段,忘了放行集群Pod网段,结果就是CoreDNS能ping通上游DNS,但解析请求全部被拒绝。排查这类问题,要在CoreDNS Pod里手动向上游DNS发起查询,观察状态码变化。如果上游DNS支持TCP和UDP,要同时测两种协议,因为有些防火墙只拦截UDP,TCP查询则能成功。

4.2 为什么重启CoreDNS有时候有效,但过几天又复发

有些同学遇到解析问题,习惯先滚动重启一轮CoreDNS Pod。重启之后指标恢复,但过两天问题又回来了。这里面的原理要搞清楚:重启CoreDNS Pod会丢弃它的缓存,重建conntrack表项,甚至可能让Pod调度到另一台节点上,暂时绕开资源紧张或网络不稳定的节点。所以“重启有效”不代表根因消失,只是把症状暂时掩盖了。

有一次我在某环境里排查,发现CoreDNS Pod重启后稳定运行了几个小时,随后解析延迟又开始缓慢上升。最终定位到是某个节点上的UDP netfilter参数太小,大量DNS查询UDP包被丢弃,CoreDNS被迫等待超时后重试。这种行为在低峰期不明显,一旦遇到业务流量波动,就表现为解析超时。重启Pod把容器移到了别的节点,所以短时间内恢复了,但由于集群里大量使用ClusterIP访问CoreDNS,conntrack表项不断堆积,问题再次出现。这种场景下,单纯重启永远治标不治本。更稳妥的办法是给CoreDNS设置合理的资源请求和限制,增加副本,或者引入节点级DNS缓存来降低ClusterIP的DNAT压力。

除了缓存和conntrack,重启CoreDNS还会重新建立与API Server的watch连接。有些时候CoreDNS内存泄漏或goroutine卡死,导致它不再更新Service信息,表现为新增的Service域名解析不了,但老Service域名正常。这种问题重启确实能解决,但应该进一步查清为什么会卡死,比如是否因为集群规模太大,CoreDNS的kubernetes插件默认频率无法及时同步全部对象。必要时要调大kubernetes插件里的resyncperiod参数,或者调整CoreDNS的内存limit。

4.3 外部DNS、云厂商内网DNS和集群自建DNS怎么选

CoreDNS的forward目标选择,看似简单,实则涉及集群部署形态。如果集群在云环境里,我通常建议把VPC内网DNS作为第一上游,因为云厂商的内网DNS在VPC内部延迟低,还能解析一些云内部服务域名。如果选择公网DNS作为上游,必须确认CoreDNS所在安全组的出方向允许UDP和TCP的53端口访问。很多同学遇到“Pod解析外部域名失败,但nslookup指定8.8.8.8却成功”的诡异现象,就是CoreDNS所在节点根本没放行到公网DNS的网络策略。

如果你在自建机房部署K8s,一般会有一到两台内网DNS服务器,负责解析公司内部系统和部分公网域名。此时Corefile里的forward目标最好写成多条地址,比如:

forward . 10.10.10.53 10.10.10.54 { max_concurrent 1000 }

多写一个备用DNS能增强容错性,但要注意CoreDNS的forward插件对多个上游默认是随机选择的,不配置health_check时不会主动剔除故障上游。如果你希望故障上游自动降级,可以加上health_check 5s参数。这段配置会让CoreDNS定期向上游发送健康检查请求,连续失败后会暂时把该上游标记为不可用,从而避免每次查询都打到坏节点上。

还有一种情况是集群内已经有自建DNS,负责统一解析所有内部域名和公网域名。此时CoreDNS会遇到一个关键问题:自建DNS可能不认识集群内部的cluster.local。不过没关系,只要CoreDNS的kubernetes插件在,它处理集群域名时并不会把请求转发给上游。关键要确保Corefile里kubernetes插件的fallthrough配置正确。fallthrough的意思是:如果一个域名没有被kubernetes插件匹配到,是否继续交给后面的插件处理。如果你把fallthrough删掉,所有非集群域名都可能被当作集群域名处理,最终返回NXDOMAIN。默认配置里只对in-addr.arpa和ip6.arpa设置了fallthrough,这个默认值一般够用,改的时候要格外谨慎。

4.4 实战心得:优先建立DNS拨测,别等问题变成故障才去查

经历了太多次从凌晨三点被电话叫醒去查CoreDNS后,我把“提前拨测”这个习惯刻在了团队流程里。DNS解析故障是可以提前暴露的,并不需要等到业务大面积报障才被动响应。我通常会在固定节点部署一个小工具,定期发起三类DNS查询:集群内域名解析、外部域名解析、上游DNS直连解析。只要任一类查询连续失败超过阈值,就自动告警,并把失败状态码输出出来。这样故障刚有苗头,我们就能在业务感知之前介入。

拨测工具的选择不强求。你可以用一个简单的CronJob跑nslookup,也可以自己写一个小脚本用dig定时探测。关键是拨测的目标要贴近真实业务:不要只测一个固定域名,最好把业务方最常见的几个外部域名和内部服务域名都纳入进来。因为CoreDNS对不同域名的解析路径不同,只测一个域名很容易漏掉另一个域名上游DNS的单独故障。拨测结果记录成结构化数据,方便后续对比变更前后是否有劣化。

最后再分享一个小技巧:在修改Corefile后,不要只是直接保存等它reload,我习惯先复制一份原配置备份,然后手动触发kubectl rollout restart deployment/coredns -n kube-system,再立即用拨测工具验证。手动重启虽然会带来秒级抖动,但能保证新配置一定生效,线上经验里ConfigMap挂载偶尔有延迟,依赖自动reload可能让人误判“改了半天怎么没反应”。排障的核心就是严谨、可重复、可验证,每一步都留下证据,你才不会在半夜三更被一个问题反复折磨。

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

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

立即咨询