DNS 深入:CoreDNS / resolv.conf / ndots / 解析坑
K8s 集群内一半的"网络问题"其实是 DNS 问题——而 90% 的 DNS 问题都源于:客户端不正确解读
/etc/resolv.conf、ndots:5的隐藏代价、CoreDNS forward 链断了。这一篇把 K8s DNS 从客户端 resolver 一路追到 CoreDNS、再到上游、再到生产坑。
这篇要回答什么
- 为什么
kubectl exec pod -- dig my-svc比dig my-svc.default.svc.cluster.local慢? ndots:5是什么神奇配置?为什么是生产事故的常客?- CoreDNS 把"我不认识"的域名给谁?
- 短域名 / 长域名 / FQDN 哪个最快?为什么?
- Pod 解析
kubernetes这种短词时,到底真发了几次 DNS 查询? - "DNS 偶发性超时"通常是哪里坏了?
1. 一次完整 DNS 解析的"实际"路径
Pod 跑 curl http://my-svc:8080。看似简单,实际背后:
sequenceDiagram
autonumber
participant App
participant Resolver as glibc resolver
participant ResolvConf as /etc/resolv.conf
participant CoreDNS
participant K8sAPI as K8s API store
participant Upstream as 上游 DNS<br>(节点 /etc/resolv.conf)
App->>Resolver: getaddrinfo("my-svc")
Resolver->>ResolvConf: 读配置<br>nameserver / search / options
Note over Resolver: search: default.svc.cluster.local<br> svc.cluster.local<br> cluster.local<br>options ndots:5
Note over Resolver: "my-svc" 点数 = 0 < 5<br>→ 先拼 search domain
Resolver->>CoreDNS: my-svc.default.svc.cluster.local. A?
CoreDNS->>K8sAPI: 查 Service "my-svc" in "default"
K8sAPI-->>CoreDNS: ClusterIP 10.96.1.5
CoreDNS-->>Resolver: NOERROR, A=10.96.1.5
Resolver-->>App: 10.96.1.5
App->>App: connect(10.96.1.5:8080)
这是顺利的情况。my-svc 拼 default ns 的 search domain 就找到了。
如果是访问外部域名 github.com 呢?
sequenceDiagram
autonumber
participant App
participant Resolver as glibc resolver
participant CoreDNS
participant K8sAPI
participant Upstream as 上游 DNS
App->>Resolver: getaddrinfo("github.com")
Note over Resolver: 点数 = 1 < 5<br>→ 先拼 search domain
Resolver->>CoreDNS: github.com.default.svc.cluster.local. A?
CoreDNS->>K8sAPI: ❌ 不是 K8s service
Note over CoreDNS: K8s plugin 不命中<br>→ 转给 forward plugin
CoreDNS->>Upstream: github.com.default.svc.cluster.local. A?
Note over Upstream: 上游也找不到这个奇怪域名
Upstream-->>CoreDNS: NXDOMAIN
CoreDNS-->>Resolver: NXDOMAIN
Note over Resolver: 第 1 个 search 失败、试第 2 个
Resolver->>CoreDNS: github.com.svc.cluster.local. A?
CoreDNS->>Upstream: github.com.svc.cluster.local. A?
Upstream-->>CoreDNS: NXDOMAIN
CoreDNS-->>Resolver: NXDOMAIN
Note over Resolver: 试第 3 个
Resolver->>CoreDNS: github.com.cluster.local. A?
CoreDNS-->>Resolver: NXDOMAIN
Note over Resolver: 终于试不带 search 的原名
Resolver->>CoreDNS: github.com. A?
CoreDNS->>Upstream: github.com. A?
Upstream-->>CoreDNS: A=140.82.114.4
CoreDNS-->>Resolver: A=140.82.114.4
Resolver-->>App: 140.82.114.4
这就是 ndots:5 的代价
解析一个外部域名 github.com 需要 4 次 DNS 查询——前 3 次都是 NXDOMAIN。
每次查询 IPv4 + IPv6(A + AAAA)共 8 次。集群内域名前缀短 + DNS 上游慢 = 你的应用启动慢、外网慢、各种偶发性超时。
K8s 生产事故 #1 来源就是这里。
2. /etc/resolv.conf 配置详解
看 pod 的 resolv.conf
$ kubectl exec my-pod -- cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
每行的含义:
nameserver 10.96.0.10
去问哪个 DNS server。多个 nameserver = 故障转移:
nameserver 10.96.0.10
nameserver 10.96.0.11
默认行为:先问 10.96.0.10、超时(默认 5 秒)后问 10.96.0.11。不是负载均衡。
search default.svc.cluster.local svc.cluster.local cluster.local
短名补全的后缀列表。按顺序尝试:
my-svc→my-svc.default.svc.cluster.local→ 如果失败 →my-svc.svc.cluster.local→ 失败 →my-svc.cluster.local→ 都失败 → 用原名my-svc
options ndots:5
关键参数。规则:
如果查询的域名 含点数 < ndots:
先拼 search domain 试
都失败再用原名
否则 (含点数 >= ndots):
直接用原名
K8s 默认 ndots:5 = "含点数 < 5 就先拼 search"。
github.com(1 个点)< 5 → 先拼 search → 4 次查询。
a.b.c.d.e.f(5 个点)≥ 5 → 直接查 → 1 次查询。
google.com.(末尾点 = FQDN)→ 直接查、跳过 search → 1 次。
options timeout:2 attempts:3 single-request-reopen
| 选项 | 含义 |
|---|---|
timeout:N | 等 N 秒(默认 5) |
attempts:N | 重试次数(默认 2) |
rotate | 多 nameserver 时轮询(不再"先用第一个") |
single-request | A 和 AAAA 串行查(默认并行) |
single-request-reopen | A/AAAA 各自用独立 socket(glibc bug workaround) |
use-vc | 强制 TCP(默认 UDP) |
3. ndots:5 的真相 + 优化
为什么 K8s 默认 ndots:5
K8s 团队想让 pod 里 curl http://my-svc 直接通——配 search 让它自动补成 my-svc.default.svc.cluster.local。
但 ndots:1 就够(my-svc 0 个点 < 1 也会触发 search)。为什么是 5?
历史原因:早期 K8s 用 KubeDNS、不同 plugin 行为有些差异。ndots:5 是个兼容性"保险"值——保证各种短名都走 search。
副作用:每个外部域名 4-8 次查询
绝大多数生产事故来源:
# 应用大量调用外部 API
curl https://api.example.com # 1 个点 < 5 → 4 次 NXDOMAIN
curl https://github.com # 1 个点 < 5 → 4 次 NXDOMAIN
curl https://prom.example.com # 2 个点 < 5 → 4 次 NXDOMAIN
每秒成千次 API 调用 → CoreDNS 被淹 → 应用启动慢 / 偶发性 timeout / NXDOMAIN 日志爆炸。
解决方案对比
$ curl https://github.com. # ← 末尾点表示 FQDN,跳过 search
应用层修改、但很多 HTTP 库不支持 / 配置麻烦。
spec:
dnsConfig:
options:
- name: ndots
value: "2"
ndots:2 = 含点数 < 2 才拼 search。github.com(1 个点)< 2 还是会拼 search、但只少了一些场景。
推荐 ndots:1:
dnsConfig:
options:
- name: ndots
value: "1"
my-svc(0 个点)< 1 仍走 search(OK)、github.com(1 个点)≥ 1 不走 search → 1 次查询。
每个节点起一个 DNS 缓存 daemon、pod 查 169.254.20.10(节点本地 IP)。
# 装 NodeLocal DNSCache
kubectl apply -f nodelocaldns.yaml
好处:
- 查询不走 kube-proxy → 减少 iptables 开销
- 节点本地 cache → 重复查询直接命中
- CoreDNS 压力降 80%+
详见 K8s 官方文档 nodelocaldns。
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 10.96.0.10
searches:
- default.svc.cluster.local
options:
- name: ndots
value: "2"
完全自定义 resolv.conf。适合知道自己在做什么的情况。
4. CoreDNS 工作机制
看 CoreDNS 配置
$ kubectl get cm -n kube-system coredns -o yaml
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
逐 plugin 解读:
| Plugin | 作用 |
|---|---|
errors | 错误打日志 |
health | /health 健康检查端点 |
ready | /ready 就绪检查 |
kubernetes cluster.local | K8s 集成:解析 *.cluster.local 走 K8s API |
prometheus :9153 | 暴露 metrics |
forward . /etc/resolv.conf | 不认识的域名转发给节点上游 DNS |
cache 30 | 缓存 30 秒(减少重复查询) |
loop | 检测 DNS 转发循环 |
reload | 配置改了自动 reload |
loadbalance | 多 A 记录时随机顺序(伪负载均衡) |
Plugin 链:先 kubernetes、再 forward
┌─────────────────┐
│ Corefile │
│ .:53 │
└────────┬────────┘
│
查询到达 CoreDNS
│
▼
┌─────────────────────────┐
│ kubernetes plugin │ ← 先问这里
│ "是 *.cluster.local?" │
└──────┬──────────────┬───┘
▼ ▼
YES NO (fallthrough)
│ │
▼ ▼
从 K8s API ┌────────────┐
返回 ClusterIP │ forward │
│ /etc/resolv.conf
└─────┬──────┘
▼
节点上游 DNS
(运营商 / 8.8.8.8 / 公司 DNS)
CoreDNS forward 的"上游"是什么
forward . /etc/resolv.conf {
max_concurrent 1000
}
. 表示"匹配所有"。/etc/resolv.conf 是 CoreDNS pod 看到的 resolv.conf —— 但是!
CoreDNS pod 跑在 K8s 节点上,但它的 dnsPolicy: Default(不是 ClusterFirst)。所以它看到的是节点的 resolv.conf(通过 hostNetwork 或 mount)。
# CoreDNS pod 里看的 resolv.conf
$ kubectl exec -n kube-system coredns-xxx -- cat /etc/resolv.conf
nameserver 8.8.8.8
nameserver 1.1.1.1
节点的 DNS 配置 → CoreDNS 用这个上游。
显式指定上游(生产推荐)
不要靠 /etc/resolv.conf 上游、显式配:
forward . 1.1.1.1 8.8.8.8 {
max_concurrent 1000
}
或者强烈推荐用阿里 / 腾讯 / 公司内部 DNS:
forward . 223.5.5.5 119.29.29.29 {
max_concurrent 1000
prefer_udp
}
为啥重要:
- 节点
/etc/resolv.conf可能配错 / 改动 - 公网 DNS 速度参差不齐
- 显式配置更可控、可监控
5. K8s DNS 域名规范
Service / Pod DNS 命名
⟨service⟩.⟨namespace⟩.svc.⟨cluster-domain⟩
例子:
| 域名 | 含义 |
|---|---|
kubernetes.default.svc.cluster.local | default ns 的 kubernetes service |
prom.monitoring.svc.cluster.local | monitoring ns 的 prom service |
短形式(依赖 search domain):
my-svc # 在 default ns 的 pod 里
my-svc.other-ns # 在 default ns 里访问 other-ns
my-svc.other-ns.svc # 同上
⟨ip-with-dashes⟩.⟨namespace⟩.pod.⟨cluster-domain⟩
例子:
10.244.0.5 → 10-244-0-5.default.pod.cluster.local
但这是默认不工作的——要 CoreDNS 配 pods 才行(默认 pods insecure 开了)。
⟨pod-name⟩.⟨headless-service⟩.⟨namespace⟩.svc.⟨cluster-domain⟩
StatefulSet + Headless Service:
$ kubectl get statefulset
NAME READY AGE
mysql 3/3 1h
$ kubectl get pod -l app=mysql
mysql-0 1/1
mysql-1 1/1
mysql-2 1/1
$ kubectl get svc mysql -o jsonpath='{.spec.clusterIP}'
None # Headless
# DNS 都通:
mysql-0.mysql.default.svc.cluster.local
mysql-1.mysql.default.svc.cluster.local
mysql-2.mysql.default.svc.cluster.local
这是为什么 StatefulSet 需要 Headless Service——稳定的 pod DNS 名。
Headless Service 的 SRV 记录
$ kubectl exec test-pod -- dig SRV _http._tcp.my-headless.default.svc.cluster.local
;; ANSWER SECTION:
_http._tcp.my-headless.default.svc.cluster.local. 30 IN SRV 0 33 8080 10-244-0-5.my-headless.default.svc.cluster.local.
_http._tcp.my-headless.default.svc.cluster.local. 30 IN SRV 0 33 8080 10-244-1-6.my-headless.default.svc.cluster.local.
_http._tcp.my-headless.default.svc.cluster.local. 30 IN SRV 0 33 8080 10-244-2-7.my-headless.default.svc.cluster.local.
SRV 记录返回端口 + 主机名——SDK / 客户端可以解析它自己做 LB。Kafka / Cassandra / Zookeeper 客户端依赖这种 SRV 自动发现。
6. 排查 DNS 解析问题
第 0 步:CoreDNS 是不是健康
$ kubectl get pod -n kube-system -l k8s-app=kube-dns
NAME READY STATUS AGE
coredns-7b6c8df-abc123 1/1 Running 1d
coredns-7b6c8df-def456 1/1 Running 1d
$ kubectl logs -n kube-system coredns-7b6c8df-abc123 --tail=30
异常信号(要查):
[ERROR] plugin/forward: ... no responding upstream
[ERROR] plugin/errors: ... NXDOMAIN ...
[INFO] Reloading
第 1 步:直接问 CoreDNS
$ kubectl exec test-pod -- nslookup kubernetes.default
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
# 或者用 dig(更详细)
$ kubectl exec test-pod -- dig kubernetes.default.svc.cluster.local @10.96.0.10
响应 = CoreDNS 工作正常。
第 2 步:测某 Service
$ kubectl exec test-pod -- dig my-svc.my-ns.svc.cluster.local @10.96.0.10
;; QUESTION SECTION:
;my-svc.my-ns.svc.cluster.local. IN A
;; ANSWER SECTION:
my-svc.my-ns.svc.cluster.local. 30 IN A 10.96.1.5
;; status: NOERROR
NOERROR + ANSWER SECTION 非空 = 解析成功。
如果 NXDOMAIN:
- Service 名拼错 / namespace 错
- Service 真的不存在(
kubectl get svc -n my-ns my-svc)
第 3 步:测搜索域是不是按预期工作
# 在 pod 里
$ kubectl exec test-pod -- cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
# 模拟 ndots:5 + search 的拼接
$ kubectl exec test-pod -- nslookup my-svc
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: my-svc.default.svc.cluster.local
Address: 10.96.1.5
显示真实解析到的是 my-svc.default.svc.cluster.local —— search 加上去的。
第 4 步:测上游
# 在 pod 里测外部域名
$ kubectl exec test-pod -- dig github.com @10.96.0.10
;; ANSWER SECTION:
github.com. 60 IN A 140.82.114.4
# 也可以直接问节点上游
$ kubectl exec test-pod -- dig github.com @8.8.8.8
如果 CoreDNS 解析外部域名失败、但 @8.8.8.8 直接解析成功 → CoreDNS forward 配置 / 上游 DNS 节点连不通。
# 看 CoreDNS pod 自己能不能解析外部
$ kubectl exec -n kube-system coredns-xxx -- dig github.com
第 5 步:抓 DNS 包
# 节点上抓 pod 的 DNS 流量
$ PID=$(crictl inspect $(crictl ps -q --name test-pod) | jq '.info.pid')
$ nsenter -t $PID -n tcpdump -i any -nn 'port 53' -A
# 触发解析
$ kubectl exec test-pod -- dig my-svc
# 看抓到的包
输出会显示完整的 DNS 查询和响应内容。能看清楚:
- 哪些 search domain 被尝试了
- CoreDNS 回应是什么
- 是否有重传
7. 反面教材合集
反面 1:以为 service 名加 ns 一定通
$ kubectl exec pod-in-default -- curl http://my-svc.other-ns
# Could not resolve host
my-svc.other-ns 拼 search 后变成 my-svc.other-ns.default.svc.cluster.local——default ns 里没这个 svc。
正确写法:
my-svc.other-ns.svc.cluster.local # FQDN
my-svc.other-ns.svc # 拼 search 也通
养成习惯:跨 ns 一律 .svc.cluster.local 全写。
反面 2:以为 dnsPolicy: Default 跟节点一致就好
spec:
dnsPolicy: Default # ← 用节点 resolv.conf
pod 用节点 /etc/resolv.conf,不走 CoreDNS。结果:
- 集群内 Service 解析全断(
my-svc找不到) - 只能访问公网
适用:CoreDNS / 其它系统 pod、不需要集群内服务。业务 pod 永远不要 dnsPolicy: Default。
反面 3:CoreDNS 缓存 stale 数据
$ kubectl get svc my-svc -o jsonpath='{.spec.clusterIP}'
10.96.1.5 # 当前 IP
$ kubectl delete svc my-svc
$ kubectl apply -f my-svc.yaml # 重建、IP 变了
$ kubectl get svc my-svc -o jsonpath='{.spec.clusterIP}'
10.96.2.5 # 新 IP
# 但 pod 还用老 IP
$ kubectl exec test-pod -- dig my-svc
# 10.96.1.5 ← cache stale
CoreDNS cache 30 = 缓存 30 秒。改了 Service 之后等 30 秒或调小:
cache 5
或者关 cache(不推荐生产)。
反面 4:用 NodeLocal DNSCache 不当 nameserver 还指 CoreDNS
装了 NodeLocal DNSCache 之后、pod 的 resolv.conf 没改:
$ kubectl exec my-pod -- cat /etc/resolv.conf
nameserver 10.96.0.10 # ← 还指 CoreDNS、不是 NodeLocal
NodeLocal 装了但没生效——pod 仍走 CoreDNS、白装。
修:
# kubelet 配置(每个节点)
clusterDNS:
- 169.254.20.10 # NodeLocal DNS IP
或者用 pod-level 的 dnsConfig。重启 kubelet 生效。
反面 5:misconfigured search 引发 NXDOMAIN 雪崩
$ cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local example.com internal.example.com prod.example.com
options ndots:5
search 加了一堆"为了方便短名"——但每个外部域名查询要先试 6 个 search!
github.com→github.com.default.svc.cluster.local(NXDOMAIN)github.com.svc.cluster.local(NXDOMAIN)github.com.cluster.local(NXDOMAIN)github.com.example.com(NXDOMAIN)github.com.internal.example.com(NXDOMAIN)github.com.prod.example.com(NXDOMAIN)github.com.(NOERROR)
- 共 7 次查询 × A + AAAA = 14 个包
生产事故:CoreDNS 被 NXDOMAIN 雪崩淹没、所有 pod DNS 解析慢。
修:
- 删 search 里非 K8s 域名
- 或者每个 pod
dnsConfig.options.ndots=1 - 或者 NodeLocal DNSCache 拦住雪崩
反面 6:AAAA 查询 timeout 拖累
应用同时查 A + AAAA(IPv4 + IPv6)。IPv6 没启用 / 上游不支持 AAAA → AAAA 查询超时(5 秒)→ 应用启动慢。
$ kubectl exec test-pod -- dig AAAA github.com
;; connection timed out; no servers could be reached # ← timeout
修:
- 应用层禁 IPv6 解析(Go:
GODEBUG=netdns=cgo+1/ Node.js:--dns-result-order=ipv4first) - pod resolv.conf 加
options single-request-reopen - CoreDNS forward 上游用支持 AAAA 的 server
反面 7:CoreDNS forward 配错让 *.cluster.local 也走外面
forward . 8.8.8.8 1.1.1.1 # ← 直接配上游、缺 K8s plugin 处理
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
如果 kubernetes plugin 没在 forward 前面 → cluster.local 也会被 forward 出去。
plugin 顺序很重要——kubernetes 必须在 forward 之前。
8. 排查 cheatsheet
"DNS 解析慢"5 步排查
flowchart TD
Start[应用 DNS 慢] --> S1{1. pod 直接<br>dig 集群内 svc 慢?}
S1 -->|否, 内部快| S2{2. dig 外部域名慢?}
S2 -->|外部慢| C1[CoreDNS forward 上游问题]
S1 -->|内部慢| S3{3. dig @CoreDNS IP 直连?}
S3 -->|慢| C2[CoreDNS pod 本身慢/挂]
S3 -->|快| C3[pod 到 CoreDNS<br>网络问题]
S2 -->|外部快| S4{4. 看 ndots / search 雪崩?}
S4 -->|是| C4[NXDOMAIN 雪崩<br>降 ndots 或加 NodeLocal]
S4 -->|否| S5[5. 看应用 DNS<br>实现细节<br>cgo vs go vs glibc]
几个有用的诊断命令
# 直接测
kubectl exec test-pod -- time dig kubernetes.default
# 总解析时间
kubectl exec test-pod -- dig kubernetes.default | grep "Query time"
# 节点上抓 pod 的 DNS 包
PID=$(crictl inspect $(crictl ps -q --name test-pod) | jq '.info.pid')
nsenter -t $PID -n tcpdump -i any -nn 'port 53' -A &
# 触发
kubectl exec test-pod -- nslookup github.com
# 看抓包结果:发了几次 DNS 查询、search 试了几个、上游 forward 了哪些
# 临时开 debug 日志
kubectl edit cm -n kube-system coredns
# 在 Corefile 加: log
kubectl rollout restart deploy -n kube-system coredns
# 看日志
kubectl logs -n kube-system coredns-xxx -f
# 查询率
sum by (server, type) (rate(coredns_dns_requests_total[1m]))
# NXDOMAIN 率(**雪崩信号**)
sum by (server) (rate(coredns_dns_responses_total{rcode="NXDOMAIN"}[1m]))
# 上游 forward 延迟
histogram_quantile(0.99, sum by (le, to) (rate(coredns_forward_request_duration_seconds_bucket[5m])))
9. 生产实战 case study
Case 1:pod 启动慢
现象:业务 pod 启动到 ready 需要 60 秒,本地测试 5 秒搞定。
排查:
$ kubectl exec my-pod -- time curl https://api.example.com
# real 8m20.xxxs ← 8 分钟!
$ kubectl exec my-pod -- time dig api.example.com
# Query time: 8200 ms ← DNS 慢
$ kubectl exec my-pod -- cat /etc/resolv.conf
options ndots:5
search default.svc.cluster.local ... example.com ... prod.example.com ← 6 个 search domain
根因:search 里有 6 个 domain × ndots:5 → 解析 api.example.com 要发 7 次查询 + IPv6 timeout × 6 → 8 分钟。
修法:
spec:
dnsConfig:
options:
- name: ndots
value: "1"
启动时间从 60 秒 → 5 秒。
Case 2:CoreDNS 偶发性挂
现象:每隔 1-2 小时业务 5xx 飙、几秒后恢复。
排查:
$ kubectl get events -n kube-system | grep coredns
... Killing coredns-xxx (OOMKilled) ← OOM 了!
$ kubectl top pods -n kube-system | grep coredns
coredns-xxx 100m 170Mi/170Mi ← 内存满
根因:CoreDNS 默认 memory limit 170Mi 太小,集群规模大 + cache 多了打满。
修法:
$ kubectl edit deploy -n kube-system coredns
# resources.limits.memory: 512Mi
或者:
cache 10 # 缩短缓存(少占内存)
Case 3:外网偶尔不通
现象:访问外部 API 间歇性 timeout。
排查:
$ kubectl exec my-pod -- for i in 1 2 3 4 5; do dig +timeout=2 github.com; done
# 第 1、4 次失败、其它通
# 看 CoreDNS forward 上游
$ kubectl exec -n kube-system coredns-xxx -- dig github.com
# 也间歇性失败
# 节点上看
$ ssh m1 'cat /etc/resolv.conf'
nameserver 192.168.1.1 # ← 老路由器、不稳
$ ssh m1 'dig github.com @192.168.1.1'
# 间歇性失败
根因:节点 DNS(运营商提供)不稳。
修法:CoreDNS 显式配上游:
forward . 223.5.5.5 119.29.29.29 8.8.8.8 {
max_concurrent 1000
policy random # 随机选上游
health_check 5s # 健康检查
}
Case 4:StatefulSet pod 互相找不到
现象:mysql-0 找不到 mysql-1。
排查:
$ kubectl exec mysql-0 -- dig mysql-1.mysql.default.svc.cluster.local
;; ANSWER SECTION:
mysql-1.mysql.default.svc.cluster.local. 30 IN A 10.244.1.6 ← 解析对了
$ kubectl exec mysql-0 -- ping 10.244.1.6
# 通
# 但应用配置文件用的是 mysql-1(短名)
$ kubectl exec mysql-0 -- nslookup mysql-1
# Could not resolve ← 短名解析失败
根因:短名 mysql-1 被 search 拼成 mysql-1.default.svc.cluster.local → 这是 mysql ns 的 service 短名错。
修法:应用配置用完整名 mysql-1.mysql.default.svc.cluster.local 或 mysql-1.mysql。
Case 5:CoreDNS 内存暴涨
现象:CoreDNS pod 内存持续涨、最终 OOM。
排查:
sum by (server) (rate(coredns_dns_requests_total[1m]))
# 突然涨到几万 qps
根因:某个业务 pod bug 死循环查询 / 误装了 dnsConfig 加了一堆 nameserver、导致 CoreDNS 被打挂。
修法:
- 找出 noisy pod、限流 / 修复
- CoreDNS 加
cache 60增缓存 - 装 NodeLocal DNSCache 隔离
10. 下一步
| 文档 | 内容 |
|---|---|
| 00-mental-model.md | 网络心智模型 |
| 01-output-reading.md | 看懂命令输出(含 dig 详解) |
| 02-namespaces.md | 容器网络底层 |
| 03-k8s-network-deep.md | K8s Service / Ingress |
| 本篇 | DNS / CoreDNS / ndots 深入 |
| 05-troubleshooting.md | 生产网络故障排查 |