Skip to content

Kubernetes DNS(CoreDNS)监控完整体系

在 Kubernetes 集群中,DNS 是服务发现和一切 Pod 间通信的基础设施:任何一次 Service 访问、跨命名空间调用、拉取外部接口,第一步都是 DNS 解析。DNS 一旦变慢或失败,故障表现会放大成"全站超时""Pod 大面积 CrashLoop"这类级联问题,而核心组件本身往往没有直接报错。本文面向 K8s 平台运维工程师,基于 CoreDNS 官方指标(以 coredns_* 系列为准),给出从链路全景、核心指标、监控接入、告警规则到客户端优化与故障排查的完整实战体系。

适用对象:运行 CoreDNS 的 Kubernetes(含 RKE、RKE2、Kubeadm 等发行版)集群,监控栈以 Prometheus + Grafana 为准。文中 10.96.0.10(kube-dns Service ClusterIP)与 169.254.20.10(NodeLocal DNSCache 本地监听地址)为社区惯用示例值,按实际集群替换。

一、DNS 在 K8s 中的链路全景

1.1 解析链路

一次 Pod 内的域名解析,实际经过的路径如下:

text
应用进程(glibc resolver)
   │  读 /etc/resolv.conf:nameserver 10.96.0.10,options ndots:5

kube-dns Service(10.96.0.10:53,iptables/IPVS 转发)

CoreDNS Pod(插件链:kubernetes → cache → forward)
   │  集群内域名(*.cluster.local):kubernetes 插件直接应答
   │  缓存命中:cache 插件直接应答

上游 DNS(节点 /etc/resolv.conf 指向的内网 DNS 或公网 DNS)

部署 NodeLocal DNSCache 后,Pod 的查询先落到本节点的本地缓存,链路变为:

text
应用容器 → NodeLocal DNSCache(169.254.20.10)
            │  缓存命中:直接应答,不跨节点、不过 conntrack

          CoreDNS(10.96.0.10)→ 上游 DNS

1.2 多级缓存位置

DNS 结果会在多个层级被缓存,排查"解析到旧 IP"类问题时必须逐层确认:

层级缓存实现监控方式
应用层JVM DNS 缓存(networkaddress.cache.ttl)、Go resolver、Nginx resolver 等应用自身配置,无统一指标
Pod 节点层NodeLocal DNSCache(可选部署)coredns_* 指标(实例标签区分)
集群层CoreDNS cache 插件coredns_cache_hits_total
节点 OS 层systemd-resolved / nscd(多数容器宿主未启用)命令行查询,见 1.3

1.3 节点 OS 层缓存的监控说明

node_exporter 并不存在 systemd_resolve_cache_entriessystemd_resolve_queries_total 之类的指标(其 systemd 采集器只覆盖 systemd unit 状态,不含 systemd-resolved 缓存统计)。如需确认节点 OS 层 DNS 缓存情况,用命令行直接查询:

bash
# systemd-resolved 统计(缓存命中率、查询量)
resolvectl statistics

# 输出示例中的关键字段:
#   Current Cache Size: 42
#   Cache Hit: 12345 / Cache Miss: 678

如需指标化,可自行用 cron + node_exporter textfile 采集器把 resolvectl statistics 输出转成指标,或者干脆依赖 NodeLocal DNSCache 作为节点层唯一受控缓存(推荐做法)。

二、核心指标详解

CoreDNS 通过 prometheus :9153 插件暴露 Prometheus 指标。以下指标名均以 CoreDNS 官方文档为准,标签取常用子集。

2.1 请求量与响应指标

指标类型关键标签说明
coredns_dns_requests_totalCounterserverzonetype(A/AAAA/MX 等)、proto(udp/tcp)、familyDNS 请求总数,用于 QPS 与查询类型分布
coredns_dns_responses_totalCounterserverzonercode按响应码统计的响应数,错误率计算基础
coredns_dns_request_size_bytesHistogramserverzoneproto请求报文大小分布
coredns_dns_response_size_bytesHistogramserverzoneproto响应报文大小分布

