我不会起名字322· 后端 / 算法 / 数据库 技术栈 力扣 Hot100 Go 项目 Redis MySQL文章目录Service 能通但 DNS 时好时坏CoreDNS 与 ndots 的坑一、现象为什么时好时坏二、先看 Pod 里的 resolv.conf三、坑 1ndots:5 造成的查询放大解法四、坑 2CoreDNS 副本不够 / 没有自动扩容五、坑 3UDP 丢包与 conntrack六、坑 4search 域导致的跨 namespace 误解析七、带走物Service 能通但 DNS 时好时坏CoreDNS 与 ndots 的坑一、现象为什么时好时坏典型表现Pod 里curl my-svc.ns.svc.cluster.local有时秒回、有时卡 5 秒才通或者干脆报could not resolve host重试又好了用 ClusterIP 直连完全正常用 Service 名就不稳定高峰期更明显。这类问题九成出在 DNS而不是 Service 本身。因为 Service 名解析不通 拿不到 ClusterIP 连不上而直接用 IP 是好的这就把问题范围锁死在 DNS 上了。二、先看 Pod 里的 resolv.conf每个 Pod 的/etc/resolv.conf长这样nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5这三行决定了所有解析行为逐个拆。三、坑 1ndots:5造成的查询放大ndots的含义是域名里的点数少于这个值时先当作非完全限定域名依次用 search 域去拼了再查。ndots:5意味着一个域名里点数 5就走 search 拼接。看一个具体例子。应用里访问redis.prod1 个点因为 1 5所以要依次尝试 1. redis.prod.default.svc.cluster.local 2. redis.prod.svc.cluster.local 3. redis.prod.cluster.local 4. redis.prod ← 前 3 次都 NXDOMAIN第 4 次才命中前 3 次查询全部返回 NXDOMAIN第 4 次才成功。一次域名解析变成了 4 次 DNS 查询。更极端的例子访问www.baidu.com2 个点1. www.baidu.com.default.svc.cluster.local → NXDOMAIN 2. www.baidu.com.svc.cluster.local → NXDOMAIN 3. www.baidu.com.cluster.local → NXDOMAIN 4. www.baidu.com → 命中访问一个外部域名也被放大 4 倍。后果有两个延迟每次解析要串行走 4 次查询如果 CoreDNS 慢延迟直接 4 倍CoreDNS 压力集群里所有 Pod 的所有解析都被放大 4 倍CoreDNS 很容易被打满然后连正常的解析也开始超时——这就形成了时好时坏。解法三种按优先级解法 1最推荐把内部域名写全。redis.prod.svc.cluster.local ← 5 个点≥ ndots直接查不走 search这样只查 1 次。代价是配置要写长一点。解法 2外部域名末尾加点。www.baidu.com. ← 末尾的点表示完全限定域名FQDN直接查解法 3调小 ndots。spec:dnsConfig:options:-name:ndotsvalue:2ndots:2时redis.prod1 个点依然会走 search但a.b.c2 个点就不走了。折中方案。注意ndots调太小会有反效果——如果你习惯了写短域名my-svc那它会直接去查公网 DNS反而更慢甚至失败。所以改 ndots 必须配合写全域名的习惯否则会引入新问题。四、坑 2CoreDNS 副本不够 / 没有自动扩容CoreDNS 默认 2 个副本在稍大的集群里很容易成为瓶颈。kubectl get deploy-nkube-system coredns kubectltoppod-nkube-system-lk8s-appkube-dns判断是不是 CoreDNS 扛不住了# 在 Pod 里连续解析看延迟kubectl run-it--rmdns-test--imagebusybox:1.28--restartNever --\sh-cfor i in $(seq 1 10); do time nslookup kubernetes.default; done如果单次解析超过 100ms 或者偶发超时就是 CoreDNS 压力大。解法 1扩容 CoreDNS。kubectl scale deploy/coredns-nkube-system--replicas4解法 2装 NodeLocal DNSCache推荐。它在每个节点上跑一个 DNS 缓存代理Pod 的 DNS 查询先走本机缓存miss 了才去 CoreDNS。这样大部分解析在本机命中延迟从毫秒级降到亚毫秒级CoreDNS 的压力大幅下降用的是 TCP 而不是 UDP 去连 CoreDNS减少丢包。kubectl get pods-nkube-system-lk8s-appnode-local-dns解法 3开启 CoreDNS 的 cache 插件默认是开的确认一下# coredns ConfigMap .:53 { cache 30 # 缓存 30 秒 ... }五、坑 3UDP 丢包与 conntrack这是时好时坏最经典的物理成因。DNS 默认走 UDP。UDP 的 DNS 查询在 Linux 上会占 conntrack 表项默认 30 秒超时。当并发查询量大时DNS 查询量放大ndots 导致× 查询并发高 → conntrack 表被 UDP 项填满 → 新的 UDP 包被内核静默丢弃 → DNS 查询超时5 秒 → 表现为时好时坏确诊dmesg|grep-inf_conntrack: table full为什么时好时坏而不是全挂DNS 客户端musl/glibc在超时后会自动重试重试可能就通了而且 conntrack 表项在 30 秒后会被回收压力是波动的。解法参考 conntrack 那篇调大表 缩短 UDP 超时 用 NodeLocal DNSCache它把 Pod→DNS 的 UDP 变成节点内的出节点时走 TCP。六、坑 4search域导致的跨 namespace 误解析这个坑很隐蔽。resolv.conf里有search default.svc.cluster.local svc.cluster.local cluster.local假设你在prodnamespace 里访问redis而defaultnamespace 里恰好也有一个叫redis的 Service1. redis.prod.svc.cluster.local ← 先查自己 namespace命中这个顺序是对的自己的 namespace 优先。但如果你写错了域名或者应用里用了不存在的名字就可能意外解析到别的 namespace 的同名 Service——连到了错误的服务而不自知。排查建议应用里访问 Service 一律写全svc.namespace.svc.cluster.local不要依赖 search。七、带走物排查顺序# 1. 看 Pod 的 resolv.confndots 是多少kubectlexecpod--cat/etc/resolv.conf# 2. 在 Pod 里连续解析量延迟kubectlexecpod--sh-cfor i in 1 2 3 4 5; do time nslookup kubernetes.default; done# 3. 看 CoreDNS 状态kubectl get deploy-nkube-system coredns kubectltoppod-nkube-system-lk8s-appkube-dns# 4. 看是不是 conntrack 满UDP 丢包的经典原因dmesg|grep-inf_conntrack: table full对应的修法原因修法ndots:5放大查询内部域名写全.svc.cluster.local、外部域名末尾加点或调小 ndotsCoreDNS 压力大扩容副本 / 装 NodeLocal DNSCache / 确认 cache 插件conntrack 满调大nf_conntrack_max 缩短 UDP 超时跨 namespace 误解析一律写全域名不依赖 search一句话结论“Service 名时好时坏但 IP 直连正常”问题就在 DNS而 K8s 里 DNS 的头号麻烦是ndots:5把每次解析放大 4 倍再叠加 UDP 丢包就变成了随机超时。