记一次内网 DNS 递归超时导致的网页间歇卡顿

问题背景

公司内网 DNS 由两台 BIND 服务器组成主从架构,对内解析 *.corp.company.com,对外域名通过 forwarders 转发到运营商 DNS 做递归解析。全公司约 500 台办公终端,DHCP 下发 DNS 为这两台内网 DNS。

故障现象

6 月下旬开始,IT 服务台陆续收到"网络很慢"的投诉。用户描述高度一致:

“打开一个网页,浏览器左下角显示’正在解析主机’,等 5-10 秒后才开始加载,但加载页面内的图片/视频速度又很快。”

使用 curl 测试也验证了这一点:

1
2
3
4
5
6
7
# DNS 阶段耗时异常
$ curl -w "DNS: %{time_namelookup}s, TCP: %{time_connect}s, Total: %{time_total}s\n" \
  -o /dev/null -s https://www.baidu.com
DNS: 5.2s, TCP: 0.03s, Total: 5.5s    # DNS解析花了5.2秒!

$ curl -w "DNS: %{time_namelookup}s..." -o /dev/null -s https://www.baidu.com
DNS: 0.05s, TCP: 0.03s, Total: 0.3s   # 第二次就快了(缓存命中)

首次访问外网域名的 DNS 解析异常缓慢(5-10 秒),但解析结果缓存后再次访问秒开——典型的 DNS 递归查询超时

排查过程

第一步:定位 DNS 延时归属

1
2
3
$ dig www.baidu.com @192.168.1.10 +stats
;; Query time: 5203 msec         # 内网DNS响应5.2秒
;; SERVER: 192.168.1.10#53(192.168.1.10)

内网 DNS 自身响应就要 5 秒。直接查询运营商 DNS:

1
2
$ dig www.baidu.com @114.114.114.114 +stats
;; Query time: 12 msec           # 外部DNS响应12ms

外部 DNS 速度快——问题出在内网 DNS 的 forwarders 配置上。

第二步:检查内网 DNS 转发器配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ cat /etc/bind/named.conf.options
options {
    forwarders {
        202.96.209.133;     # 上海电信 DNS(已废弃的旧地址!)
        202.96.209.5;       # 上海电信 DNS
        114.114.114.114;    # 114 DNS
    };
    ...
    forward first;          # first = 先转发,超时后自己递归
};

first 策略表示:先尝试转发给 forwarders,如果所有 forwarders 都超时或失败,再自行递归。

第三步:逐个测试 forwarders

1
2
3
4
5
6
7
8
$ dig www.baidu.com @202.96.209.133 +time=5 +retry=0
;; connection timed out; no servers could be reached    # 第一个DNS不可达!

$ dig www.baidu.com @202.96.209.5 +time=5
;; Query time: 5230 msec                                # 第二个很慢

$ dig www.baidu.com @114.114.114.114 +time=5
;; Query time: 15 msec                                  # 第三个正常

第一个 forwarder 202.96.209.133 已经彻底不可达。由于 forward first 策略,BIND 会按顺序尝试每个 forwarder,每个超时约 2-3 秒(默认超时),两个不可达/慢的加起来刚好 5 秒。

第四步:分析为什么缓存命中也偶发缓慢

当 BIND 缓存中的记录过期需要重新解析时,如果用户请求恰好落在缓存过期和重新解析的时间窗口,就需要等待 forwarders 响应。高峰期这个窗口频繁出现。

解决方案

  1. 更新 forwarders 配置
1
2
3
4
5
forwarders {
    223.5.5.5;       # 阿里 DNS(公网)
    119.29.29.29;    # DNSPod(公网)
};
forward only;        # 改为 only,不自行递归
  1. 验证性能
1
2
3
$ rndc flush                    # 清除缓存后测试
$ dig www.baidu.com @192.168.1.10 +stats
;; Query time: 15 msec          # 恢复至15ms
  1. 配置响应缓存优化
1
2
max-cache-ttl 86400;      # 最大缓存24小时
max-ncache-ttl 300;       # 否定缓存5分钟

根因分析

直接原因:BIND forwarders 列表中的第一个 DNS 服务器 202.96.209.133 已下线,每次查询都需要等待该地址超时(约 2.5 秒),加上第二个 forwarder 响应也较慢,总计可达 5 秒以上。

深层原因:

  • 内网 DNS 配置多年未更新,运营商的 DNS 地址变更后没有同步调整
  • forward first 策略在 forwarders 不可靠时放大了故障影响
  • 缺少对 DNS 服务器查询延迟的监控

预防措施

  1. DNS 监控:通过 Zabbix 监控每台 DNS 的递归查询平均响应时间,设置 >500ms 告警
  2. forwarders 定期验证:每月用 dig +time=3 测试所有 forwarders 可达性和响应时间
  3. 使用 DNS over HTTPS:考虑将 forwarders 升级为支持 DoH 的公共 DNS,减少运营商 DNS 变更的影响
  4. DNS 配置版本管理:将 /etc/bind/ 纳入 Git 管理,变更记录可追溯
  5. 冗余架构:保持主从两台 DNS 的 forwarders 配置一致,但使用不同的上游 DNS 组合

总结

DNS 是互联网世界的"电话簿",它的一个微小延时会被前端的每一项网络请求放大。这次故障的根本原因极其简单——一个硬编码了多年的"死地址",但由于 DNS 的层级缓存掩盖了问题(缓存命中时感觉很快),让大家误以为是"网络波动"而非配置问题。对于运维来说,定期验证 DNS forwarders 的健康状态应该是"开机自检"级别的例行工作。

使用 Hugo 构建
主题 StackJimmy 设计