关键响应码含义:

rcode含义常见诱因
NOERROR成功
NXDOMAIN域名不存在客户端查询错误域名、ndots 导致的 search 域遍历产生大量 NXDOMAIN
SERVFAIL服务器失败上游 DNS 故障、kubernetes 插件与 API Server 失联、递归循环
REFUSED拒绝查询ACL 拦截、查询类型不允许

2.2 延迟指标

指标类型说明
coredns_dns_request_duration_secondsHistogramCoreDNS 处理请求的耗时分布,p95/p99 是 DNS 性能的核心 SLO
coredns_forward_request_duration_secondsHistogramforward 插件向上游查询的耗时,区分"CoreDNS 慢"与"上游慢"
promql
# 集群 DNS p95 延迟
histogram_quantile(0.95, sum by (le) (rate(coredns_dns_request_duration_seconds_bucket[5m])))

# 上游转发 p95 延迟(高于总延迟说明瓶颈在上游)
histogram_quantile(0.95, sum by (le) (rate(coredns_forward_request_duration_seconds_bucket[5m])))

关于"按插件统计耗时":社区流传的 coredns_plugin_enabled_duration_seconds 并不是真实指标。CoreDNS 确实提供一个按插件统计耗时的直方图 coredns_plugin_request_duration_seconds(标签含 plugin),但它需要用带插件指标扩展的构建参数重新编译 CoreDNS,发行版官方镜像默认不开启。实际排查插件级瓶颈时,建议用对比法(分别看总延迟、forward 延迟、kubernetes 插件指标)定位,而非依赖插件耗时直方图。

2.3 缓存指标

指标类型说明
coredns_cache_hits_totalCounter缓存命中次数(标签 type:success/denial)
coredns_cache_misses_totalCounter缓存未命中次数
coredns_cache_entriesGauge当前缓存条目数
coredns_cache_evictions_totalCounter缓存条目被淘汰次数(突增说明缓存容量不足)
coredns_cache_served_stale_totalCounter提供过期记录的次数(评估 serve_stale 配置效果)
promql
# 缓存命中率
sum(rate(coredns_cache_hits_total[5m]))
/
(sum(rate(coredns_cache_hits_total[5m])) + sum(rate(coredns_cache_misses_total[5m])))

2.4 错误与稳定性指标

指标说明
coredns_panics_totalCoreDNS panic 次数,必须告警(>0 即为异常)
coredns_dns_responses_total{rcode=~"SERVFAIL|REFUSED"}失败响应,错误率计算入口(CoreDNS 没有单独的"失败总数"指标,失败按 rcode 从响应里算)
coredns_health_request_failures_total健康检查失败次数(需启用 health 插件)

2.5 转发(forward)插件指标

指标说明
coredns_forward_requests_total转发到上游的请求数(标签 to:上游地址)
coredns_forward_responses_total上游返回的响应数(标签 rcode,可算上游错误率)
coredns_forward_healthcheck_failures_total上游健康检查失败次数
coredns_forward_max_concurrent_rejects_total超过 max_concurrent 并发上限被拒绝的查询数,>0 说明并发配置不足或上游堆积
coredns_forward_conn_cache_hits_total / coredns_forward_conn_cache_misses_total上游连接复用情况

2.6 kubernetes 插件指标

指标说明
coredns_kubernetes_dns_programming_duration_seconds集群内记录变更(Endpoint 更新)到 DNS 可应答的延迟,反映 Service 变更生效速度

2.7 Go 运行时与进程指标(自动暴露)

指标说明
go_goroutinesGoroutine 数量,持续增长且不回落提示泄漏
go_memstats_alloc_bytes / process_resident_memory_bytes内存分配 / 常驻内存,监控 OOM 趋势
go_gc_duration_secondsGC 耗时,GC 停顿长会抬高解析延迟
process_cpu_seconds_totalCPU 使用量,配合 cgroup limit 计算使用率

