主题
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)→ 上游 DNS1.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_entries、systemd_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_total | Counter | server、zone、type(A/AAAA/MX 等)、proto(udp/tcp)、family | DNS 请求总数,用于 QPS 与查询类型分布 |
coredns_dns_responses_total | Counter | server、zone、rcode | 按响应码统计的响应数,错误率计算基础 |
coredns_dns_request_size_bytes | Histogram | server、zone、proto | 请求报文大小分布 |
coredns_dns_response_size_bytes | Histogram | server、zone、proto | 响应报文大小分布 |
关键响应码含义:
| rcode | 含义 | 常见诱因 |
|---|---|---|
| NOERROR | 成功 | — |
| NXDOMAIN | 域名不存在 | 客户端查询错误域名、ndots 导致的 search 域遍历产生大量 NXDOMAIN |
| SERVFAIL | 服务器失败 | 上游 DNS 故障、kubernetes 插件与 API Server 失联、递归循环 |
| REFUSED | 拒绝查询 | ACL 拦截、查询类型不允许 |
2.2 延迟指标
| 指标 | 类型 | 说明 |
|---|---|---|
coredns_dns_request_duration_seconds | Histogram | CoreDNS 处理请求的耗时分布,p95/p99 是 DNS 性能的核心 SLO |
coredns_forward_request_duration_seconds | Histogram | forward 插件向上游查询的耗时,区分"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_total | Counter | 缓存命中次数(标签 type:success/denial) |
coredns_cache_misses_total | Counter | 缓存未命中次数 |
coredns_cache_entries | Gauge | 当前缓存条目数 |
coredns_cache_evictions_total | Counter | 缓存条目被淘汰次数(突增说明缓存容量不足) |
coredns_cache_served_stale_total | Counter | 提供过期记录的次数(评估 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_total | CoreDNS 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_goroutines | Goroutine 数量,持续增长且不回落提示泄漏 |
go_memstats_alloc_bytes / process_resident_memory_bytes | 内存分配 / 常驻内存,监控 OOM 趋势 |
go_gc_duration_seconds | GC 耗时,GC 停顿长会抬高解析延迟 |
process_cpu_seconds_total | CPU 使用量,配合 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" | head3.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:5ndots: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.105.3 应用层 DNS 配置最佳实践
| 层 | 配置 | 建议 |
|---|---|---|
| JVM | -Dnetworkaddress.cache.ttl=60、-Dnetworkaddress.cache.negative.ttl=10 | JVM 在 Security Manager 开启时默认永久缓存 DNS,必须显式设置 TTL |
| Go | 默认走 cgo 或纯 Go resolver | 纯 Go resolver 不走系统 nscd,TTL 行为依赖 CoreDNS 侧 |
| Nginx | resolver 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 | head6.2 场景一:解析慢(p95 延迟升高)
排查路径:
- 看 CoreDNS 总延迟与上游转发延迟是否同步升高:
coredns_forward_request_duration_seconds升高 → 瓶颈在上游 DNS;不升高 → 瓶颈在 CoreDNS 自身或网络。 - 上游慢:检查
coredns_forward_responses_total按to标签拆分,定位是哪个上游实例慢;节点侧dig @<上游IP> test.com直接验证。 - CoreDNS 自身慢:看 CPU 是否打满(
rate(process_cpu_seconds_total[5m])对 limit)、GC 停顿(go_gc_duration_seconds)、缓存命中率是否骤降。 - 客户端侧慢但 CoreDNS 指标正常:八成是 ndots 问题或 conntrack 丢包——进业务 Pod 用
time nslookup复现,并查节点dmesg | grep conntrack。
6.3 场景二:解析失败(SERVFAIL 升高)
排查路径:
coredns_dns_responses_total按 rcode 拆分,确认失败类型与涉及的 zone。- 仅集群外域名 SERVFAIL:上游 DNS 不可达,验证
coredns_forward_healthcheck_failures_total,并检查 CoreDNS Pod 到上游的 53 端口连通性(NetworkPolicy、节点防火墙)。 - 集群内域名 SERVFAIL:kubernetes 插件异常,看 CoreDNS 日志是否有
connection refused(API Server 失联)或大量loop关键字(转发循环)。 - 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- 内存随 QPS 线性增长:给足 resources limits(大集群建议 512Mi-1Gi),并按 QPS 水平扩容副本(默认 2 副本对大集群明显不足)。
coredns_cache_entries持续走高:缓存配置过大,收紧 cache 容量(cache 30默认上限 10000 条成功缓存)。go_goroutines只涨不跌:疑似 goroutine 泄漏,升级 CoreDNS 版本并关注 changelog。- 大集群治本:开启 CoreDNS 的 autoscale(cluster-proportional-autoscaler),按节点数/核数自动调整副本数。
6.5 场景四:上游 DNS 故障
coredns_forward_healthcheck_failures_total与coredns_forward_responses_total{rcode="SERVFAIL"}确认故障上游。- 若 Corefile 用
forward . /etc/resolv.conf,上游即节点 resolv.conf 内容——检查节点 DNS 配置是否被 DHCP/云控制台改动。 - 临时止血:把 forward 改为多个稳定上游(
forward . 223.5.5.5 119.29.29.29),CoreDNS 默认轮询+健康检查自动摘除故障上游。 - 事后根治:内网上游 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 解析 SERVFAIL | kubernetes 插件与 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 默认走节点 DNS | dnsPolicy 显式设为 ClusterFirstWithHostNet |
| 某个命名空间 DNS QPS 异常高 | 失控应用循环查询错误域名 | 用 Hubble/Pixie 按 Pod 维度定位;限制应用重试逻辑 |
| 指标抓取不到 CoreDNS | Corefile 缺 prometheus :9153,或 ServiceMonitor 标签不匹配 | 检查 Corefile;确认 ServiceMonitor 的 selector 与 Prometheus 实例匹配 |
| NodeLocal DNSCache 部署后 Pod 仍走 10.96.0.10 | kubelet cluster-dns 未切换,仅装了 DaemonSet 没生效 | 检查 kubelet --cluster-dns 配置或发行版对应参数,确认 Pod resolv.conf 指向 169.254.20.10 |
| DNS 延迟高但 CoreDNS 指标全部正常 | 瓶颈在客户端到 CoreDNS 的网络路径(节点防火墙、CNI、conntrack) | netshoot 抓包对比发起/到达时间;查节点防火墙规则与 conntrack 表 |