首页 / 资讯中心 / 文章详情

Kubernetes Pod 服务别名解析失败排查:从 no such host 到 CoreDNS 根因定位

Kubernetes Pod 服务别名解析失败排查:从 no such host 到 CoreDNS 根因定位 ★ FEATURED ARTICLE
1. 从一个诡异的通信故障说起集群里跑着一套微服务订单服务要调库存服务库存服务要调商品服务彼此之间全靠 Service 别名互相访问。某天做灰度发布新拉起来的一批 Pod 突然报错日志里清一色是no such host或者连接超时。诡异的地方在于老 Pod 一切正常新 Pod 就是找不到隔壁服务。重启、重建、换节点全试了一遍问题依旧。这种同一个集群里有的 Pod 能通、有的 Pod 不能通的现象是 Kubernetes 网络排查里最让人头疼的一类。这篇记录就是围绕这个场景展开的。核心关键词是Kubernetes、Pod 内服务别名通信、DNS 解析、CoreDNS、Service 发现。我会把当时从现象到根因、从临时绕过到彻底修复的完整过程拆开讲包括 DNS 解析链路是怎么走的、ndots和search域为什么会让别名解析失败、CoreDNS 的配置怎么查、NetworkPolicy 和 Endpoint 怎么排除。适合正在被集群内服务互调问题折磨的运维和开发同学也适合刚接触 K8s 网络、想搞明白为什么我的 Pod 解析不了服务名的入门者。看完你至少能做到拿到一个no such host的报错知道按什么顺序去查而不是盲目重启。2. 先搞清楚 Pod 里服务别名到底是怎么解析的2.1 服务别名不是魔法本质是一次 DNS 查询很多人以为 Pod 里写inventory-svc就能通是 Kubernetes 内置了什么服务发现魔法。其实不是。当你在 Pod 里执行curl http://inventory-svc:8080时第一步是 glibc 或 musl 的解析器去查 DNS把inventory-svc这个名字翻译成一个 ClusterIP然后才发起 TCP 连接。真正干活的是集群里那个叫CoreDNS的组件。CoreDNS 以 Deployment 形式跑在kube-system命名空间下前面挂一个名为kube-dns的 ServiceClusterIP 通常是10.96.0.10这个网段里的某个地址。每个 Pod 被创建时kubelet 会把/etc/resolv.conf写进去内容大致长这样nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5这三行就是整个服务发现的入口。nameserver指向 CoreDNS 的 ClusterIPsearch定义了域名后缀的补全顺序ndots:5决定了什么样的名字会先被当成绝对域名去查。理解这三行基本就理解了 80% 的别名解析问题。2.2 一次inventory-svc查询的完整链路假设 Pod 在default命名空间执行getent hosts inventory-svc解析器会按search列表依次尝试inventory-svc.default.svc.cluster.localinventory-svc.svc.cluster.localinventory-svc.cluster.local最后才尝试inventory-svc.作为绝对域名只要其中任意一个返回了 A 记录解析就成功。CoreDNS 收到查询后会根据配置的kubernetes插件去 API Server 里查对应的 Service 或 Endpoint把 ClusterIP 返回去。整个过程在毫秒级完成正常情况下你根本感知不到。问题就出在正常情况下这四个字。一旦search域不对、ndots被改、CoreDNS 挂了、或者查询被 NetworkPolicy 拦了这条链路就会断在某一环表现就是no such host。2.3 为什么有的 Pod 能通、有的不能这是当时最迷惑我的点。同一个 Deployment滚动更新出来的新 Pod 解析不了老 Pod 却好好的。后来发现问题往往出在Pod 的 DNS 配置差异上。Kubernetes 允许通过dnsPolicy和dnsConfig对单个 Pod 的解析行为做定制dnsPolicy: ClusterFirst是默认值走集群 DNS。dnsPolicy: Default会继承节点上的 resolv.conf不走 CoreDNS自然解析不了服务别名。dnsPolicy: None配合dnsConfig手动指定配错了就直接废掉。dnsConfig里的searches和options会覆盖默认值改错ndots或search顺序别名就查不到。当时那批新 Pod 的 YAML 里有人为了优化外部域名解析加了dnsConfig把ndots从 5 改成了 1还把searches清空了。结果就是外部域名确实快了但集群内的短别名inventory-svc因为ndots:1被当成绝对域名直接查search域又没了CoreDNS 自然返回 NXDOMAIN。老 Pod 没这个配置所以正常。这就是同集群不同命的典型根因。3. 排查这类问题的标准动作与工具3.1 第一步永远是看 Pod 里的 resolv.conf别急着上 tcpdump先kubectl exec进 Pod把/etc/resolv.conf打出来kubectl exec -it pod-name -n namespace -- cat /etc/resolv.conf重点核对三件事nameserver是不是 CoreDNS 的 ClusterIP、search域里有没有svc.cluster.local、ndots是不是被改过。我踩过的坑里有一半以上在这一步就能定位。如果nameserver指向的是8.8.8.8这种外部地址那基本可以确定dnsPolicy配错了Pod 压根没走集群 DNS。3.2 用 nslookup 和 dig 分层验证确认 resolv.conf 没问题后进 Pod 手动查# 查短别名 nslookup inventory-svc # 查完整 FQDN nslookup inventory-svc.default.svc.cluster.local # 直接指定 CoreDNS 查排除 search 域干扰 nslookup inventory-svc.default.svc.cluster.local 10.96.0.10如果短别名失败、FQDN 成功问题就在search域或ndots如果 FQDN 也失败但直接指定 CoreDNS 又成功那可能是 Pod 的 resolv.conf 里 nameserver 写错了如果连指定 CoreDNS 都失败问题就上升到 CoreDNS 本身或网络策略层面了。这个分层验证法能快速缩小范围比一上来就抓包高效得多。提示很多精简镜像里没有nslookup和dig。可以临时起一个带工具的调试 Pod用kubectl run tmp --imagebusybox --rm -it -- sh然后nslookup目标服务。busybox 自带 nslookup够用。3.3 检查 CoreDNS 自身状态如果怀疑是 CoreDNS 的问题先看 Pod 和日志kubectl get pods -n kube-system -l k8s-appkube-dns kubectl logs -n kube-system -l k8s-appkube-dns --tail100重点看日志里有没有大量SERVFAIL、NXDOMAIN、i/o timeout。如果 CoreDNS Pod 频繁重启可能是资源不够或者配置有语法错误。再看 ConfigMapkubectl get configmap coredns -n kube-system -o yaml里面的Corefile定义了kubernetes插件、forward上游、cache等。如果kubernetes插件的pods或endpoints配置被改坏服务解析就会出问题。我遇到过有人为了减少 API Server 压力把pods insecure去掉结果某些依赖 Pod 域名解析的场景直接挂了。3.4 别忘了 Endpoint 和 NetworkPolicyDNS 解析成功只是第一步解析出来的 ClusterIP 后面还得有真实的 Endpoint 才能通。如果 Service 的 selector 写错、或者后端 Pod 没 ReadyEndpoint 列表就是空的连接会超时而不是no such host。这两个现象要区分开现象可能原因排查方向no such hostDNS 解析失败resolv.conf、CoreDNS、ndotsconnection refused端口不对或进程没起Service targetPort、容器端口connection timeoutEndpoint 为空或网络策略拦截Endpoint 列表、NetworkPolicy间歇性失败CoreDNS 负载高或缓存问题CoreDNS 资源、cache 配置NetworkPolicy 是另一个隐形杀手。如果命名空间里配了默认拒绝入站的策略又没放行 CoreDNS 的 53 端口Pod 的 DNS 查询会被直接丢弃表现也是解析超时。查一下kubectl get networkpolicy -n namespace确认有没有策略把 UDP/TCP 53 给挡了。4. 根因定位与修复实操4.1 还原当时的故障现场回到开头那个案例。新 Pod 报no such host老 Pod 正常。按上面的顺序查第一步进新 Pod 看 resolv.conf发现ndots:1search域是空的。老 Pod 是ndots:5加完整 search 域。差异找到了。第二步在新 Pod 里nslookup inventory-svc失败nslookup inventory-svc.default.svc.cluster.local成功。这印证了短别名因为ndots:1被当成绝对域名又没有 search 域补全所以查不到FQDN 是完整的所以能查到。第三步翻 Deployment 的 YAML找到那段优化配置dnsConfig: options: - name: ndots value: 1 searches: []问题根源确认有人手动清空了searches并把ndots降到 1破坏了集群内短别名的解析。4.2 修复方案与验证修复很简单把dnsConfig里破坏性的部分去掉恢复默认行为# 删除 searches: [] 和 ndots 覆盖或者显式写回正确值 dnsConfig: options: - name: ndots value: 5 searches: - default.svc.cluster.local - svc.cluster.local - cluster.local改完滚动更新新 Pod 起来后再验证kubectl exec -it new-pod -- cat /etc/resolv.conf kubectl exec -it new-pod -- nslookup inventory-svc短别名解析成功故障解除。这里有个经验如果确实需要优化外部域名解析速度不要动ndots和searches而是用 FQDN 访问外部域名或者在 CoreDNS 层面做 forward 优化。动 Pod 级别的 DNS 配置影响面太大很容易误伤集群内通信。4.3 参数背后的取舍逻辑为什么默认ndots是 5因为 Kubernetes 希望集群内的短别名优先被解析。ndots的含义是如果域名里的点数量少于这个值就先按 search 域补全去查。inventory-svc有 0 个点小于 5所以会走 search 列表。如果设成 1inventory-svc的 0 个点仍然小于 1理论上还是会走 search——但前提是 search 列表非空。当时的问题是 search 被清空了所以补全无从谈起直接查绝对域名失败。那为什么有人会把ndots调小因为访问外部域名如api.example.com有 2 个点默认ndots:5下会先尝试api.example.com.default.svc.cluster.local等一系列无效查询浪费几次 DNS 往返才查到真实地址。调小ndots能减少这种无效查询。但代价就是集群内短别名可能受影响。这是一个典型的优化外部、牺牲内部的取舍做之前必须评估集群内服务互调的规模。注意如果非要调ndots建议保留完整的searches列表并且把值设在 2 到 3 之间而不是 1。这样既能减少外部域名的无效查询又能保证集群内短别名0 个点仍然走 search 补全。5. 常见问题速查与避坑心得5.1 高频问题速查表问题现象根因解决动作短别名no such hostFQDN 正常ndots或searches被改恢复默认 DNS 配置所有域名都解析失败dnsPolicy: Default或 nameserver 错改回ClusterFirst间歇性解析超时CoreDNS 负载高或副本不足扩容 CoreDNS、调 resources解析成功但连接超时Endpoint 为空或 NetworkPolicy查 Endpoint、放行 53 端口跨命名空间别名失败用了短名但不在同命名空间用svc.namespace或 FQDNCoreDNS 日志大量 SERVFAIL上游 forward 不可达检查 Corefile 的 forward 配置5.2 跨命名空间访问的坑短别名inventory-svc只在同命名空间内有效。如果订单服务在order命名空间库存服务在inventory命名空间那订单服务里必须写inventory-svc.inventory或者完整 FQDNinventory-svc.inventory.svc.cluster.local。我见过有人图省事在两个命名空间里各建了一个同名 Service结果解析到了错误的那个排查了半天。命名空间隔离是好事但别名解析不会跨命名空间自动补全这一点必须记牢。5.3 独家避坑心得第一条永远不要在生产环境随意改 Pod 的dnsConfig。如果确实有外部域名解析性能问题优先在 CoreDNS 的 Corefile 里做forward和cache优化而不是动每个 Pod 的解析器配置。Pod 级别的改动影响面不可控滚动更新时新老 Pod 行为不一致排查成本极高。第二条给 CoreDNS 配好资源限制和副本数。默认 CoreDNS 可能只有 2 个副本集群规模一大QPS 上来就容易超时。建议至少 3 个副本并且配上podAntiAffinity打散到不同节点。资源方面requests给足 CPUDNS 是延迟敏感型服务CPU 被限流会直接导致解析超时。第三条用kubectl run起临时调试 Pod 时记得指定命名空间。默认会落在default如果你要排查的是order命名空间的服务调试 Pod 也得在order里否则短别名解析行为不一样容易误判。第四条CoreDNS 的 cache 有时会骗你。改了 Service 的 Endpoint 后短时间内解析结果可能还是旧的。排查时如果怀疑缓存可以在 Corefile 里临时调小cache的 TTL或者直接重启 CoreDNS Pod 清缓存。不过生产环境慎重重启最好通过配置调整。5.4 一个容易被忽略的细节IPv6 与双栈如果集群开了双栈Pod 的 resolv.conf 里可能同时有 IPv4 和 IPv6 的 nameserver。某些应用的解析器对 IPv6 处理不好会先尝试 IPv6 查询、超时后再回退 IPv4表现为解析特别慢但最终能通。这种情况可以在dnsConfig里显式指定nameservers只保留 IPv4 的 CoreDNS 地址或者检查 CoreDNS 的 Service 是不是双栈暴露的。双栈环境下的 DNS 问题比较隐蔽日志里往往只看到延迟看不到报错需要结合抓包才能确认。6. 把排查思路固化成习惯这套排查流程我后来整理成了一个固定动作一看 resolv.conf二查短名与 FQDN 对比三验 CoreDNS 状态四排 Endpoint 与 NetworkPolicy。四步走下来绝大多数 Pod 内服务别名通信问题都能定位。真正难的不是技术本身而是面对有的通有的不通这种非确定性现象时能不能保持分层验证的耐心而不是一上来就重启大法。我个人在实际操作中的体会是Kubernetes 的网络问题90% 的根因都藏在配置差异里而不是组件本身坏了。同一个集群里行为不一致第一反应应该是这两个 Pod 的配置哪里不一样而不是集群是不是出问题了。把kubectl exec进 Pod 看 resolv.conf 变成肌肉记忆能省下大量抓包和重启的时间。最后再分享一个小技巧给关键命名空间配一个常驻的调试 Pod镜像里预装dig、nslookup、tcpdump、curl出问题时直接 exec 进去比临时拉镜像快得多也不会因为镜像拉取失败耽误排查。
阅读完成 · 觉得有帮助?
咨询建站