一个工作日上午,客服那边突然涌进一堆反馈:官网打不开、下单页面转圈。我自己电脑刷新却一切正常,服务器监控也是绿的。折腾了二十分钟才定位到——机器没挂,是 DNS 解析在部分地区和运营商那里出了问题。这篇把那次排查、应急切换和后来补的多线路容灾完整写一遍。DNS 是网站可用性的"第一公里",平时没人在意,真出问题时它比宕机还让人摸不着头脑。
那次最迷惑人的地方就在这:我本地访问飞快,监控面板上 CPU、带宽、进程全正常。但用户是真打不开。这种"我这边好的、用户那边挂了"的局面,先别急着重启服务,大概率要往链路上看。
用户访问一个域名,要先走一遍解析:浏览器缓存、本机 hosts、运营商的递归 DNS、再到根和权威服务器,最后才拿到 IP 去建连。任何一环把解析给了错误结果、或者干脆超时,用户看到的都是"打不开",而你的源站可能毫发无损。
现场第一步是用不同的 DNS 去问,看返回到底一不一致:
# 看完整解析链路,每一级是谁在应答
dig +trace example.com
# 分别问三家公共 DNS,对比返回的 A 记录和剩余 TTL
dig @114.114.114.114 example.com
dig @223.5.5.5 example.com
dig @119.29.29.29 example.com
# Windows 上没有 dig 时用 nslookup
nslookup -type=ns example.com当时一对比就露馅了:我默认用的公共 DNS 返回正常 IP,而某运营商递归返回的是一个旧 IP——那台机器上周就下线了。问题不在服务器,在"门牌号"还指着旧地址。
定位清楚后,第一反应是去 DNS 控制台把 A 记录改到新地址(或临时切到 CDN)。但点完保存不等于用户立刻生效,中间隔着一层缓存。
递归解析器会按 TTL 缓存你的记录,运营商那层、用户浏览器和操作系统也各有缓存。TTL 没走完,很多人拿到的还是旧值。这就是为什么同样改一条记录,有人一分钟恢复、有人半小时还打不开。
这里有个必须在平时做好、出事时才不后悔的取舍:TTL 设多大。设大了(比如 3600 秒甚至一天),解析压力小、递归命中率高,但故障切换时你得干等;设太小(比如 10 秒),切换是快了,可权威 DNS 的查询量会明显上来,极端情况下还可能被限流。我们后来把官网主记录的 TTL 固定在 300 秒,是切换速度和解析压力之间比较舒服的点,大促、割接这种已知风险窗口会临时再降到 60 秒。
改完之后,别只在自己电脑上刷新,要主动向多个公共 DNS 反复确认传播情况:
# 每 20 秒问一次,观察各 DNS 何时拿到新记录
while true; do
date
dig @223.5.5.5 example.com +short
dig @114.114.114.114 example.com +short
sleep 20
done那次故障暴露的第二个问题是:我们当时只用了一家权威 DNS。这意味着只要这家 DNS 服务本身抖动,我连"改解析自救"的入口都没有,只能干等服务商恢复。后来做了三件事。
一是双权威 DNS。 在两家不同厂商的 DNS 服务上都建好同一套记录,域名注册局那边把两家的 NS 都配上。递归服务器查询时会依次尝试,一家不通自动走另一家,成本不高,却把"DNS 服务商整体故障"这个单点摘掉了。
二是按线路智能解析。 不同运营商、不同地区回不同的线路 IP,电信用户走电信入口、移动走移动,境内外分开,避免跨网绕路。这套在国内网络环境下收益很直接,尤其官网源站有多线接入时。
三是准备一个静态兜底页。 把一个纯静态的"服务维护中/核心功能可用"页面放到对象存储、挂到 CDN。真到源站整体不可用、短时间修不好时,直接把域名 CNAME 切到兜底页,用户至少看到的是有信息的页面,而不是浏览器报错。
需要清醒认识的是 DNS 故障切换(failover)的边界:健康检查能发现故障、能自动改记录,但因为递归缓存客观存在,RTO(恢复时间)不可能是零,对外报恢复预期时要按"TTL 加传播延迟"来报,别承诺"秒级切换",最后反而被动。
出事前我们的监控是"内向"的:进程在不在、端口通不通、机房内访问快不快。这些都正常,可用户照样打不开。补的方向是加上"外向"的黑盒拨测。
一是多地域、多运营商的主动探测,定时从各地去做"解析—建连—拿到正确内容"这一整套动作,而不是只在自己机房里自测。二是对解析结果做白名单校验:域名当前解析到的 IP 必须落在我们登记的合法 IP 集合里,一旦解析到陌生地址(劫持、污染、旧记录残留都可能),立刻告警。下面是我们内部用的一个简化版探测脚本:
import socket
import time
import requests
ALLOWED_IPS = {"203.0.113.10", "203.0.113.11"}
DOMAIN = "example.com"
PROBES = ["223.5.5.5", "114.114.114.114", "119.29.29.29"]
def resolve_via(dns_server):
# 生产环境可用 dnspython 指定 resolver,这里用系统解析做示意
return {ai[4][0] for ai in socket.getaddrinfo(DOMAIN, 443, proto=socket.IPPROTO_TCP)}
def probe():
ips = resolve_via(None)
if not ips <= ALLOWED_IPS:
alert(f"解析异常,出现非白名单IP: {ips - ALLOWED_IPS}")
try:
r = requests.get(f"https://{DOMAIN}", timeout=5)
if r.status_code != 200:
alert(f"内容层异常,状态码 {r.status_code}")
except requests.RequestException as e:
alert(f"建连失败: {e}")
def alert(msg):
# 对接你的告警通道
print(time.strftime("%F %T"), msg)
if __name__ == "__main__":
probe()解析层、连接层、内容层分开告警也很重要,否则一条"网站不可用"甩过来,你还得重新判断是哪一段的问题。
DNS 这一层平时无声无息,真出问题却最容易把人带偏——因为你的服务器看起来一切正常。把 TTL 提前调合理、权威 DNS 做双线、准备静态兜底页、再加上站在用户视角的外部拨测,这四件事都不复杂,难的是在没出事的时候愿意花时间补。做完之后,再遇到解析类故障,我们的恢复是以分钟计的,而不是像那次一样,先慌二十分钟才找到门牌号。建议你现在就 dig 一下自己的官网,看看 TTL 是多少、NS 有几家,这是成本最低的一次体检。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。