2.8 KPI 阈值速览

指标建议阈值采集频率
请求成功率(NOERROR 占比)< 99.9% 告警1 分钟
p95 解析延迟> 100ms 告警1 分钟
缓存命中率< 85% 关注5 分钟
SERVFAIL 速率持续 > 0.1 QPS 告警1 分钟
panic 次数> 0 立即告警1 分钟
内存使用率(对 limit)> 80% 告警1 分钟
promql
# 请求成功率
sum(rate(coredns_dns_responses_total{rcode="NOERROR"}[5m]))
/
sum(rate(coredns_dns_requests_total[5m]))

三、监控接入实践

3.1 Corefile 启用 metrics

Kubeadm / RKE2 默认 Corefile 已带 prometheus :9153。完整参考配置:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods verified
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        cache 30 {
            prefetch 20 60s 15%
        }
        forward . /etc/resolv.conf {
            max_concurrent 1000
        }
        prometheus :9153
        loop
        reload
        loadbalance
    }

要点说明:

  • prometheus :9153:指标端点,必开。
  • cache 30:成功与否定缓存 TTL 30 秒;prefetch 20 60s 15% 表示热门记录在 TTL 消耗 15% 后预取刷新,降低缓存穿透时的延迟毛刺。
  • max_concurrent 1000:上游并发查询上限,配合 coredns_forward_max_concurrent_rejects_total 观察是否够用。
  • loop:检测转发循环,检测到会直接退出 Pod,日志里搜 loop 关键字。
  • log:全量查询日志,生产高 QPS 集群不建议常开(写 IO 与磁盘压力大),排障时临时开启。

3.2 Prometheus 抓取

使用 Prometheus Operator 的集群,用 ServiceMonitor 接入。先确认 kube-dns Service 已带 metrics 端口声明(Kubeadm 默认有 9153/TCP metrics):

yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: coredns
  namespace: kube-system
  labels:
    release: kube-prometheus-stack   # 与 Prometheus 实例的 serviceMonitorSelector 匹配
spec:
  selector:
    matchLabels:
      k8s-app: kube-dns
  endpoints:
    - port: metrics
      interval: 15s

非 Operator 环境直接在 scrape_configs 中配置(K8s 服务发现写法):

yaml
scrape_configs:
  - job_name: coredns
    kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names: [kube-system]
    relabel_configs:
      - source_labels: [__meta_kubernetes_service_label_k8s_app]
        regex: kube-dns
        action: keep
      - source_labels: [__meta_kubernetes_endpoint_port_name]
        regex: metrics
        action: keep

验证抓取成功:

bash
# 直接从 CoreDNS Pod 拉一次指标确认端点可用
kubectl exec -n kube-system deploy/coredns -- \
  wget -qO- http://localhost:9153/metrics | grep -E "coredns_dns_requests_total|coredns_cache_hits_total" | head

3.3 Grafana 面板

直接使用社区成熟面板导入(Grafana 市场 ID:5926 CoreDNS、15798 CoreDNS per Cluster),面板至少应覆盖以下视图:

面板组内容
DNS 性能概览查询 QPS、p50/p95/p99 延迟、错误率(按 rcode)、缓存命中率
上游转发上游 QPS、上游 p95 延迟、上游错误率、并发拒绝数
资源使用CPU/内存对 limit 的使用率、Goroutine 数、GC 耗时
客户端视角各命名空间/节点的 DNS QPS(需 NodeLocal DNSCache 或 ebpf 数据)

四、告警规则与阈值

以下 PrometheusRule 可直接套用(阈值按 2.8 节口径,按集群规模微调):

yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: coredns-alerts
  namespace: kube-system
  labels:
    release: kube-prometheus-stack
