“网络不通”——这是 SRE 最常接到的告警之一。而在所有”网络不通”的故障中,DNS 问题占了相当大的比例。DNS 超时导致的服务不可用、NXDOMAIN 导致的连接失败、SERVFAIL 导致的间歇性错误、DNS 劫持导致的流量被截获——每一种故障的根因和排查路径都不一样。
大多数工程师对 DNS 排查的认知停留在 ping 和
nslookup。但真实的 DNS
故障需要更系统化的方法论和更精确的工具链。
本文构建一个完整的 DNS 故障排查框架:从现象分类到工具选择,从快速定位到根因分析,从应急处理到预防措施。
一、DNS 故障分类
1.1 四大故障类型
DNS 故障类型:
1. 超时(Timeout)
现象: DNS 查询无响应,应用挂起或延迟飙升
常见原因: 递归解析器不可达、防火墙阻断、UDP 被丢弃
2. NXDOMAIN(域名不存在)
现象: 域名解析返回"不存在"
常见原因: 域名拼写错误、域名未注册/已过期、DNS 记录配置错误
3. SERVFAIL(服务器失败)
现象: 递归解析器返回服务器内部错误
常见原因: 权威服务器故障、DNSSEC 验证失败、上游超时
4. 劫持(Hijacking)
现象: DNS 返回了错误的 IP 地址
常见原因: ISP 劫持、中间人攻击、缓存投毒、恶意软件
1.2 快速分类流程
DNS 故障快速分类:
dig example.com @8.8.8.8
│
├─ 无响应 → 超时问题 → 转第二节
│
├─ status: NXDOMAIN → 域名不存在 → 转第三节
│
├─ status: SERVFAIL → 服务器故障 → 转第四节
│
├─ status: REFUSED → 服务器拒绝 → 转第四节
│
├─ status: NOERROR, 但 IP 不对 → 劫持 → 转第五节
│
└─ status: NOERROR, IP 正确 → 问题在客户端
→ 检查本地 resolv.conf / nsswitch.conf / hosts 文件
二、DNS 超时排查
2.1 确认超时层级
DNS 超时可能发生在多个层级:
排查路径(由近及远):
1. 本地到递归解析器
dig @<递归解析器IP> example.com +time=2 +tries=1
→ 如果超时,问题在本地网络或递归解析器
2. 递归解析器到权威服务器
dig @<权威服务器IP> example.com +time=2 +tries=1
→ 如果成功,问题在递归解析器的上游链路
3. 多个递归解析器交叉验证
dig @8.8.8.8 example.com +time=2
dig @1.1.1.1 example.com +time=2
dig @208.67.222.222 example.com +time=2
→ 如果所有解析器都超时,问题可能是权威服务器故障
→ 如果只有部分超时,问题是特定解析器或路径
2.2 常见超时原因
# 原因一: 本地 DNS 配置错误
cat /etc/resolv.conf
# 检查 nameserver 是否可达
ping -c 1 <nameserver_ip>
# 原因二: 防火墙阻断 UDP 53
# 某些防火墙会限制或阻断 DNS UDP 流量
# 尝试 TCP 查询
dig @8.8.8.8 example.com +tcp +time=5
# 如果 TCP 成功但 UDP 超时 → UDP 被防火墙阻断
# 原因三: MTU 问题导致大 DNS 包被丢弃
# DNSSEC 或 EDNS0 响应可能超过 MTU
dig @8.8.8.8 example.com +bufsize=512 +time=5
# 限制 EDNS0 缓冲区大小,避免大包
dig @8.8.8.8 example.com +noedns +time=5
# 完全禁用 EDNS0
# 原因四: 递归解析器过载
# 检查解析器的负载指标(如果有权限)
ss -ulnp | grep :53
# 查看 DNS 端口的连接队列
# 原因五: conntrack 表满(常见于高流量环境)
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 如果 count 接近 max → conntrack 表满导致新 DNS 包被丢弃2.3 超时参数调优
# /etc/resolv.conf 的超时相关参数
nameserver 8.8.8.8
nameserver 1.1.1.1
options timeout:2 # 每次查询超时 2 秒(默认 5 秒)
options attempts:3 # 每个 nameserver 重试 3 次(默认 2 次)
options rotate # 轮询多个 nameserver(而非只用第一个)
# 总超时时间 = timeout × attempts × nameserver数量
# 默认: 5 × 2 × 2 = 20 秒
# 优化后: 2 × 3 × 2 = 12 秒
# 但要注意: 缩短超时可能导致在高延迟网络中
# 本应成功的查询被提前放弃三、NXDOMAIN 排查
3.1 确认 NXDOMAIN 来源
# 第一步: 确认域名是否真的不存在
dig example.com @8.8.8.8
# status: NXDOMAIN → 在 Google DNS 也不存在
# 第二步: 查询权威服务器
dig example.com NS
# 找到权威服务器
dig example.com @ns1.example.com
# 如果权威服务器也返回 NXDOMAIN → 域名确实不存在或记录未配置
# 第三步: 检查域名注册状态
whois example.com
# 查看域名是否已注册、是否过期
# Status: ok / clientTransferProhibited → 正常
# Status: redemptionPeriod → 域名已过期,在赎回期
# 无结果 → 域名未注册3.2 常见 NXDOMAIN 原因
原因一: 域名拼写错误
查询: exmaple.com(拼错了)
修正: example.com
原因二: 域名过期
whois 显示 Expiry Date 已过
→ 联系注册商续费
原因三: DNS 记录未配置
dig www.example.com @ns1.example.com → NXDOMAIN
dig example.com @ns1.example.com → NOERROR
→ www 子域的 A/CNAME 记录未添加
原因四: NS 委托错误
dig example.com NS → ns1.wrongprovider.com
→ 域名的 NS 记录指向了错误的 DNS 服务商
→ 在注册商处检查并修正 NS 记录
原因五: search domain 未生效
resolv.conf:
search internal.company.com
查询 "redis" 应该扩展为 redis.internal.company.com
但如果 search 行缺失或格式错误 → 直接查询 "redis" → NXDOMAIN
3.3 否定缓存导致的”持久” NXDOMAIN
# 场景: 刚添加了 DNS 记录,但还是返回 NXDOMAIN
# 原因: 否定缓存(Negative Caching)
# 之前的 NXDOMAIN 被递归解析器缓存了
# 缓存 TTL 由 SOA 记录的 minimum 字段决定
dig example.com SOA
# example.com. 86400 IN SOA ns1.example.com. admin.example.com. (
# 2025080101 3600 900 604800 300 )
# ^^^ 否定缓存 TTL = 300 秒
# 解决方案:
# 1. 等待否定缓存过期(这里是 300 秒 = 5 分钟)
# 2. 使用其他递归解析器验证(没有缓存的解析器)
dig @1.1.1.1 newrecord.example.com
# 3. 如果你管理递归解析器,可以手动清除缓存
rndc flush # BIND
unbound-control flush newrecord.example.com # Unbound四、SERVFAIL 排查
4.1 SERVFAIL 的含义
SERVFAIL(RCODE=2)表示递归解析器在处理查询时遇到了错误。
关键理解:
SERVFAIL ≠ 权威服务器返回了错误
SERVFAIL = 递归解析器告诉你"我无法完成这个查询"
可能的原因:
- 递归解析器无法到达权威服务器
- 权威服务器返回了畸形响应
- DNSSEC 验证失败
- 递归解析器内部错误
- 查询超时(递归到权威的超时)
4.2 SERVFAIL 诊断流程
# 步骤一: 确认是否所有递归解析器都返回 SERVFAIL
dig example.com @8.8.8.8 # Google
dig example.com @1.1.1.1 # Cloudflare
dig example.com @208.67.222.222 # OpenDNS
# 全部 SERVFAIL → 权威服务器问题或 DNSSEC 问题
# 部分 SERVFAIL → 特定递归解析器问题
# 步骤二: 直接查询权威服务器
# 先找到权威服务器
dig example.com NS +norecurse @8.8.8.8
# 然后直接查询
dig example.com @ns1.example.com
# 如果权威服务器正常返回 → 问题在递归解析器端
# 步骤三: 检查 DNSSEC
# 禁用 DNSSEC 验证(Checking Disabled)
dig example.com @8.8.8.8 +cd
# 如果 +cd 返回 NOERROR 但不加 +cd 返回 SERVFAIL
# → DNSSEC 验证失败
# 步骤四: 追踪完整解析链路
dig example.com +trace
# 逐级显示从根到权威的查询
# 在哪一级失败就是哪一级的问题4.3 DNSSEC 导致的 SERVFAIL
# DNSSEC 验证失败是 SERVFAIL 的高频原因
# 诊断:
dig example.com @8.8.8.8 +dnssec
# 查看返回的 DNSSEC 记录
# 使用 delv 做详细验证
delv @8.8.8.8 example.com A +rtrace +vtrace
# 输出会显示验证在哪一步失败
# 常见原因:
# 1. RRSIG 签名过期
dig example.com A +dnssec +noall +answer | grep RRSIG
# 检查 RRSIG 中的过期时间
# 2. DS 记录与 DNSKEY 不匹配
dig example.com DS +short # 父域的 DS
dig example.com DNSKEY +short # 本域的 DNSKEY
# 用 dnssec-dsfromkey 验证是否匹配
# 3. 算法不支持
dig example.com DNSKEY +short | awk '{print $3}'
# 检查算法编号,确认递归解析器是否支持
# 紧急修复: 如果你是域名管理员
# 选项 A: 修复签名(重新签名区域文件)
# 选项 B: 删除父域的 DS 记录(降级到非 DNSSEC)
# 注意: 这需要等待 DS TTL 过期4.4 权威服务器故障导致的 SERVFAIL
# 检查所有权威服务器的可达性和响应
dig example.com NS +short
# ns1.example.com.
# ns2.example.com.
# 逐个测试
for ns in $(dig example.com NS +short); do
echo "=== $ns ==="
dig example.com @$ns +time=3 +tries=1 +noall +stats 2>&1 | \
grep -E "status|Query time|connection timed"
done
# 如果所有 NS 都不可达 → 权威服务器全部故障
# 如果部分 NS 可达 → 部分故障,检查不可达 NS 的网络/服务状态
# 检查权威服务器端口是否开放
nc -zuv ns1.example.com 53 2>&1
# Connection to ns1.example.com 53 port [udp/domain] succeeded!五、DNS 劫持检测与排查
5.1 劫持类型
DNS 劫持的三种类型:
1. ISP 劫持(最常见)
- ISP 拦截 DNS 查询,返回自己的应答
- 通常用于: 广告注入、NXDOMAIN 重定向到搜索页
- 检测方法: 对比 ISP DNS 和公共 DNS 的结果
2. 中间人劫持
- 攻击者(如公共 WiFi 运营者)拦截 DNS 流量
- 将特定域名解析到恶意服务器
- 检测方法: 查询结果与已知正确 IP 不符
3. 缓存投毒
- 攻击者向递归解析器注入虚假记录
- 影响所有使用该解析器的客户端
- 检测方法: 对比多个递归解析器的结果
5.2 劫持检测方法
# 方法一: 多解析器交叉对比
echo "=== 对比多个 DNS 解析器的结果 ==="
for dns in 8.8.8.8 1.1.1.1 208.67.222.222 9.9.9.9; do
result=$(dig @$dns www.example.com +short +time=3 2>/dev/null)
echo "$dns: $result"
done
# 如果所有解析器返回相同 IP → 大概率正确
# 如果本地解析器的结果不同 → 可能被劫持
# 方法二: 直接查询权威服务器
# 获取权威 NS
AUTH_NS=$(dig example.com NS +short | head -1)
# 查询权威服务器
AUTH_IP=$(dig @$AUTH_NS www.example.com +short)
# 对比本地解析
LOCAL_IP=$(dig www.example.com +short)
echo "权威: $AUTH_IP"
echo "本地: $LOCAL_IP"
# 不一致 → 可能被劫持
# 方法三: 使用 HTTPS 验证(DoH)
# 绕过本地 DNS,直接通过 HTTPS 查询
curl -s "https://dns.google/resolve?name=www.example.com&type=A" | \
python3 -c "import sys,json; d=json.load(sys.stdin); \
print('DoH结果:', [a['data'] for a in d.get('Answer',[])])"
# 方法四: 检查 NXDOMAIN 劫持
# 查询一个确定不存在的域名
dig @<local_dns> thisdomaindoesnotexist12345.com
# 正确结果: NXDOMAIN
# 劫持结果: NOERROR + 某个 IP(ISP 的广告页面)5.3 ISP DNS 劫持的典型特征
# 特征一: NXDOMAIN 被重定向
dig nonexistent-domain-xyz.com
# 正常: status: NXDOMAIN
# 劫持: status: NOERROR, A 记录指向 ISP 的搜索/广告页面
# 特征二: HTTP 302 重定向
# ISP 将 DNS 指向自己的服务器,然后返回 302 重定向到广告页
curl -I http://nonexistent-domain-xyz.com
# 劫持时可能返回:
# HTTP/1.1 302 Found
# Location: http://search.isp.com/?q=nonexistent-domain-xyz.com
# 特征三: HTTPS 证书不匹配
# 劫持后访问 HTTPS 网站会出现证书错误
# 因为 ISP 的服务器没有目标域名的合法证书
curl -v https://www.example.com 2>&1 | grep "SSL certificate"
# 证书不匹配 → 可能被劫持
# 特征四: 透明 DNS 代理
# 即使你配置了 8.8.8.8,ISP 仍然拦截端口 53 的流量
dig @8.8.8.8 whoami.ds.akahelp.net TXT +short
# 如果返回的不是 Google 的解析器 IP → ISP 劫持了端口 535.4 防止 DNS 劫持
防劫持措施:
1. 使用加密 DNS(DoH/DoT)
→ ISP 无法拦截加密的 DNS 流量
→ 参见上一篇"加密 DNS"
2. 使用 DNSSEC
→ 劫持的应答无法通过签名验证
→ 但需要域名启用 DNSSEC 且解析器启用验证
3. 使用 VPN
→ 所有流量(包括 DNS)通过加密隧道
→ 最彻底的防护
4. 验证关键域名的 HTTPS 证书
→ 即使 DNS 被劫持,HTTPS 证书验证会失败
→ 浏览器会显示安全警告
5. 监控 DNS 解析结果
→ 定期检查关键域名的解析结果
→ 与已知正确 IP 对比,异常告警
六、DNS 诊断工具详解
6.1 dig 高级用法
# dig 是最重要的 DNS 诊断工具
# 基本查询
dig example.com # 使用默认 DNS
dig @8.8.8.8 example.com # 指定 DNS 服务器
dig example.com AAAA # 查询 IPv6 地址
dig example.com MX # 查询邮件记录
dig example.com ANY # 查询所有记录类型
# 详细输出控制
dig example.com +noall +answer # 只显示 Answer 段
dig example.com +noall +authority # 只显示 Authority 段
dig example.com +short # 最简输出
dig example.com +multiline # 多行格式(SOA 更易读)
# 追踪完整解析链路
dig example.com +trace
# 从根服务器开始,逐级追踪到权威服务器
# 每一级显示 NS 记录和查询结果
# 非递归查询(直接问,不让服务器递归)
dig @ns1.example.com example.com +norecurse
# DNSSEC 相关
dig example.com +dnssec # 请求 DNSSEC 记录
dig example.com +cd # 禁用 DNSSEC 验证
# 反向查询
dig -x 93.184.216.34 # PTR 查询
# 批量查询
dig -f domains.txt +noall +answer # 从文件读取域名批量查询
# TCP 查询
dig example.com +tcp # 强制 TCP(绕过 UDP 问题)
# 控制超时和重试
dig example.com +time=2 +tries=1 # 2 秒超时,不重试
# EDNS0 控制
dig example.com +bufsize=1232 # 设置 EDNS0 缓冲区
dig example.com +noedns # 禁用 EDNS0
# 查看 OPT 伪记录(EDNS0 信息)
dig example.com +noall +additional
# Cookie
dig example.com +cookie # 发送 DNS Cookie6.2 nslookup 与 host
# nslookup(交互式)
nslookup
> server 8.8.8.8
> set type=MX
> example.com
> exit
# nslookup(非交互式)
nslookup example.com 8.8.8.8
nslookup -type=MX example.com
# host(简洁输出)
host example.com
# example.com has address 93.184.216.34
# example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
# example.com mail is handled by 0 .
host -t MX example.com
host -t NS example.com
# host 查反向
host 93.184.216.346.3 drill(ldns 工具链)
# drill 是 ldns 库的 DNS 查询工具,DNSSEC 支持更好
# 安装
apt-get install -y ldnsutils
# 基本查询
drill example.com
drill @8.8.8.8 example.com A
# DNSSEC 追踪(比 dig 的 DNSSEC 追踪更清晰)
drill -DT example.com
# -D: 启用 DNSSEC
# -T: 追踪信任链
# 输出每一级的 DNSKEY、DS、RRSIG 验证结果
# 最后: ;; Chase successful 或 ;; Chase failed
# DNSSEC 签名验证
drill -S example.com6.4 系统级 DNS 诊断
# 查看系统使用的 DNS 配置
cat /etc/resolv.conf
# 查看 nsswitch 配置(DNS 查询优先级)
grep hosts /etc/nsswitch.conf
# hosts: files dns
# → 先查 /etc/hosts,再查 DNS
# 查看 /etc/hosts 中是否有覆盖
grep example.com /etc/hosts
# systemd-resolved 状态
resolvectl status
resolvectl query example.com # 使用系统解析器查询
resolvectl statistics # 缓存统计
# 清除系统 DNS 缓存
# systemd-resolved
resolvectl flush-caches
# nscd
nscd --invalidate=hosts
# Chrome 浏览器
# chrome://net-internals/#dns → Clear host cache
# 跟踪 DNS 查询(实时监控系统的 DNS 行为)
tcpdump -i any -n port 53 -l 2>/dev/null七、SRE DNS 应急手册
7.1 应急处理流程
DNS 故障应急处理(5 分钟定位):
T+0 收到告警
│
├─ 第一步(30秒): 确认范围
│ curl -sI https://affected-service.com → 是否能访问?
│ dig affected-service.com → 能否解析?
│ → 确认是 DNS 问题还是其他网络/服务问题
│
├─ 第二步(60秒): 交叉验证
│ dig @8.8.8.8 affected-service.com
│ dig @1.1.1.1 affected-service.com
│ dig @<内部DNS> affected-service.com
│ → 哪些解析器有问题? 全部还是部分?
│
├─ 第三步(60秒): 定位故障层级
│ dig affected-service.com +trace
│ → 在哪一级失败? 权威? TLD? 递归?
│
├─ 第四步(60秒): 检查 DNSSEC
│ dig affected-service.com @8.8.8.8 +cd
│ → 加 +cd 能解析? → DNSSEC 问题
│
├─ 第五步(60秒): 确认根因
│ 权威服务器不可达 → 联系 DNS 服务商
│ DNSSEC 故障 → 修复签名或删除 DS
│ 域名过期 → 紧急续费
│ DNS 记录错误 → 修正记录
│
└─ 第六步: 临时缓解
方案 A: 修改 /etc/hosts 直接指定 IP(最快)
方案 B: 切换到备用域名
方案 C: 修改应用配置使用 IP 地址
7.2 /etc/hosts 紧急修复
# 当 DNS 故障且修复需要时间时,用 /etc/hosts 临时绕过
# 1. 确认正确的 IP(通过其他能正常解析的渠道)
dig @8.8.8.8 api.example.com +short # 从其他 DNS 获取
# 2. 添加到 hosts 文件
echo "93.184.216.34 api.example.com" >> /etc/hosts
# 3. 验证
ping -c 1 api.example.com
# PING api.example.com (93.184.216.34)
# 4. 故障恢复后移除
sed -i '/api.example.com/d' /etc/hosts
# 批量修复(影响多个域名时)
cat >> /etc/hosts << 'EOF'
# === DNS 故障临时修复 2025-08-01 ===
93.184.216.34 api.example.com
93.184.216.35 www.example.com
93.184.216.36 cdn.example.com
# === END DNS 故障临时修复 ===
EOF7.3 自动化 DNS 健康检查
#!/bin/bash
# dns_health_check.sh — DNS 健康检查脚本
# 用于 crontab 定期运行或告警触发时手动运行
DOMAINS=(
"api.example.com"
"www.example.com"
"db-primary.internal"
"redis.internal"
)
DNS_SERVERS=(
"8.8.8.8"
"1.1.1.1"
"10.0.0.2" # 内部 DNS
)
TIMEOUT=3
ALERT_EMAIL="oncall@example.com"
ISSUES=()
echo "=== DNS Health Check $(date) ==="
for domain in "${DOMAINS[@]}"; do
echo ""
echo "--- $domain ---"
for dns in "${DNS_SERVERS[@]}"; do
result=$(dig @$dns $domain +short +time=$TIMEOUT +tries=1 2>/dev/null)
status=$?
rcode=$(dig @$dns $domain +noall +comments +time=$TIMEOUT +tries=1 2>/dev/null | \
grep "status:" | awk -F'status: ' '{print $2}' | awk -F',' '{print $1}')
if [ -z "$result" ] && [ "$rcode" != "NOERROR" ]; then
echo " $dns: FAIL (rcode=$rcode)"
ISSUES+=("$domain@$dns: $rcode")
else
echo " $dns: OK ($result) [rcode=$rcode]"
fi
done
done
echo ""
if [ ${#ISSUES[@]} -gt 0 ]; then
echo "=== ISSUES FOUND ==="
for issue in "${ISSUES[@]}"; do
echo " ✗ $issue"
done
# 发送告警(如果配置了邮件)
if command -v mail &>/dev/null; then
echo "DNS Issues: ${ISSUES[*]}" | \
mail -s "DNS Health Check Alert" "$ALERT_EMAIL"
fi
exit 1
else
echo "=== ALL CHECKS PASSED ==="
exit 0
fi八、常见 DNS 配置错误
8.1 配置错误清单
排名前十的 DNS 配置错误:
1. CNAME 与其他记录共存
错误: example.com CNAME cdn.example.com
example.com MX mail.example.com
正确: CNAME 不能与同名的其他记录共存(RFC 1034)
修正: 使用 A 记录替代 CNAME,或使用 ALIAS/ANAME
2. 根域使用 CNAME
错误: example.com CNAME something.cdn.com
正确: 根域(zone apex)不能使用 CNAME
修正: 使用 A/AAAA 记录,或提供商的 ALIAS 功能
3. 遗漏的尾部点号
错误: www CNAME example.com(在区域文件中)
解释: 没有尾部点号,BIND 会追加域名
实际变成 www.example.com CNAME example.com.example.com
正确: www CNAME example.com.(注意尾部的点)
4. SOA 序列号未递增
错误: 修改 DNS 记录后忘记递增 SOA serial
影响: 从服务器不会同步新记录
修正: 每次修改都递增 serial(推荐格式 YYYYMMDDNN)
5. NS 记录指向 CNAME
错误: example.com NS ns1-alias.example.com
ns1-alias CNAME ns1.provider.com
正确: NS 记录必须指向 A/AAAA 记录,不能是 CNAME
修正: NS 直接指向有 A/AAAA 的主机名
6. MX 记录指向 CNAME
错误: example.com MX 10 mail-alias.example.com
mail-alias CNAME mail.provider.com
正确: MX 目标不应是 CNAME(RFC 2181)
修正: MX 直接指向有 A/AAAA 的主机名
7. TTL 不一致
错误: 同一域名的 A 记录 TTL=300, AAAA 记录 TTL=3600
影响: IPv4 和 IPv6 的缓存行为不一致
修正: 同一域名的相关记录使用相同 TTL
8. Glue Records 缺失
场景: NS 服务器在自己的域内
example.com NS ns1.example.com
但父域 .com 中没有 ns1.example.com 的 A 记录
影响: 鸡生蛋问题 — 要解析 ns1.example.com 需要先查 example.com 的 NS
修正: 在注册商处添加 Glue Records
9. 反向 DNS 未配置
影响: 邮件可能被拒收(SPF/DKIM 检查时反向解析失败)
检查: dig -x <your-ip>
修正: 联系 IP 提供商配置 PTR 记录
10. 通配符记录的副作用
错误: *.example.com A 1.2.3.4
影响: 所有不存在的子域名都返回 1.2.3.4
包括 nonexistent.example.com
DNSSEC 的否定存在证明也受影响
修正: 谨慎使用通配符,明确需要的子域名
8.2 验证 DNS 配置
# 使用 named-checkzone 验证区域文件
named-checkzone example.com /etc/bind/zones/example.com.zone
# zone example.com/IN: loaded serial 2025080101
# OK
# 使用 named-checkconf 验证 BIND 配置
named-checkconf /etc/named.conf
# (无输出表示通过)
# 在线工具:
# - https://intodns.com/ — 综合 DNS 配置检查
# - https://mxtoolbox.com/ — 邮件 DNS 检查(MX/SPF/DKIM)
# - https://dnschecker.org/ — 全球 DNS 传播检查九、Kubernetes DNS 故障排查
9.1 K8s DNS 常见问题
# 问题一: Pod 内无法解析域名
# 进入 Pod 测试
kubectl exec -it <pod> -- nslookup kubernetes.default
# 如果失败:
# 1. 检查 CoreDNS 是否运行
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 如果 CoreDNS Pod 不健康 → 修复 CoreDNS
# 2. 检查 DNS Service
kubectl get svc -n kube-system kube-dns
# ClusterIP 应该与 Pod 的 /etc/resolv.conf 中的 nameserver 一致
# 3. 检查 Pod 的 DNS 配置
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
# 4. 直接测试 CoreDNS 端点
kubectl exec <pod> -- dig @10.96.0.10 kubernetes.default.svc.cluster.local
# 问题二: 外部域名解析慢
# 原因: ndots:5 导致大量无效搜索
kubectl exec <pod> -- time dig google.com
# 可能需要 5+ 秒(尝试 5 个搜索域后才查真实域名)
# 解决:
# 方案 A: 使用 FQDN(末尾加点)
kubectl exec <pod> -- dig google.com. # 注意末尾的点
# 方案 B: 降低 ndots
# 在 Pod spec 中设置 dnsConfig: options: [{name: ndots, value: "2"}]9.2 CoreDNS 日志分析
# 启用 CoreDNS 查询日志(调试时使用)
kubectl edit configmap coredns -n kube-system
# 添加 log 插件
# 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
# 分析日志中的错误
kubectl logs -n kube-system -l k8s-app=kube-dns | \
grep -E "SERVFAIL|NXDOMAIN|i/o timeout" | tail -20
# 常见日志错误:
# [ERROR] plugin/errors: 2 example.com. A: i/o timeout
# → CoreDNS 到上游 DNS 超时
# → 检查 CoreDNS 的 forward 配置和上游 DNS 可达性
# [ERROR] plugin/errors: 2 svc.cluster.local. A: NXDOMAIN
# → 查询的 Service 不存在
# → 检查 Service 名称和 namespace十、DNS 监控与预防
10.1 关键监控指标
DNS 监控的核心指标:
可用性指标:
- DNS 查询成功率(目标 > 99.9%)
- SERVFAIL 比例(目标 < 0.1%)
- 超时比例(目标 < 0.01%)
性能指标:
- 查询延迟 P50 / P95 / P99
- 缓存命中率(目标 > 90%)
- 查询 QPS
安全指标:
- 异常域名查询(DGA 检测)
- NXDOMAIN 率突增(可能是扫描或攻击)
- 关键域名解析结果变化
10.2 Prometheus 告警规则
# DNS 告警规则
groups:
- name: dns-alerts
rules:
- alert: DnsResolutionFailure
expr: |
probe_success{job="dns-probe"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "DNS 解析失败: {{ $labels.instance }}"
description: "DNS 服务器 {{ $labels.instance }} 连续 2 分钟无法解析"
- alert: DnsHighLatency
expr: |
probe_dns_lookup_time_seconds{job="dns-probe"} > 1
for: 5m
labels:
severity: warning
annotations:
summary: "DNS 延迟过高: {{ $labels.instance }}"
description: "DNS 查询延迟超过 1 秒,持续 5 分钟"
- alert: DnsCacheHitRateLow
expr: |
sum(rate(coredns_cache_hits_total[5m])) /
(sum(rate(coredns_cache_hits_total[5m])) +
sum(rate(coredns_cache_misses_total[5m]))) < 0.8
for: 10m
labels:
severity: warning
annotations:
summary: "DNS 缓存命中率过低"
description: "缓存命中率低于 80%,持续 10 分钟"
- alert: CoreDnsServfailRate
expr: |
sum(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m])) /
sum(rate(coredns_dns_responses_total[5m])) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: "CoreDNS SERVFAIL 率过高"
description: "SERVFAIL 比例超过 1%,持续 5 分钟"十一、总结
DNS 故障排查的核心是系统化的方法论——不是随机尝试命令,而是按照分类、定位、验证、修复的流程有序推进:
先分类再排查。 DNS 故障无非四种——超时、NXDOMAIN、SERVFAIL、劫持。
dig一条命令就能快速分类,分类后的排查路径完全不同。不要跳过分类直接猜原因。交叉验证是最重要的技巧。 使用多个递归解析器(8.8.8.8、1.1.1.1、208.67.222.222)和权威服务器交叉对比。全部失败说明是权威端问题,部分失败说明是递归端或网络路径问题。
DNSSEC 是 SERVFAIL 的高频原因。 遇到 SERVFAIL,第一个动作是
dig +cd(禁用 DNSSEC 验证)。如果+cd能解析,就是 DNSSEC 问题——签名过期、DS 不匹配、信任链断裂。掌握 dig 就掌握了 DNS 排查的 80%。
+trace追踪解析链路、+dnssec检查签名、+tcp绕过 UDP 问题、+cd禁用 DNSSEC、+norecurse直查权威。这五个选项覆盖了绝大多数场景。预防优于救火。 建立 DNS 健康检查脚本、Prometheus 告警规则、关键域名解析结果监控。在用户感知到问题之前发现和修复 DNS 异常,是成熟运维体系的标志。
本篇是 DNS 工程系列的最后一篇。从协议解剖到解析链路,从性能优化到DNSSEC和加密 DNS,我们完整覆盖了 DNS 工程的核心知识。下一篇开始进入 TLS 与安全传输系列。
上一篇:加密 DNS:DoH、DoT 与 DoQ 的工程部署
下一篇:TLS 1.2 握手完整解剖:从 ClientHello 到 Application Data
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】DNS 协议解剖:查询格式、记录类型与响应码
DNS 是互联网最基础的目录服务,也是最脆弱的单点之一。本文从 wire format 出发逐字段解析 DNS 报文结构,详解 A/AAAA/CNAME/MX/SRV/TXT/NS/SOA 等记录类型的工程用途,分析 EDNS0 扩展与 DNS over TCP 的触发条件,结合 dig +trace 完整实操展示 DNS 解析的真实链路。
网络工程索引
汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。
【网络工程】CDN 架构原理:PoP、边缘节点与 Origin Shield
系统解剖 CDN 的多层缓存架构——从 DNS 调度到 PoP 内部结构、Origin Shield 回源保护、多 CDN 部署策略。结合实际配置和响应头分析,给出 CDN 架构的工程理解。
【网络工程】CDN 故障调试:缓存命中率、回源异常与头分析
CDN 故障排查是运维工程中的高频场景。本文系统覆盖缓存未命中分析、回源异常诊断、CDN 响应头解读、性能监控体系搭建四个维度,提供从现象到根因的排查方法论。