返回首页

Kubernetes DNS 故障排查与高可用实战:从超时到熔断的完整 SOP

📅 创建于 2026-07-20 🔄 更新于 2026-07-20 📝 837 字

来源: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

快速定位步骤:

  1. 确认 CoreDNS Pod 是否 Running:kubectl get pods -n kube-system -l k8s-app=kube-dns
  2. 查看 CoreDNS 日志:kubectl logs -n kube-system <coredns-pod>
  3. 在 Pod 内手动测试 DNS:kubectl exec -it <any-pod> -- nslookup google.com
  4. 确认 kube-dns Service 正常:kubectl get svc -n kube-system kube-dns
  5. 检查 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):

  1. 升级 K8s 到 1.21+:默认 ndots 改为 2,同时 Pod 的 dnsConfig 支持更细粒度控制
  2. 应用层指定完整域名:访问外部服务时,域名末尾加 .(如 api.weixin.qq.com.),跳过 search
  3. 自定义 dnsConfig:在 Deployment 里设置 ndots:2,仅影响本应用

🟣 类型 3:集群内服务域名解析失败

症状:Pod 可以正常解析外网域名,但无法解析集群内的 Service(如 redis.default.svc.cluster.local)。

常见根因:

  1. Service 未创建或命名空间不匹配:确认 Service 在正确 ns 下
  2. CoreDNS 的 kubernetes 插件配置错误:检查 CoreDNS ConfigMap
  3. 网络策略(NetworkPolicy)拦截了 DNS 流量:检查是否有限制 53 端口的 NP
  4. 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 稳定性建设的三条原则

  1. 预防胜于救火。在集群搭建阶段就把 CoreDNS 资源配置好(合理的副本数、资源限制、cache 插件),比故障后再排查效率高 10 倍。
  2. 监控要前置。DNS 是基础设施的基础设施,它的异常影响是全局性的。给 CoreDNS 配置完善的监控告警,是 SRE 的基本功。
  3. 排查要有 SOP。DNS 故障往往在凌晨发生,值班同学精神状态不佳。提前准备好排查 SOP,能让 MTTR(平均修复时间)从 30 分钟压缩到 5 分钟。

行动建议(本周就能做)

  1. 检查你们集群的 CoreDNS 资源限制和副本数,是否存在隐患
  2. 在 Grafana 里加一个 CoreDNS 监控面板(用本文第四节的指标)
  3. 把本文的排查 SOP 保存下来,贴在团队的故障响应文档里

关联页面

页面关联点
k8s-dns-conntrack-5s-timeoutK8s DNS 间歇性 5s 解析超时根因深挖:conntrack 竞态 + ndots 放大效应
k8s-dns-iptables-troubleshootingK8s DNS 故障真实案例复盘:iptables 封禁 53 端口导致 CoreDNS 雪崩
k8s-service-access-troubleshootingK8s 服务访问排查十步工作流:Pod→Service→kube-proxy→CNI→Ingress→DNS→NetworkPolicy