spec:
  groups:
    - name: coredns
      rules:
        - alert: CoreDNSInstanceDown
          expr: up{job=~".*coredns.*"} == 0
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: "CoreDNS 实例不可抓取:{{ $labels.instance }}"

        - alert: CoreDNSPanics
          expr: increase(coredns_panics_total[10m]) > 0
          for: 0m
          labels:
            severity: critical
          annotations:
            summary: "CoreDNS 出现 panic,实例:{{ $labels.instance }}"

        - alert: CoreDNSHighErrorRate
          expr: |
            sum(rate(coredns_dns_responses_total{rcode=~"SERVFAIL|REFUSED"}[5m]))
            /
            sum(rate(coredns_dns_responses_total[5m])) > 0.01
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "CoreDNS 错误响应占比超过 1%"

        - alert: CoreDNSHighServfail
          expr: sum(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m])) > 0.1
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "CoreDNS SERVFAIL 速率持续偏高,检查上游 DNS 与 kubernetes 插件"

        - alert: CoreDNSHighLatency
          expr: |
            histogram_quantile(0.95,
              sum by (le) (rate(coredns_dns_request_duration_seconds_bucket[5m]))) > 0.1
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "CoreDNS p95 解析延迟超过 100ms"

        - alert: CoreDNSForwardLatencyHigh
          expr: |
            histogram_quantile(0.95,
              sum by (le) (rate(coredns_forward_request_duration_seconds_bucket[5m]))) > 0.5
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "上游 DNS 转发 p95 延迟超过 500ms,瓶颈在上游"

        - alert: CoreDNSCacheHitRateLow
          expr: |
            sum(rate(coredns_cache_hits_total[15m]))
            /
            (sum(rate(coredns_cache_hits_total[15m])) + sum(rate(coredns_cache_misses_total[15m]))) < 0.7
          for: 30m
          labels:
            severity: info
          annotations:
            summary: "CoreDNS 缓存命中率低于 70%,检查 TTL 与查询模式"

        - alert: CoreDNSForwardConcurrentReject
          expr: increase(coredns_forward_max_concurrent_rejects_total[5m]) > 0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "CoreDNS 上游并发查询达到 max_concurrent 上限,出现拒绝"

        - alert: CoreDNSMemoryHigh
          expr: |
            max by (pod) (container_memory_working_set_bytes{namespace="kube-system", container="coredns"})
            /
            max by (pod) (kube_pod_container_resource_limits{namespace="kube-system", container="coredns", resource="memory"}) > 0.8
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "CoreDNS Pod {{ $labels.pod }} 内存使用超过 limit 的 80%"

阈值使用说明:

  • NXDOMAIN 升高通常不是 CoreDNS 故障(多为客户端查询错误域名或 ndots 副作用),不建议单独对 NXDOMAIN 做 critical 告警,放到面板观察即可。
  • DNSQuerySpike 类突增告警(rate(...[2m]) > rate(...[10m] offset 5m) * 3)可用于发现 DNS 放大攻击或失控应用,但抖动大的集群容易误报,建议先以 info 级别试运行。

五、客户端侧优化

服务端监控做得再好,客户端配置不合理依然会产生大量无效查询。这一节是"DNS 慢"问题最常见的根治手段。

5.1 ndots:5 的坑

Pod 默认 resolv.conf 为:

bash
kubectl exec <pod> -- cat /etc/resolv.conf
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5

ndots:5 的含义:域名中点号少于 5 个时,先依次拼接 4 个 search 域尝试解析,全部 NXDOMAIN 后才按原始域名查询。后果:

  • 集群内短域名(如 my-svc)解析要遍历 search 域,产生多次无效查询。
  • 解析外部域名(如 api.example.com,只有 2 个点)同样先遍历 search 域,一次外部解析放大成 5 次查询,CoreDNS QPS 与 NXDOMAIN 量翻倍。

优化手段(按优先级):

yaml
# 方式一:应用访问外部域名时结尾加点,直接命中"绝对域名"分支
# 例如把 api.example.com 写成 api.example.com.

# 方式二:对外部调用为主的负载,调低 ndots
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"

