深色模式
CoreDNS 与集群 DNS 排障
摘要:本文解释 CoreDNS 如何为集群提供 Service 名称解析,剖析
ndots:5陷阱与间歇性 5 秒延迟根因,并给出从 Pod 到 CoreDNS 到上游解析器的分层排障清单。覆盖版本:Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(CoreDNS 为默认 DNS,通过
kube-dnsService 暴露)。 - 工具:
kubectl、dig/nslookup(netshoot 镜像)、节点 shell。 - 前提:理解 Service 与 Headless(见
service.md、headless-service.md)。
背景与问题
应用里几乎不会硬编码 Pod IP,而是用名字(mysql、api.backend)。这些名字能解析,靠的是集群内的 CoreDNS。它正常运行时无人关注;一旦异常,典型症状是:间歇性 5 秒延迟、某些名字在一个命名空间能解析另一个不能、外部域名查询放大。本文目的是让你在出问题时能快速定位。
核心机制
- CoreDNS 以 Deployment(通常 2 副本) 运行在
kube-system,由 Service 名kube-dns暴露一个稳定 ClusterIP(常见10.96.0.10)。 - kubelet 在创建每个 Pod 时,把
/etc/resolv.conf的nameserver指向该 ClusterIP——这就是整个集成的全部。 - CoreDNS 通过 watch Service 与 EndpointSlice,在内存里维护集群域名;其他域名(如
example.com)转发给节点上游解析器(forward . /etc/resolv.conf)。
Corefile(ConfigMap coredns in kube-system)最关键的两行:
text
.:53 {
kubernetes cluster.local in-addr.arpa ip6.arpa { ... } # 回答集群域名
forward . /etc/resolv.conf # 其余转发上游
cache 30
}1
2
3
4
5
2
3
4
5
每个对象对应什么记录
| 对象 | 记录类型 | 解析到 |
|---|---|---|
| 普通 ClusterIP Service | A | ClusterIP |
| Headless Service | A(多条) | 每个 Ready Pod IP |
| StatefulSet Pod | A | 该 Pod IP(稳定名) |
| ExternalName Service | CNAME | 外部域名 |
反射 readiness
DNS 反映 Pod 的 readiness:未通过就绪探针的 Pod 不会出现在 Headless Service 的 DNS 答案里。“DNS 查不到”常常等于“Pod 没 Ready”,而非 DNS 本身坏了。
ndots:5 陷阱
kubelet 写入的 resolv.conf 典型内容:
text
nameserver 10.96.0.10
search production.svc.cluster.local svc.cluster.local cluster.local
options ndots:51
2
3
2
3
ndots:5 的含义:名字中点数少于 5 时,先依次尝试所有 search 后缀,再当作绝对名查询。于是查询 api.stripe.com(2 个点)会先试 4 个错误域名,最后才查真名——每次外部查询都多花 4 倍查询量,在高频调用下明显抬升 CoreDNS 负载与尾延迟。
缓解(按优先级):
- 外部名加结尾点:
api.stripe.com.是完全限定名,跳过 search 列表。 - 降低 ndots:对主要访问外部的服务设
dnsConfig.options.ndots: "2"。 - NodeLocal DNSCache:在每节点跑 DNS 缓存 DaemonSet,把重复查询挡在 CoreDNS 之外(大规模/重 DNS 集群标配)。
yaml
spec:
dnsConfig:
options:
- name: ndots
value: "2"1
2
3
4
5
2
3
4
5
间歇性 5 秒延迟
经典症状:偶发正好约 5 秒的 DNS 延迟。历史根因之一是内核 conntrack 与并行 A/AAAA 查询竞争 UDP 丢包导致的解析器重试超时;NodeLocal DNSCache 是标准缓解手段。也可能是 CoreDNS 自身过载或上游慢。
排障清单
步骤 1:确认 CoreDNS 健康
bash
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get svc kube-dns -n kube-system
kubectl logs -n kube-system -l k8s-app=kube-dns --since=5m1
2
3
2
3
步骤 2:从 Pod 内验证解析
bash
kubectl run dns-test --rm -it --image=nicolaka/netshoot -- bash
cat /etc/resolv.conf
dig myservice.default.svc.cluster.local +short
dig @10.96.0.10 google.com +short1
2
3
4
2
3
4
步骤 3:查 CoreDNS 配置与上游
bash
kubectl get configmap coredns -n kube-system -o yaml
# 重点看 forward 指向;若指向 /etc/resolv.conf,需确认节点上游可用1
2
2
步骤 4:NetworkPolicy 是否挡了 53
若集群启用了默认拒绝出站(见 networkpolicy.md),必须放行到 CoreDNS 的 53 端口,否则解析全面失败。
步骤 5:抓包(节点侧)
bash
# 在节点上抓 DNS 流量
tcpdump -n -i any port 53 -vv1
2
2
常见故障模式
| 现象 | 可能原因 | 处置 |
|---|---|---|
| CoreDNS CrashLoop | Loop detected(转发到自己) | 修正节点 /etc/resolv.conf 不要指向 ClusterIP |
| 集群名能解析、外部名不行 | forward 上游不可达 | 改 forward 为明确上游(如 8.8.8.8) |
| 间歇性 5 秒延迟 | ndots / conntrack / CoreDNS 过载 | 降 ndots、上 NodeLocal DNSCache、扩 CoreDNS |
| 某命名空间解析不到 | search 域未覆盖 / 跨 ns 需写全名 | 使用 svc.ns 全名 |
生产实践
- HPA CoreDNS:按 CPU 设
minReplicas:2 maxReplicas:10,避免单点过载。 - 启用 NodeLocal DNSCache:大规模或 DNS 重负载集群强烈建议。
- 监控指标:CoreDNS 在
:9153暴露 Prometheus 指标(请求数、缓存命中、解析失败率)。 - 避免
pods insecure过度暴露:pods模式决定是否允许按 Pod IP 反查,按需设置。
回滚与清理
bash
# 调试 Pod 退出即删除(--rm)
# 修改 CoreDNS ConfigMap 后通常自动 reload;异常时滚动重启
kubectl rollout restart deployment/coredns -n kube-system1
2
3
2
3
生产危险
不要直接把节点 /etc/resolv.conf 指向 CoreDNS ClusterIP,否则 CoreDNS 转发上游时形成自循环(Loop detected)。CoreDNS 应使用节点真实上游。
安全与合规
- DNS 是服务发现基础,CoreDNS 被压制会级联影响全网;应通过 HPA 与资源 limits 保障其容量。
- 解析内容不含认证;敏感服务仍要靠 NetworkPolicy/mTLS 保护。
常见坑
- 用
ping测 Service 名:ClusterIP 不响应 ICMP,请用dig/curl。 - 跨命名空间用短名:
api只在同 ns 解析,跨 ns 要写api.other-ns。 - 改了 CoreDNS 配置没生效:确认 ConfigMap 名称与挂载一致,并等待 reload 或重启。
替代方案与权衡
- NodeLocal DNSCache:不是替代 CoreDNS,而是其前置缓存,降低延迟与 CoreDNS 压力。
- 自定义上游 / 私有域:通过 CoreDNS
forward或k8s.guide的hosts/私有 zone 插件接入企业内网 DNS。