K8s CoreDNS
这一课你将能做到
- 写出集群内服务域名的完整形态与四种简写规则,并用 Pod 内
nslookup验证 - 读懂 CoreDNS 的 Corefile(CoreDNS ConfigMap),解释每个插件(kubernetes、forward、cache、logs)的作用
- 排查集群内典型解析问题:ndots 拖慢、外部域名超时、CoreDNS 负载不均、5 秒延迟
为什么要学
把应用搬上 Kubernetes 后,DNS 的角色比在虚拟机时代更重:Pod IP 随时漂移,服务之间不靠 IP 互相找,而靠 Service 名字——http://backend:8080 这样的调用能成立,全靠集群里的 CoreDNS。它为每个 Service 自动生成 名字.命名空间.svc.cluster.local 的记录,还负责把集群外域名转发给上游递归器。
初学者在 K8s 遇到的「服务偶发超时」「外部接口时快时慢」,相当部分是 CoreDNS 配置(ndots、缓存)问题。本课讲透集群解析机制与调优。
先建立图像
Pod (dnsPolicy: ClusterFirst)
│ /etc/resolv.conf 指向 CoreDNS Service:10.96.0.10
▼
CoreDNS(Deployment + Service kube-system/coredns)
│ Corefile 决定怎么回答
├─ 集群内名字(*.cluster.local)
│ └─ kubernetes 插件直接读 API Server 的 Service/Pod 数据回答
└─ 集群外名字(www.coderoadmap.cn 等)
└─ forward 插件转发给节点上游 DNS(如 223.5.5.5 / 云内网DNS),cache 插件缓存
服务域名规则(以 default 命名空间的 backend 服务为例):
完整形态:backend.default.svc.cluster.local
同命名空间:backend ← 最常用
跨命名空间:backend.default
Pod 也有名字:10-244-1-5.default.pod.cluster.local(默认不开,需配置)
| 名词 | 人话解释 |
|---|---|
| CoreDNS | K8s 默认集群 DNS(1.11 起,替代 kube-dns),Go 编写、插件化 |
| ClusterIP | Service 的虚拟 IP,DNS 返回的就是它(如 backend → 10.96.x.x) |
| Headless Service | 无 ClusterIP,DNS 直接返回各 Pod IP(StatefulSet 场景) |
| ndots | 名字里点的数量低于此值就先按 search 后缀逐个补全尝试 |
| dnsPolicy | Pod 的 DNS 策略:ClusterFirst / Default / None |
跟着做
步骤 1:查看集群 DNS 与 Pod 的 resolv.conf
kubectl get svc -n kube-system kube-dns
kubectl run dns-test --image=busybox:1.36 -it --rm -- sh
Pod 内执行:
cat /etc/resolv.conf
示例输出:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
三行含义:查询全部发给 CoreDNS;先按本命名空间、svc、cluster.local 的顺序补后缀;ndots:5 是后面要讲的「慢」的根源。
步骤 2:解析 Service 的四种写法
# 在 default 命名空间的测试 Pod 里
nslookup backend
nslookup backend.default
nslookup backend.default.svc
nslookup backend.default.svc.cluster.local
示例输出:
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: backend.default.svc.cluster.local
Address 1: 10.96.7.21 backend.default.svc.cluster.local
跨命名空间调用必须写全到命名空间级,如 nslookup kubernetes.default。
步骤 3:看 CoreDNS 实际配置
kubectl -n kube-system get cm coredns -o yaml
默认 Corefile 示例:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
插件逐个解释:
| 插件 | 作用 |
|---|---|
| kubernetes | 监听 API Server,Service/Pod 数据直接成答(非区域文件) |
| forward | 非集群域名转发给上游(节点 /etc/resolv.conf 或指定公共 DNS) |
| cache | 缓存 30 秒(集群内记录 TTL 通常很短,外部记录按其 TTL) |
| loadbalance | 对多条 A 轮询排序 |
| pods insecure | 允许 Pod IP 反查 |
| ready/health | 就绪与存活探针 |
步骤 4:观察 Headless Service 的差异
kubectl create service clusterip mysvc --clusterip=None
kubectl get svc mysvc
对普通 Service,DNS 返回单个 ClusterIP;对 Headless Service,CoreDNS 直接返回所有就绪 Pod IP:
# 普通 Service
mysvc.default.svc.cluster.local → 10.96.7.21
# Headless(StatefulSet:还带每副本稳定名字 web-0.mysvc、web-1.mysvc)
web-0.mysvc.default.svc.cluster.local → 10.244.1.5
web-1.mysvc.default.svc.cluster.local → 10.244.2.8
步骤 5:理解 ndots:5 与搜索后缀,量化额外查询
# 在 Pod 中解析一个只有一个点的外部名(如 api.partner.com)
nslookup api.partner.com
由于 api.partner.com 只有 1 个点 < ndots:5,解析器不会直接查它,而是依次尝试:
api.partner.com.default.svc.cluster.local → NXDOMAIN
api.partner.com.svc.cluster.local → NXDOMAIN
api.partner.com.cluster.local → NXDOMAIN
api.partner.com → 成功
3 次无效查询,丢包时还要等超时——这是外部调用偶发慢的常见原因。两种解法:
# 解法 1:应用直接写 FQDN 末尾加点,强制按绝对名查(最省事)
url: https://api.partner.com./v1
# 解法 2:给工作负载单独调低 ndots
apiVersion: v1
kind: Pod
spec:
dnsConfig:
options:
- name: ndots
value: "2"
containers: [ ... ]
步骤 6:CoreDNS 扩容与排障三板斧
# 1) 看 CoreDNS 是否有压力/错误日志(先开日志插件)
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
# 2) 大规模集群默认副本不足,调大(或配合 autoscaler)
kubectl -n kube-system scale deploy coredns --replicas=3
# 3) Pod 内直接对比「CoreDNS」与「公共DNS」,区分转发问题
nslookup www.coderoadmap.cn 10.96.0.10
# 在能直连公网 DNS 的节点上
nslookup www.coderoadmap.cn 223.5.5.5
补充:节点 /etc/resolv.conf 指向的 DNS 不稳时 CoreDNS 也会抖;生产建议 forward 直接写云厂商内网 DNS 或多个公共 DNS 做 failover。
讲清楚要点
- 集群内 DNS 答案来自 API Server 实时数据,不是预生成文件:Service 创建后几乎立即可解析,不存在 TTL 传播问题。
- Pod 拿到的是 CoreDNS ClusterIP:靠 iptables/IPVS 规则负载均衡到 CoreDNS 副本;Pod 多、查询密集时要保证副本数与节点分布(Pod 反亲和打散)。
- ndots:5 + search 列表是 K8s 解析慢的头号配置:理解「短名先补后缀、绝对名直接查」,能解释大量偶发超时。
- 外部解析问题要分层:Pod→CoreDNS(UDP 是否被 NetworkPolicy 拦)、CoreDNS→上游(forward 目标是否可达、conntrack 丢包)、上游本身。
- conntrack/UDP race 会造成约 5 秒延迟:高并发下 SNAT 端口竞争丢 UDP 包,解析器重传正好 5 秒;修复靠调优 conntrack、部署 NodeLocal DNSCache 与扩 CoreDNS。
- NodeLocal DNSCache 是大集群标配:节点本地 DaemonSet 缓存,减少到 CoreDNS 的 UDP 跳转,显著降低 5 秒延迟。
- dnsPolicy 要会选:默认 ClusterFirst;宿主机网络 Pod 常用 Default;需要完全自定义时用 None + dnsConfig。
你可能会懵的地方
-
Service 刚删掉,为什么 DNS 有时还返回旧 ClusterIP? CoreDNS 的 cache 插件默认缓存 30 秒(成功与否定都缓存)。等缓存过期;在缩发布、频繁重建同名服务时尤为明显。
-
为什么 Pod 里 ping 不通
cluster.local? cluster.local 是域后缀不是主机;要 ping 具体名字,如ping backend.default.svc.cluster.local。而且 CoreDNS 本身只应答查询,不响应 ICMP。 -
nslookup 正常、Java 应用却解析失败? JVM 默认缓存 DNS 结果(旧版默认永久缓存,可用
networkaddress.cache.ttl);且 Java 不走 glibc search 的某些行为、老版本对 ndots 处理不同。容器里设-Dnetworkaddress.cache.ttl=30并确认 dnsPolicy。 -
CoreDNS Pod 挂了集群内调用全断? DNS 是集群基础设施,必须冗余并配 PodDisruptionBudget;Service 后没有就绪端点时解析立即全失败。
-
forward
. /etc/resolv.conf里的点代表什么? CoreDNS 语法中 forward 的第一个参数是匹配的域,.匹配所有(即默认转发所有非集群域);后面是上游地址,写文件表示从该文件读上游。
错法对照
| 错法 | 后果 | 正确做法 |
|---|---|---|
| 应用写死 Service ClusterIP | 重建集群/Service 后全线崩 | 用服务名,让 DNS 解析 |
| 跨命名空间只写服务名 | 补后缀只在本命名空间找,NXDOMAIN | 写 服务.命名空间 或 FQDN |
| 忽略 ndots,短外部名多 | 每域名 3~4 次无效查询,偶发超时 | FQDN 加点或调低 ndots |
| CoreDNS 副本长期默认 2 不扩 | 查询排队、UDP 丢包、5 秒延迟 | 按规模扩副本 + NodeLocal DNSCache |
| NetworkPolicy 只放 80/443 | Pod 到 CoreDNS 的 UDP 53 被拦 | 放行到 kube-dns:53 TCP/UDP |
卡点急救
- 集群内服务全部偶发解析超时:先看 CoreDNS Pod 是否全部就绪、是否被调度在同一节点;再看日志有无大量 SERVFAIL;立即扩容并检查节点 conntrack 表是否满(
dmesg | grep conntrack)。 - 只有外部域名解析慢/失败:在 CoreDNS Pod 内直接
nslookup 外部名 上游IP判断上游健康;检查 forward 配置与节点 DNS;必要时把 forward 改为显式多个公共/内网 DNS。 - 固定出现约 5 秒卡顿后成功:典型 UDP 丢包重传。部署 NodeLocal DNSCache、调大 conntrack 表、确认 CoreDNS 副本与节点反亲和,而不是去改业务代码。
面试常问
-
Kubernetes 集群里 Service 的域名是怎么解析的? Pod 的 resolv.conf 指向 CoreDNS Service(默认 10.96.0.10),并配置 search 后缀与 ndots。查
服务.命名空间.svc.cluster.local时,CoreDNS 的 kubernetes 插件直接从 API Server 的 Service 对象取 ClusterIP 返回;Headless Service 则返回各 Pod IP。非集群域由 forward 插件转发上游,cache 插件做缓存。 -
ndots 是什么?为什么会导致解析变慢? resolv.conf 里的 ndots 设定阈值:域名中点的数量达到阈值才当作绝对域名直接查询,否则先依次拼上 search 列表(命名空间.svc、svc、cluster.local)尝试,全失败后才查原始名。默认 ndots:5 让大多数只有一两个点的外部域名多出 3 次 NXDOMAIN 查询,链路差或丢包时延迟明显。用 FQDN 末尾加点、调低 ndots 或上 NodeLocal DNSCache 解决。
-
什么是 K8s 中著名的「5 秒 DNS 延迟」? 高并发场景下 Pod 经 SNAT 访问 CoreDNS 的 UDP 包因 conntrack 竞态被丢弃,glibc/musl 解析器重传间隔恰为 5 秒,表现为请求稳定卡 5 秒后成功。社区标准对策是 NodeLocal DNSCache(节点本地缓存避免跨网络 UDP/SNAT)、调 conntrack、扩 CoreDNS;TCP 模式也可规避但非默认。
自测三问
- 写出 default 命名空间中 redis 服务的四种域名形态;跨命名空间最少要写全到哪一级?
- CoreDNS 收到
www.coderoadmap.cn和backend.default.svc.cluster.local分别走哪条插件路径? - 应用日志中大量「调用合作方接口固定慢 5 秒」,你按哪三步定位到 DNS 层?
小结一句话
CoreDNS 用 kubernetes 插件把 Service 对象实时变成集群内域名答案,用 forward+cache 接外部世界;ndots、conntrack 和副本数是集群解析性能的三个命门。
延伸
- Kubernetes 官方:Pod 与 Service 的 DNS
- Kubernetes 官方:使用 NodeLocal DNSCache
- CoreDNS 官方文档与插件手册
- CoreDNS kubernetes 插件说明
- 下一课预告:常见故障——NXDOMAIN、传播延迟、解析超时、5 秒延迟的系统排障法。