# 方式三:精简 search 域(谨慎,影响集群内短域名解析习惯)
spec:
  dnsConfig:
    searches:
      - default.svc.cluster.local
      - svc.cluster.local
      - cluster.local

注意:调低 ndots 后,集群内用短域名(如直接写 Service 名)的解析行为不变(仍会走 search 域补全),但外部域名优先按原始域名查询。全集群推广前先在单个 Deployment 上验证。

5.2 NodeLocal DNSCache 部署

NodeLocal DNSCache 以 DaemonSet 在每节点运行一个 CoreDNS 实例,监听 169.254.20.10:53,Pod 查询优先命中本地缓存,收益:

  • 降低 CoreDNS 集群压力与跨节点网络延迟。
  • 绕开 conntrack(本地链路地址不经过 iptables NAT 表),根治高并发 UDP DNS 查询导致的 conntrack 冲突与丢包(经典症状:间歇性 5 秒解析超时)。

安装方式(二选一):

bash
# 方式一:社区 Helm Chart(推荐,参数已模板化)
helm repo add deliveryhero https://charts.deliveryhero.io/
helm install node-local-dns deliveryhero/node-local-dns \
  -n kube-system \
  --set config.localDns=169.254.20.10 \
  --set config.kubeDnsIp=10.96.0.10

# 方式二:使用 kubernetes 仓库的清单模板
# 注意:必须下载 raw 文件,且模板中含 __PILLAR__DNS__SERVER__ 等占位变量,需先替换
curl -sSL https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml \
  -o nodelocaldns.yaml
# 替换变量(按集群实际值):
#   __PILLAR__DNS__SERVER__    → kube-dns ClusterIP,如 10.96.0.10
#   __PILLAR__LOCAL__DNS__     → 169.254.20.10
#   __PILLAR__DNS__DOMAIN__    → cluster.local
sed -i \
  -e 's/__PILLAR__DNS__SERVER__/10.96.0.10/g' \
  -e 's/__PILLAR__LOCAL__DNS__/169.254.20.10/g' \
  -e 's/__PILLAR__DNS__DOMAIN__/cluster.local/g' \
  nodelocaldns.yaml
kubectl apply -f nodelocaldns.yaml

不要对 GitHub 仓库的 blob 页面 URL 直接 kubectl apply -f,blob URL 返回的是 HTML 页面而非 YAML。

部署后让 Pod 使用本地 DNS:

yaml
spec:
  dnsConfig: {}
  dnsPolicy: None   # 只对需要走 NodeLocal 的负载设置

实际更通用的做法由发行版决定:部分发行版(如 RKE2 的 kubelet 配置)可全局把 kubelet 的 --cluster-dns 指到 169.254.20.10,无需逐个改 Pod。验证:

bash
kubectl get pods -n kube-system -l k8s-app=node-local-dns -o wide
kubectl exec <pod> -- cat /etc/resolv.conf   # nameserver 应为 169.254.20.10

5.3 应用层 DNS 配置最佳实践

配置建议
JVM-Dnetworkaddress.cache.ttl=60-Dnetworkaddress.cache.negative.ttl=10JVM 在 Security Manager 开启时默认永久缓存 DNS,必须显式设置 TTL
Go默认走 cgo 或纯 Go resolver纯 Go resolver 不走系统 nscd,TTL 行为依赖 CoreDNS 侧
Nginxresolver 10.96.0.10 valid=30s;不显式配置 resolver 时 Nginx 只在启动时解析一次 upstream
Pod 级dnsPolicy默认 ClusterFirst;hostNetwork Pod 如需集群内解析必须显式设为 ClusterFirstWithHostNet

5.4 DNS 基准压测

上线优化前后可用 dnsperf 做对比:

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: dns-benchmark
  namespace: kube-system
spec:
  schedule: "*/15 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: dnsperf
              image: infoblox/dnsperf:latest
              command:
                - /bin/sh
                - -c
                - |
                  echo "kubernetes.default.svc.cluster.local A" > /tmp/q.txt
                  dnsperf -s 10.96.0.10 -d /tmp/q.txt -l 30 -Q 100

