来源:huyouba1 | 发布日期:2026-06-21
一、K8s DNS 解析链路
K8s 的 DNS 不是「一个服务」,而是一条完整的请求链路。不理解这条链路,排查就是盲人摸象。
完整链路(ClusterFirst 策略):
Pod 发起 DNS 查询
↓ 先查 /etc/resolv.conf(nameserver = kube-dns Service IP)
↓ 请求到达 kube-dns Service(ClusterIP: 通常 .10)
↓ kube-dns 将请求转发给 CoreDNS Pod(默认3副本)
↓ CoreDNS 查询:先查本地缓存 → 再查 K8s Service 记录(由 kubernetes 插件处理)
↓ 若非集群内域名,走上游 DNS(默认继承 Node 的 /etc/resolv.conf)
↓ 返回结果,逐层回传
关键配置文件 /etc/resolv.conf
nameserver 172.20.0.10 # kube-dns Service IP
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5 timeout:2 attempts:3
⚠️ ndots:5 是最大的性能杀手。这个参数决定了什么情况下认为域名「完整」、不需要拼接 search 后缀。ndots:5 意味着:域名中点号少于5个,就先拼接 search 后缀再查询——这会导致大量不必要的DNS查询。这个问题在 K8s 1.21+ 有所改善,但存量集群依然广泛存在。
二、5 类常见 DNS 异常:症状 + 定位 + 修复
🔴 类型 1:DNS 查询超时(最常见)
典型症状:
curl: (6) Could not resolve host: api.example.com
dial tcp: lookup xxx on 172.20.0.10:53: read udp 172.20.0.10:53: i/o timeout
Name or service not known
快速定位步骤:
- 确认 CoreDNS Pod 是否 Running:
kubectl get pods -n kube-system -l k8s-app=kube-dns - 查看 CoreDNS 日志:
kubectl logs -n kube-system <coredns-pod> - 在 Pod 内手动测试 DNS:
kubectl exec -it <any-pod> -- nslookup google.com - 确认 kube-dns Service 正常:
kubectl get svc -n kube-system kube-dns - 检查 Node 的 conntrack 表是否满(DNS 超时最常见根因之一)
根因 A:CoreDNS Pod 资源不足
CoreDNS 默认资源限制很低(通常 70m CPU / 170Mi 内存)。当集群规模增长,DNS QPS 上升,CoreDNS 就会 OOM 被杀或 CPU 节流,表现为间歇性 DNS 超时。
修复方案:
kubectl edit deployment -n kube-system coredns
# 建议:CPU 100m-200m,内存 256Mi-512Mi(视集群规模)
# 同时:副本数从3提升到5(跨可用区部署)
根因 B:conntrack 表满(高频!)
Linux 内核的 conntrack 表记录了所有 NAT 连接。DNS 使用 UDP,每次查询都会占用一个 conntrack 条目,且超时时间是 30s(默认值)。当 DNS QPS 很高时,conntrack 表会被打满,新连接无法建立,表现为 DNS 超时。
修复方案:
# 检查 conntrack 使用量
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 调大 conntrack 表大小(在所有 Node 上)
echo "net.netfilter.nf_conntrack_max = 1310720" >> /etc/sysctl.conf
echo "net.netfilter.nf_conntrack_tcp_timeout_established = 86400" >> /etc/sysctl.conf
sysctl -p
🟡 类型 2:DNS 查询慢(不超时,但延迟高)
症状更隐蔽:请求没有报错,但 P99 延迟突然从 50ms 飙升到 800ms。查应用层没发现问题,最后发现是 DNS 查询耗时过长。
根因:ndots:5 导致的 search 风暴
当 Pod 访问一个外部域名(如 api.weixin.qq.com),由于该域名中点号数为 3(<5),DNS 客户端会先拼接 search 后缀发起查询:
# 实际发出的查询(按顺序,直到命中或耗尽):
1. api.weixin.qq.com.default.svc.cluster.local → NXDOMAIN
2. api.weixin.qq.com.svc.cluster.local → NXDOMAIN
3. api.weixin.qq.com.cluster.local → NXDOMAIN
4. api.weixin.qq.com. → 真正查询(成功)
# 每次失败都有 2s 超时(options timeout:2),最多 attempts:3
# 最坏情况:3次失败 × 3次重试 = 18秒延迟!
修复方案(三选一,推荐方案1):
- 升级 K8s 到 1.21+:默认 ndots 改为 2,同时 Pod 的 dnsConfig 支持更细粒度控制
- 应用层指定完整域名:访问外部服务时,域名末尾加
.(如api.weixin.qq.com.),跳过 search - 自定义 dnsConfig:在 Deployment 里设置 ndots:2,仅影响本应用
🟣 类型 3:集群内服务域名解析失败
症状:Pod 可以正常解析外网域名,但无法解析集群内的 Service(如 redis.default.svc.cluster.local)。
常见根因:
- Service 未创建或命名空间不匹配:确认 Service 在正确 ns 下
- CoreDNS 的 kubernetes 插件配置错误:检查 CoreDNS ConfigMap
- 网络策略(NetworkPolicy)拦截了 DNS 流量:检查是否有限制 53 端口的 NP
- kube-dns Service 的 Endpoints 不正常:Pod 未就绪导致 Service 无后端
排查命令:
# 检查 kube-dns Service 的 Endpoints(看是否有健康的 CoreDNS Pod)
kubectl get endpoints -n kube-system kube-dns
# 检查 CoreDNS 配置(ConfigMap)
kubectl get configmap -n kube-system coredns -o yaml
# 在 Pod 内直接测试集群内域名
kubectl exec -it <pod> -- nslookup redis.default.svc.cluster.local 172.20.0.10
🔵 类型 4:DNS 缓存导致服务发现延迟
这是最容易被忽视的一类问题。应用使用了 DNS 缓存库(如 Go 的 github.com/miekg/dns 或 Java 的 dnsjava),缓存 TTL 设置过长。当后端 Service 的 ClusterIP 发生变化(比如重建 Service),客户端要等缓存过期才能感知到新地址,导致连接失败。
💡 最佳实践:K8s 集群内的 DNS 缓存 TTL 建议设置为 5-10 秒(集群内服务随时可能变 IP)。如果是外部域名,可以设置更长(60s+)。不要禁用 DNS 缓存(会打爆 CoreDNS),而是设置一个合理的短 TTL。
🟢 类型 5:NodeLocal DNS Cache 引发的新问题
为了解决上述所有问题,K8s 社区推出了 NodeLocal DNS Cache(在每节点跑一个 DNS 缓存 DaemonSet,Pod 直接查本地缓存)。这确实大幅降低了 CoreDNS 负载,但也引入了新的故障模式。
新症状:部分节点上的 Pod DNS 解析异常,其他节点正常。
根因通常是:NodeLocal DNS Cache 的 DaemonSet Pod(nodelocaldns)在某节点上崩溃或 OOM,而 Kubelet 的 --cluster-dns 指向的是本地缓存(169.254.20.10),导致该节点上所有 Pod 的 DNS 全部失效。
修复方案:
# 检查各节点的 nodelocaldns 状态
kubectl get pods -n kube-system -o wide | grep nodelocaldns
# 如果发现有 OOMKilled,调整其内存限制(建议 512Mi+)
kubectl edit daemonset -n kube-system nodelocaldns
三、DNS 故障排查 SOP(5 分钟快速定位)
把上面所有场景抽象一下,就是下面这套 5 分钟快速定位流程:
STEP 1:确认故障范围(1分钟)
- 是全部 Pod 还是部分 Pod?
- 是集群内域名还是外部域名?
- 是新出现还是间歇出现?
STEP 2:检查 CoreDNS 健康状态(1分钟)
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl top pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system <coredns-pod> --tail=50
STEP 3:在 Pod 内手动测试(1分钟)
kubectl exec -it <pod> -- nslookup <target>
kubectl exec -it <pod> -- dig <target> @172.20.0.10
# 对比:直接指定 DNS Server 的查询结果
STEP 4:检查基础设施(1分钟)
- 各 Node 的 conntrack 使用量
- NodeLocal DNS Cache 状态(如果启用了)
- kube-dns Service 的 Endpoints 是否完整
STEP 5:根因修复 + 验证(1分钟操作 + 观察)
- 按前文对应类型进行修复
- 修复后在 Pod 内重跑 nslookup 验证
四、CoreDNS 优化:让 DNS 不再成为瓶颈
1. CoreDNS 配置优化(ConfigMap)
默认的 CoreDNS 配置对于大规模集群(>500 节点或 >5000 Pod)是不够的。以下是推荐的优化配置:
# kubectl edit configmap -n kube-system coredns
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
}
# 🔑 关键优化1:开启缓存,默认TTL 30s
cache 30
# 🔑 关键优化2:限制并发,防止被打爆
reload 10s
# 🔑 关键优化3:上游DNS配置(避免依赖Node的resolv.conf)
forward . 8.8.8.8 8.8.4.4
prometheus :9153
}
2. CoreDNS 副本数与调度策略
- 副本数:建议至少 3个(小集群)到 5-7个(大集群),且必须配置 podAntiAffinity,确保副本分布在不同节点
- 资源限制:CPU 建议 200m-500m,内存 512Mi(监控实际用量后调整)
- 探针配置:确保 livenessProbe 和 readinessProbe 都已正确配置
3. 监控:关键指标不能少
在 Prometheus + Grafana 中,至少监控以下 CoreDNS 指标:
| 指标 | 含义 | 告警阈值 |
|---|---|---|
| coredns_dns_request_duration_seconds | DNS 查询延迟 | P99 > 100ms |
| coredns_dns_responses_total{rcode="NXDOMAIN"} | 解析失败次数 | 5分钟内 > 100 |
| coredns_cache_hits_total | 缓存命中率 | 命中率 < 50% 需关注 |
| process_resident_memory_bytes | 内存使用量 | 接近限制值的 80% |
五、总结:DNS 稳定性建设的三条原则
- 预防胜于救火。在集群搭建阶段就把 CoreDNS 资源配置好(合理的副本数、资源限制、cache 插件),比故障后再排查效率高 10 倍。
- 监控要前置。DNS 是基础设施的基础设施,它的异常影响是全局性的。给 CoreDNS 配置完善的监控告警,是 SRE 的基本功。
- 排查要有 SOP。DNS 故障往往在凌晨发生,值班同学精神状态不佳。提前准备好排查 SOP,能让 MTTR(平均修复时间)从 30 分钟压缩到 5 分钟。
行动建议(本周就能做)
- 检查你们集群的 CoreDNS 资源限制和副本数,是否存在隐患
- 在 Grafana 里加一个 CoreDNS 监控面板(用本文第四节的指标)
- 把本文的排查 SOP 保存下来,贴在团队的故障响应文档里
关联页面
| 页面 | 关联点 |
|---|---|
| k8s-dns-conntrack-5s-timeout | K8s DNS 间歇性 5s 解析超时根因深挖:conntrack 竞态 + ndots 放大效应 |
| k8s-dns-iptables-troubleshooting | K8s DNS 故障真实案例复盘:iptables 封禁 53 端口导致 CoreDNS 雪崩 |
| k8s-service-access-troubleshooting | K8s 服务访问排查十步工作流:Pod→Service→kube-proxy→CNI→Ingress→DNS→NetworkPolicy |