土法炼钢兴趣小组的算法知识备份

【网络工程】DNS 故障排查实战:从超时到劫持

文章导航

分类入口
network
标签入口
#dns#troubleshooting#dig#nslookup#servfail#dns-hijack#sre

目录

“网络不通”——这是 SRE 最常接到的告警之一。而在所有”网络不通”的故障中,DNS 问题占了相当大的比例。DNS 超时导致的服务不可用、NXDOMAIN 导致的连接失败、SERVFAIL 导致的间歇性错误、DNS 劫持导致的流量被截获——每一种故障的根因和排查路径都不一样。

大多数工程师对 DNS 排查的认知停留在 pingnslookup。但真实的 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 劫持了端口 53

5.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 Cookie

6.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.34

6.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.com

6.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 故障临时修复 ===
EOF

7.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 故障排查的核心是系统化的方法论——不是随机尝试命令,而是按照分类、定位、验证、修复的流程有序推进:

  1. 先分类再排查。 DNS 故障无非四种——超时、NXDOMAIN、SERVFAIL、劫持。dig 一条命令就能快速分类,分类后的排查路径完全不同。不要跳过分类直接猜原因。

  2. 交叉验证是最重要的技巧。 使用多个递归解析器(8.8.8.8、1.1.1.1、208.67.222.222)和权威服务器交叉对比。全部失败说明是权威端问题,部分失败说明是递归端或网络路径问题。

  3. DNSSEC 是 SERVFAIL 的高频原因。 遇到 SERVFAIL,第一个动作是 dig +cd(禁用 DNSSEC 验证)。如果 +cd 能解析,就是 DNSSEC 问题——签名过期、DS 不匹配、信任链断裂。

  4. 掌握 dig 就掌握了 DNS 排查的 80%。 +trace 追踪解析链路、+dnssec 检查签名、+tcp 绕过 UDP 问题、+cd 禁用 DNSSEC、+norecurse 直查权威。这五个选项覆盖了绝大多数场景。

  5. 预防优于救火。 建立 DNS 健康检查脚本、Prometheus 告警规则、关键域名解析结果监控。在用户感知到问题之前发现和修复 DNS 异常,是成熟运维体系的标志。

本篇是 DNS 工程系列的最后一篇。从协议解剖解析链路,从性能优化DNSSEC加密 DNS,我们完整覆盖了 DNS 工程的核心知识。下一篇开始进入 TLS 与安全传输系列。


上一篇:加密 DNS:DoH、DoT 与 DoQ 的工程部署

下一篇:TLS 1.2 握手完整解剖:从 ClientHello 到 Application Data

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-07-28 · network

【网络工程】DNS 协议解剖:查询格式、记录类型与响应码

DNS 是互联网最基础的目录服务,也是最脆弱的单点之一。本文从 wire format 出发逐字段解析 DNS 报文结构,详解 A/AAAA/CNAME/MX/SRV/TXT/NS/SOA 等记录类型的工程用途,分析 EDNS0 扩展与 DNS over TCP 的触发条件,结合 dig +trace 完整实操展示 DNS 解析的真实链路。

2026-04-22 · network

网络工程索引

汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。


By .