六、故障排查实战

6.1 通用排查命令

bash
# CoreDNS 实例与日志
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100
kubectl logs -n kube-system -l k8s-app=kube-dns --previous   # 查重启前日志(OOM/panic)

# 临时开启查询日志(改 Corefile 加 log 插件,reload 自动生效)
kubectl edit configmap coredns -n kube-system

# 起一个带 DNS 工具的测试 Pod
kubectl run dns-test --rm -it --restart=Never \
  --image=registry.k8s.io/e2e-test-images/dnsutils:1.3 -- /bin/sh

# 抓包(网络调试容器,netshoot 自带 tcpdump/dig/conntrack)
kubectl debug -it <pod> --image=nicolaka/netshoot --target=<container>
# 进入后:
tcpdump -i any -nn port 53
dig @10.96.0.10 kubernetes.default.svc.cluster.local
conntrack -L | grep dport=53 | head

6.2 场景一:解析慢(p95 延迟升高)

排查路径:

  1. 看 CoreDNS 总延迟与上游转发延迟是否同步升高:coredns_forward_request_duration_seconds 升高 → 瓶颈在上游 DNS;不升高 → 瓶颈在 CoreDNS 自身或网络。
  2. 上游慢:检查 coredns_forward_responses_totalto 标签拆分,定位是哪个上游实例慢;节点侧 dig @<上游IP> test.com 直接验证。
  3. CoreDNS 自身慢:看 CPU 是否打满(rate(process_cpu_seconds_total[5m]) 对 limit)、GC 停顿(go_gc_duration_seconds)、缓存命中率是否骤降。
  4. 客户端侧慢但 CoreDNS 指标正常:八成是 ndots 问题或 conntrack 丢包——进业务 Pod 用 time nslookup 复现,并查节点 dmesg | grep conntrack

6.3 场景二:解析失败(SERVFAIL 升高)

排查路径:

  1. coredns_dns_responses_total 按 rcode 拆分,确认失败类型与涉及的 zone。
  2. 仅集群外域名 SERVFAIL:上游 DNS 不可达,验证 coredns_forward_healthcheck_failures_total,并检查 CoreDNS Pod 到上游的 53 端口连通性(NetworkPolicy、节点防火墙)。
  3. 集群内域名 SERVFAIL:kubernetes 插件异常,看 CoreDNS 日志是否有 connection refused(API Server 失联)或大量 loop 关键字(转发循环)。
  4. NXDOMAIN 大面积出现:先确认是否业务在查错误域名;若是 ndots 副作用属预期,配合 5.1 节优化。

6.4 场景三:CoreDNS OOM / 频繁重启

排查路径:

bash
kubectl describe pod -n kube-system -l k8s-app=kube-dns   # 看 Last State 是否 OOMKilled
kubectl top pod -n kube-system -l k8s-app=kube-dns
  1. 内存随 QPS 线性增长:给足 resources limits(大集群建议 512Mi-1Gi),并按 QPS 水平扩容副本(默认 2 副本对大集群明显不足)。
  2. coredns_cache_entries 持续走高:缓存配置过大,收紧 cache 容量(cache 30 默认上限 10000 条成功缓存)。
  3. go_goroutines 只涨不跌:疑似 goroutine 泄漏,升级 CoreDNS 版本并关注 changelog。
  4. 大集群治本:开启 CoreDNS 的 autoscale(cluster-proportional-autoscaler),按节点数/核数自动调整副本数。

6.5 场景四:上游 DNS 故障

  1. coredns_forward_healthcheck_failures_totalcoredns_forward_responses_total{rcode="SERVFAIL"} 确认故障上游。
  2. 若 Corefile 用 forward . /etc/resolv.conf,上游即节点 resolv.conf 内容——检查节点 DNS 配置是否被 DHCP/云控制台改动。
  3. 临时止血:把 forward 改为多个稳定上游(forward . 223.5.5.5 119.29.29.29),CoreDNS 默认轮询+健康检查自动摘除故障上游。
  4. 事后根治:内网上游 DNS 做主备,CoreDNS 侧配置 serve_stale(缓存插件)在上游全挂时继续应答过期记录兜底。

