我发现在 ubuntu 22.04 的 k8s 集群中,如果不禁用掉外部的 dns,比如 8.8.8.8, 集群内部的 dns 解析就会一直失败
root@k8s-ctrl:~/kubespray1-31# cat /etc/resolv.conf
nameserver 169.254.25.10
search default.svc.cluster.local svc.cluster.local cluster.local
#nameserver 8.8.8.8
root@k8s-ctrl:~/kubespray1-31#
root@k8s-ctrl:~/kubespray1-31#
root@k8s-ctrl:~/kubespray1-31#
root@k8s-ctrl:~/kubespray1-31# nslookup kubernetes
;; Got recursion not available from 169.254.25.10
Server: 169.254.25.10
Address: 169.254.25.10#53
Name: kubernetes.default.svc.cluster.local
Address: 10.233.0.1
;; Got recursion not available from 169.254.25.10
root@k8s-ctrl:~/kubespray1-31#
现在观察到的现象,正是 Linux glibc + NodeLocalDNS fallback 行为 的“微妙交互结果”。
我们来逐层剖析下为什么「注释掉外部 DNS」反而能解析成功:
🧩 现象回顾
当 /etc/resolv.conf 是:
nameserver 169.254.25.10
# nameserver 8.8.8.8
执行:
nslookup kubernetes
得到:
;; Got recursion not available from 169.254.25.10
Name: kubernetes.default.svc.cluster.local
Address: 10.233.0.1
✅ 居然成功解析到了 10.233.0.1
⚙️ 内部原理
1️⃣ Linux resolver 的默认行为
/etc/resolv.conf 中定义多个 nameserver 时:
- 默认顺序尝试;
- 若第一个返回 “SERVFAIL / REFUSED / recursion not available”,
glibc 会直接跳到下一个 nameserver; - 但在某些情况下(例如返回
NOERROR+ 有结果),glibc 会“认为成功”并停止; - 如果只有一个 nameserver,则必须使用它,无论返回什么。
2️⃣ NodeLocalDNS 的行为
NodeLocalDNS 监听在 169.254.25.10:
- 对
.cluster.local、in-addr.arpa等域名 → 转发给 CoreDNS; - 对其他域名(如
www.baidu.com)→ 转发给外部上游(比如 8.8.8.8); - 不提供 recursion 服务(即拒绝递归查询请求)。
当你执行:
nslookup kubernetes
查询流程大概如下:
nslookup → resolver → 169.254.25.10 (NodeLocalDNS)
NodeLocalDNS 收到后:
-
发现查询 “kubernetes”;
-
因为
/etc/resolv.conf中的search字段包含:default.svc.cluster.local svc.cluster.local cluster.local→ glibc 会依次尝试补全:
kubernetes.default.svc.cluster.local kubernetes.svc.cluster.local kubernetes.cluster.local kubernetes
CoreDNS 能正确回答第一个 → 成功解析。
3️⃣ 那“Got recursion not available”是啥意思?
那其实是 nslookup 的显示逻辑问题:
- 它在尝试最初的短名
kubernetes时,NodeLocalDNS 拒绝递归; - 但随后 resolver 自动补全
kubernetes.default.svc.cluster.local并得到成功响应; - 所以 nslookup 同时显示两段:
一段是 recursion not available(短名失败),
一段是成功结果(补全名成功)。
4️⃣ 为什么注释掉 8.8.8.8 后反而“更稳”?
因为:
- 当有多个 nameserver 时,glibc 在遇到 recursion not available 后会立刻尝试下一个(8.8.8.8);
- 而 8.8.8.8 对
kubernetes.default.svc.cluster.local返回 NXDOMAIN; - 最终 resolver 会以 最后一次失败 为准(即 NXDOMAIN);
- 结果就覆盖掉了 NodeLocalDNS 的成功答复。
所以:
✅ 去掉 8.8.8.8 → resolver 不再 fallback → NodeLocalDNS 的答复被保留并显示成功。
总结: 其实 dns 解析虽然成功了,但是也失败了,所以最后失败了。
✅ 总结对比表
/etc/resolv.conf 配置 | 解析结果 | 原因 |
|---|---|---|
nameserver 169.254.25.10``nameserver 8.8.8.8 | ❌ 失败 | NodeLocalDNS 拒绝 recursion → fallback 到 8.8.8.8 → NXDOMAIN 覆盖结果 |
仅 nameserver 169.254.25.10 | ✅ 成功 | glibc 无可退选 → 等待 NodeLocalDNS 补全 .svc.cluster.local 并得到正确答复 |
💡 建议
你的现象完全符合 NodeLocalDNS 预期逻辑。
最推荐的配置是:
nameserver 169.254.25.10
search default.svc.cluster.local svc.cluster.local cluster.local
✅ Pod 内外 DNS 都可正常工作
✅ NodeLocalDNS 缓存生效
✅ 不会被外部 DNS 覆盖错误返回