6.6 客户端无差别慢/间歇 5 秒超时

这是经典 conntrack 冲突场景(UDP 并发查询同五元组竞争插入 conntrack 失败,内核丢弃后 glibc 默认 5 秒重试):

bash
# 节点上确认 conntrack 表与丢包
dmesg | grep -i "conntrack"
cat /proc/sys/net/netfilter/nf_conntrack_count /proc/sys/net/netfilter/nf_conntrack_max

根治方案即 5.2 节的 NodeLocal DNSCache(本地链路地址不过 conntrack);临时缓解可让高 DNS QPS 的应用改用 TCP 查询(options use-vc)。

6.7 高级手段:eBPF 观测

需要按进程/Pod 维度统计 DNS 延迟与失败时,不必手写 bcc 探针脚本(易错且内核版本敏感),直接用成熟工具:

bash
# Cilium 集群:Hubble 直接观测 DNS 流量
hubble observe --to-port 53 --verdict FORWARDED -f

# Pixie:内置 DNS 流量统计脚本
px run px/dns_data

两者均可输出按 Pod/域名维度的查询量、延迟、响应码,是定位"哪个应用在疯狂查 DNS"的利器。

七、故障速查表

故障现象可能原因排查与处理
全集群应用访问外部接口间歇性超时上游 DNS 故障或延迟高coredns_forward_* 指标与延迟;CoreDNS 配置多上游轮询,必要时启用 serve_stale
Pod 内解析间歇性卡住 5 秒UDP 查询 conntrack 冲突丢包,glibc 5 秒重试节点 dmesg 查 conntrack;部署 NodeLocal DNSCache 根治,或应用开启 TCP 查询
CoreDNS QPS 与 NXDOMAIN 量是预期数倍ndots:5 导致 search 域遍历外部域名结尾加点,或按 5.1 节调低 ndots
CoreDNS Pod OOMKilled副本/内存 limit 不足,缓存过大调大 memory limit、增加副本;大集群上 cluster-proportional-autoscaler
CoreDNS 日志大量 loop 后退出转发循环(上游又指回 CoreDNS/kube-dns)检查节点 resolv.conf 是否指向集群 DNS;修正上游配置
集群内 Service 解析 SERVFAILkubernetes 插件与 API Server 失联,或 RBAC 异常看 CoreDNS 日志;验证 CoreDNS Pod 到 API Server 连通性与 ClusterRole
改了 Service/Endpoints 后解析不生效CoreDNS 缓存 TTL 未过期,或应用层缓存(JVM 永久缓存)等 TTL(默认 30s)或重启 CoreDNS;应用层按 5.3 节设置 DNS TTL
hostNetwork Pod 无法解析集群内域名hostNetwork 下 dnsPolicy 默认走节点 DNSdnsPolicy 显式设为 ClusterFirstWithHostNet
某个命名空间 DNS QPS 异常高失控应用循环查询错误域名用 Hubble/Pixie 按 Pod 维度定位;限制应用重试逻辑
指标抓取不到 CoreDNSCorefile 缺 prometheus :9153,或 ServiceMonitor 标签不匹配检查 Corefile;确认 ServiceMonitor 的 selector 与 Prometheus 实例匹配
NodeLocal DNSCache 部署后 Pod 仍走 10.96.0.10kubelet cluster-dns 未切换,仅装了 DaemonSet 没生效检查 kubelet --cluster-dns 配置或发行版对应参数,确认 Pod resolv.conf 指向 169.254.20.10
DNS 延迟高但 CoreDNS 指标全部正常瓶颈在客户端到 CoreDNS 的网络路径(节点防火墙、CNI、conntrack)netshoot 抓包对比发起/到达时间;查节点防火墙规则与 conntrack 表