第 19 章 DNS、CDN 与网络排障
前几章讲协议"应该怎么工作",本章讲它"实际上哪里坏了"。DNS 是唯一能把整条链路拖慢数秒的旁路系统。
学习目标
读完本章,你应当能够:
- 画出 DNS 递归查询与迭代查询的两条路径,说清每一层的缓存位置、TTL 归属与负缓存作用范围;
- 判断一组 DNS 记录能否合法共存(CNAME 与 MX、CNAME 与根域名),解释"切换 IP 前为什么要调小 TTL"的量化依据;
- 用
dig +trace、+short、@server、+tcp、+noedns定位解析异常,说明nslookup在排障中的信息缺失; - 对一次"网站访问慢"给出分层命令序列,按
curl -w的time_*差值定位到 DNS、建连、TLS、TTFB、传输中的哪一段; - 写出
tcpdump的主机/端口/标志位过滤表达式,并判断握手是否完成、有无重传、RST、零窗口; - 辨析
ping/traceroute/mtr/telnet/nc/ss各自的能力边界,在 ICMP 被禁时给出替代手段。
前置知识检查
| 你需要知道 | 在哪一节补齐 |
|---|---|
UDP 语义、TCP 三次握手与状态名(SYN_SENT/ESTABLISHED/TIME_WAIT) | 见第 16 章 16.1、16.3 节;本章只用它们读抓包结果 |
| 重传、重复 ACK、零窗口的成因 | 见第 17 章 17.2、17.4 节;本章只讲"抓包时怎么看出来" |
TLS 握手往返数、SNI、证书链 | 见第 20 章 20.5 节;本章只用 time_appconnect 度量它 |
| IP 分片、MTU、UDP 报文长度上限 | 见第 15 章 15.4 节;本章用来说明 DNS 的 TC 截断 |
19.1 DNS 的完整查询流程:递归与迭代是两条路径
面试里"讲一下 DNS 解析过程"最常见的错误回答,是把浏览器缓存、根服务器、权威服务器串成一条线性链。真实的 DNS 是两段拼接:客户端到本地 DNS 服务器(local DNS server,也常叫递归解析器 recursive resolver)是递归,本地 DNS 服务器到根/TLD/权威是迭代。两段的责任划分、缓存归属、故障表现都不同。
19.1.1 两段路径的全图
| |
三个容易答错的点。第一,/etc/hosts 优先于 DNS 不是"永远优先",而由 nsswitch.conf 里 hosts: 一行的顺序决定:写成 hosts: dns files 就反过来,写成 hosts: files myhostname dns 是多数发行版默认。面试问"改了 hosts 不生效",第一个要看这一行,第二个是应用自己缓存了(JVM 的 networkaddress.cache.ttl、Go 的 GODEBUG=netdns)。第二,本地 DNS 服务器自己也会缓存,所以第 ⑥~⑧ 步通常根本不发生——这也是"改了权威记录但不生效"的第一大原因,不是你的 TTL 没到,是别人的缓存还没到期。第三,递归与迭代的分界在本地 DNS 服务器:客户端发的是 RD=1(recursion desired)请求,只关心最终答案;本地 DNS 发的是 RD=0 请求,每问一台就要自己接着问下一台。根服务器从不做递归,以避免被放大攻击利用。
重要
答题模板固定为:“客户端→本地 DNS 是递归,本地 DNS→根→TLD→权威是迭代”。只答"一层层问下去"会被追问"那谁负责帮你问完",这个追问就是在区分你是否知道 RD 标志位与两段责任划分。
19.1.2 TTL 与负缓存:为什么"删除记录"不一定立刻失效
| 缓存位置 | 缓存什么 | 有效期由谁决定 | 排障时怎么绕过 |
|---|---|---|---|
| 浏览器 DNS 缓存 | 域名 → IP | 浏览器自定上限(常为 60 s 量级) | 清浏览器缓存、开隐身窗口 |
| OS 解析器缓存 | 域名 → IP | 记录的 TTL | resolvectl flush-caches |
/etc/hosts | 静态映射 | 永不过期 | 不适用(它是覆盖源,不是缓存) |
| 本地 DNS 服务器 | 正/负应答 | 正应答按 TTL;负应答按 SOA 的 minimum | 换 @8.8.8.8 这类公共解析器对比 |
| 权威服务器 | 不缓存 | 它是数据源 | 不适用 |
负缓存(negative caching) 是面试的高区分度点:查询不存在的名字时,权威返回 NXDOMAIN 并附上 SOA 记录,本地 DNS 按 SOA 的 minimum 字段(RFC 2308 口径)把这个"不存在"缓存一段时间。后果是:刚上线的新域名若已被解析器查过一次,必须等负缓存过期才能解析成功,表现为"我这边能通、别人那边 NXDOMAIN"。因此上线顺序永远是先建记录再切流量。同理,TTL 设为 0 也不等于"不缓存":多数解析器有秒级下限(RFC 1035 里 0 只表示"不要复用"),客户端缓存更不受你的 TTL 完全支配。
19.1.3 切换 IP 时必须提前调小 TTL:一个算例
TTL 决定缓存能活多久,所以变更生效的最坏时间 ≈ 当前 TTL。切换 IP 的标准动作是两阶段:提前至少一个旧 TTL 的时长把 TTL 从 86400(1 天)改成 60,等旧的长 TTL 缓存全部过期;然后改记录值(A → 新 IP)并等 60 s 让新值扩散;稳定后再把 TTL 调回 86400,降低解析器压力与放大攻击面。
算例:TTL = 86400 时直接改 IP,最坏情况下一部分客户端 24 小时内仍在访问旧 IP,而你很可能已经下线了旧机器——这就是"灰度切换变成部分用户 404"。把 TTL 降到 60 s 后,最坏生效时间从 86400 s 压到 60 s,代价是解析器查询量上升 1440 倍,所以必须在切换完成后调回去。
提示
面试话术:“TTL 不是’什么时候生效’,而是’多久之内一定生效’。调小 TTL 不是为了加快生效,而是为了给最坏情况设上界。“这句话能直接压住"TTL 就是缓存时间"这种一句话题。
19.2 DNS 报文结构与记录类型
19.2.1 报文的五个区段
DNS 报文固定 12 字节头(Header),之后是四个可变长区段。面试常考标志位与"哪些区段会变长”:
| |
| 标志位 | 含义 | 排障时说明什么 |
|---|---|---|
QR | 0 查询 / 1 应答 | 抓包时区分方向;两者 ID 必须一致,否则说明混了并发查询 |
AA | Authoritative Answer | 为 1 表示来自该域权威服务器;问公共解析器时通常为 0 |
TC | Truncated | 为 1 表示应答被截断,客户端必须改用 TCP 重查 |
RD / RA | Recursion Desired / Available | RD=1 是客户端请求递归;RA=0 说明它是纯权威,不能当解析器用 |
RCODE | 0 NOERROR、2 SERVFAIL、3 NXDOMAIN、5 REFUSED | SERVFAIL 常见于 DNSSEC 校验失败或权威不可达;REFUSED 是被策略拒绝 |
TC 与 EDNS0 的关系是高频追问:传统 DNS 的 UDP 应答上限 512 字节(RFC 1035),超了就置 TC=1;EDNS0(RFC 6891)通过一条**伪记录(OPT RR)**把 UDP 承载能力协商到更大(常见 1232~4096 字节),并同时承载 DNSSEC 的 DO 位。所以”dig 看到没截断",往往是因为对方支持 EDNS0,而不是记录真的小。
19.2.2 记录类型与"为什么有些记录不能共存"
| 类型 | 语义 | 面试要说的点 |
|---|---|---|
A / AAAA | 名字 → IPv4 / IPv6 | 一个名字可挂多条 A 形成轮询;双栈域名有问题先看 AAAA |
CNAME | 别名 → 规范名 | 只允许指向另一个名字而非 IP;解析器会继续追到底 |
NS | 该域由哪些服务器负责 | 委托(delegation)的载体 |
MX | 邮件交换,带优先级 | 标准上不能指向 CNAME |
TXT | 任意文本 | 现用于 SPF/DKIM/DMARC 与域名所有权校验 |
SOA | 区的起始授权 | 含序列号与 minimum(负缓存时长) |
PTR | IP → 名字(反向解析) | 在 in-addr.arpa/ip6.arpa 下;邮件被拒收常因缺 PTR |
为什么 CNAME 不能与 MX 共存、不能用在根域名? 根因是同一条记录集(RRset)里"名字到值"的映射必须唯一。DNS 是一条"问名字、拿值"的查询协议;当某个名字被声明为 CNAME 时,它只剩下别名这一种语义,RFC 1034 要求该名字下不得再存在其他类型的数据,否则解析器拿到 CNAME 后去追新名字,其他记录就成了永远取不到的孤儿。所以 example.com CNAME target.com 与 example.com MX mail.example.com 冲突。根域名(zone apex)不能是 CNAME,因为根域名必须承载 NS 与 SOA(区的委托点与起始授权)——一个名字既然已有 NS/SOA,就不可能"只剩 CNAME 语义"。这就是"裸域不能直接 CNAME 到 CDN"的规范来源,工程上用 A/AAAA 指向 CDN 固定 IP,或用厂商的 ANAME/ALIAS 类记录(服务端合成 A 记录)绕过。同理 MX 指向 CNAME 也违反 RFC 2181 的澄清,它既多一次解析,又给邮件投递路径增加失败面。追踪一条 CNAME 链则意味着解析器逐跳再查,每跳都可能是一次额外迭代:
| |
每跳都增加延迟(未缓存即为一次 RTT),并放大失败面:任一跳的权威不可达,整个解析就是 SERVFAIL。这是 CDN 接入时建议减少 CNAME 层数的原因。
19.3 DNS 负载均衡与健康检查
| 策略 | 做法 | 适用与代价 |
|---|---|---|
| 轮询(round robin) | 同一名字返回多条 A,解析器轮换取用 | 实现最简;粒度是"客户端"而非"请求",无法按负载分配 |
| 地理就近(geo-steering) | 按解析器 IP(EDNS Client Subnet 可带客户端网段)选最近机房 | 依赖解析器位置准确;公共解析器集中化会让"就近"偏到大城市 |
| 加权(weighted) | 给记录配权重,按比例返回 | 常用于灰度;权重只在解析时刻生效,不反映真实负载 |
分工上,DNS 做的是最外层、最粗粒度的一次分流:决定流量进哪个机房/集群入口。进入机房后,LVS 一类四层负载均衡按连接分流,Nginx/Envoy 再按请求分流与路由(并发模型见第 7 章 7.5 节)。三层职责不可互换:DNS 看不到连接,四层看不到 URL。
小心
不要答"用 DNS 做负载均衡就可以了"。DNS 有两个结构上无法消除的缺陷:(1) 摘除不及时——客户端与中间解析器按 TTL 缓存结果,一台机器挂了,在 TTL 过期前仍有流量打到它;(2) 感知不到服务端真实负载——返回哪条记录与你的 CPU、队列深度无关,DNS 协议里根本没有回传负载的字段。
工程上的补救是分层兜底:DNS 负责机房级选择(TTL 设小一些,如 60 s),健康检查在 DNS 侧只做服务器级存活探测(失败则摘除该 A 记录),精细负载交给机房内的四层/七层,再由应用层做熔断降级(见第 35 章)。能把"DNS 只能做粗粒度、且摘除受 TTL 约束"说清楚,就已超过只背"轮询/加权/就近"的答法。
19.4 dig 的实战用法与 nslookup 的差距
dig(domain information groper)的价值在于它把报文的每个区段都打出来,包括 flags、QUERY/ANSWER/AUTHORITY 段与耗时。写排障命令时,注释要写"为什么这么查":
| |
dig 与 nslookup 的差距在于信息量:nslookup 是历史工具,输出经过"友好格式化",默认不显示 flags、TTL、AUTHORITY 段,也不区分答案来自缓存还是权威,只能告诉你"解析出来了/没解析出来"。而排障需要的是为什么:
| 需求 | dig | nslookup |
|---|---|---|
看 AA/TC/RA 标志位 | 头部直接显示 | 不显示 |
| 看 TTL(判断缓存新旧) | ANSWER 段每条都有 | 不显示 |
看 AUTHORITY/ADDITIONAL 段 | 完整输出 | 不输出 |
| 复现迭代链路 | +trace 内建 | 无 |
| 指定端口、EDNS0、超时控制 | -p、+bufsize、+time | 能力缺失 |
注意
面试官常追问"为什么 nslookup 不推荐"。标准答案不是"它旧",而是它隐藏了判断缓存与截断所必需的信息:同样是"解析成功",dig 能告诉你答案来自哪台服务器、TTL 还剩几秒、是否走过 TCP,nslookup 只能给你一个 IP。
19.5 CDN:回源、命中率与调度
设 命中率 = 边缘直接响应的请求数 / 总请求数,其补数是回源率。回源请求要额外走一次"边缘 → 源站"的 RTT 并占用源站容量,所以回源率高的 CDN 等于给源站加了一层绕路。
| |
命中率低的常见原因(面试高频,按出现频率排序):一是 Cache-Control: no-cache/no-store/private 写错,边缘只能遵守源站指令;二是 URL 带随机参数(?t=时间戳、?rand=),每次都是新 key,等于关掉缓存;三是 Vary 维度过多(Vary: User-Agent 会把每个 UA 变成独立 key)或按 Cookie 全量区分;四是缓存键设计过细,把完整 query string 都算进 key 而业务只需要其中一两个参数;五是 TTL 过短,或源站 max-age 与边缘默认 TTL 冲突时取了小值;六是内容本来就动态(个性化接口),此时应走动态加速而不是硬缓存;七是发布后大规模刷新使缓存未回温,或请求合并没有开启导致同一文件被并发回源多次。
调度有两条路线。DNS 调度按解析器位置/运营商返回不同边缘 IP,粒度是客户端级且受 TTL 约束,切换与摘除都慢。Anycast 让同一 IP 在全球多节点公告,由 BGP 选最近入口,逐包生效、切换快,但路由抖动会影响长连接,且故障节点若未撤公告会持续收流量。两者常叠加:Anycast 做入口就近,DNS 做机房级容量调度。预热(preheat)与刷新(purge) 方向相反,面试常混:预热是主动把内容推到边缘(上线前拉一遍热门文件,避免上线瞬间全量回源打垮源站);刷新是主动让边缘副本失效(发布新版本后强制回源)。刷新本质上牺牲命中率换一致性,因此发布后短时间内回源率会尖峰上升。至于动态加速,对不可缓存的请求,CDN 的价值从"缓存"转为"路径优化":边缘与源站之间保持长连接复用(省掉每次 TCP + TLS 握手,见第 20 章 20.5 节)、使用更优的传输协议(如基于 UDP 的 QUIC,见第 15 章)、选更优的回源路径。这时该看回源时延与回源成功率,而不是命中率。
19.6 一次"网站访问慢"的分层排查法(主线案例)
排障第一原则是只改变一个变量。先用一条命令把一次 HTTP 请求切成若干时间段,再决定往哪一层深挖:
| |
| 时间段(差值) | 含义 | 异常阈值(同城口径) | 异常时的根因清单 |
|---|---|---|---|
time_namelookup | 从发起到 DNS 解析完成 | > 100 ms | resolv.conf 指向的解析器不可达或跨网;CNAME 链过长;负缓存/权威抖动;nsswitch.conf 里 dns 排在 files 前多走一次 |
time_connect − time_namelookup | TCP 三次握手耗时 | > 3 × RTT 或 > 50 ms | 服务端 SYN 队列满(tcp_max_syn_backlog/somaxconn);中间设备丢包;跨地域(先量 RTT);SYN 重传 |
time_appconnect − time_connect | TLS 握手耗时 | > 2 × RTT 或 > 100 ms | TLS 1.2 的 2-RTT 握手(对比见第 20 章 20.5 节);证书链过大、未启用会话复用、OCSP 未装订;CPU 算力不足 |
time_pretransfer − time_appconnect | 握手完成到即将发请求 | 接近 0 | 明显偏大说明在等代理或重定向链,或客户端自己在忙 |
time_starttransfer − time_pretransfer | 发出请求到收到首字节,即 TTFB | > 200 ms | 应用层慢(下游依赖、慢 SQL、锁);服务端排队(后端并发已满);CDN 回源(边缘未命中);不是网络问题 |
time_total − time_starttransfer | 收到首字节到传完 | 与响应体大小/带宽比对 | 带宽瓶颈;丢包导致拥塞窗口上不去;响应体未压缩;最后一公里差 |
按图索骥的命令序列,不要一上来就抓包:
| |
重要
time_starttransfer 偏大而前三段正常,是应用层问题不是网络问题。这条判断能在面试中直接止损:很多人的错误回答是把 TTFB 高也归为"网络慢",然后一路去查路由和丢包,方向从一开始就错了。
19.6.1 curl -w 全部时间指标表
| 变量 | 起点 → 终点 | 与前一指标的差值含义 | 典型阈值(同城口径) |
|---|---|---|---|
time_namelookup | 命令起始 → DNS 解析完成 | 纯 DNS 耗时 | < 50 ms;> 100 ms 异常 |
time_connect | 命令起始 → TCP 建连完成 | −namelookup = 握手 RTT | ≈ 1 RTT;同城 0.1~0.5 ms 量级 |
time_appconnect | 命令起始 → TLS 等握手完成 | −connect = TLS 握手 | TLS 1.3 ≈ 1 RTT,TLS 1.2 ≈ 2 RTT |
time_pretransfer | 命令起始 → 即将发出请求 | −appconnect ≈ 0 | 明显大于 0 说明有代理/重定向/客户端阻塞 |
time_starttransfer | 命令起始 → 收到首字节 | −pretransfer = 服务端处理 + 首包回程 = TTFB | < 200 ms;> 500 ms 需查应用 |
time_redirect | 所有重定向(含 DNS/建连/握手)累计 | 多次跳转的累计耗时 | 存在重定向时非 0,跳数越多越不可控 |
time_total | 命令起始 → 全部完成 | −starttransfer = 内容传输 | 与 size_download / 带宽 相符即可 |
两个易踩的坑:这些 time_* 是相对命令起点的累计量而非分段量,必须做减法;time_appconnect 在非 TLS 请求(如 http://)下为 0,此时 time_pretransfer − time_connect 也接近 0,不要错误解读为"TLS 没耗时"。
19.7 抓包与协议分析:tcpdump 看什么
| |
tcp[tcpflags] 的位含义固定:tcp-fin=0x01、tcp-syn=0x02、tcp-rst=0x04、tcp-push=0x08、tcp-ack=0x10、tcp-urg=0x20。
| 观察对象 | 正常样子 | 异常含义 |
|---|---|---|
| 三次握手 | SYN → SYN,ACK → ACK 三包齐全,间隔 ≈ 1 RTT | 只有 SYN 无应答:未监听、SYN 队列满、中间设备丢包;出现 SYN 重传即丢包证据 |
| 重传 | 偶发,间隔近似指数退避(RTO 计算见第 17 章 17.2 节) | 大量重传 = 链路丢包,或拥塞窗口被压到很小 |
| 重复 ACK | 偶尔出现 | 连续三个重复 ACK 提示丢包并触发快速重传 |
RST | 正常关闭流程里少见 | 应用进程退出、连接被中间设备重置、端口未监听;“谁发 RST 谁有问题"要结合方向判断 |
| 零窗口 | 不应长期出现 | 接收缓冲区被打满 → 应用读取太慢(不是网络问题) |
| TLS 握手 | ClientHello 到 Finished 的往返数固定 | 往返数偏多说明未复用会话或证书链过长(见第 20 章 20.5 节) |
| 传输段大小 | 稳定在 MSS 附近(常见 1460) | 长期小于 MSS 说明路径 MTU 受限、拥塞窗口小或应用写得零碎 |
警告
“抓包看到慢"不等于"服务端慢”。 抓包点决定了你能看到的因果:在客户端抓,你看到的是"字节到达客户端慢”,它可能来自服务端、可能来自中间链路、也可能来自客户端自己读取慢(零窗口)。要定位责任方,必须在两端同时抓并比对时间戳,或至少先确认接收窗口未被打满。
Wireshark 里两个必用功能:Follow → TCP Stream 把一条连接的双向载荷按序拼成可读流,用来确认应用层到底发了什么(响应体是否被压缩、是否少了某个头部),注意它按连接过滤,多连接时要先在包列表里选中一条会话;Statistics → TCP Stream Graphs 的 Throughput 图给出实际速率随时间的变化,Time-Sequence (Stevens) 图给出序列号随时间上升的斜率——斜率变平就是停等或阻塞,锯齿状回落通常是丢包/重传导致窗口收缩。这两张图是把"抓包"变成"证据"的关键。
19.8 连通性工具的能力边界
面试高频辨析题几乎都围绕"某个工具说通/不通,能不能证明服务可用"。核心原则:每个工具只探测它自己那一层的语义,跨层推断就是错的。
| 工具 | 实际探测的东西 | 能力边界(关键) | 替代手段 |
|---|---|---|---|
ping | ICMP echo request/reply 是否往返 | 显示"通"不保证端口可达;被防火墙丢 ICMP 时显示"不通"但服务完全可用 | nc -vz/curl 直接测端口;tcpdump icmp 确认丢在哪一侧 |
traceroute | 逐跳增大 TTL 收集 ICMP Time Exceeded | 依赖 ICMP 回包,屏蔽就显示 *;不反映实际转发路径(多路径/负载均衡导致每跳不同) | mtr 连续统计;traceroute -T -p 443 用 TCP 探测绕过 ICMP 屏蔽 |
mtr | 持续采样的逐跳丢包与时延 | 同样受 ICMP 策略影响;中间跳丢包不代表端到端丢包 | 关注最后一跳的丢包与抖动,而非中间跳 |
telnet / nc -vz | TCP 建连是否成功(nc -z 不发数据) | 只证明三次握手成功,不证明应用能正确响应;UDP 模式结论不可靠(无握手) | curl -v 看应用层响应;openssl s_client 验 TLS |
ss | 本机套接字状态与队列 | 只能看本机;不产生流量,故不验证对端 | 配合 ss -lntp、ss -tn state established 看积压 |
curl -w | 端到端各阶段耗时 | 只给时间,不给原因 | 与抓包、服务端日志对齐定位 |
ICMP 被禁时的替代手段(把"ping 不通"继续往下推):一是 TCP 建连探测,nc -vz -w 3 host port,或 traceroute -T -p 443 host 用 TCP SYN 探路;二是应用层探测,curl -v --max-time 5 看是否拿到状态码,openssl s_client -connect host:443 -servername host 验证证书链;三是抓包确认责任方,tcpdump -nn 'icmp or tcp port 443' 区分"没发包"、“发了没回”、“回了被丢”;四是绕开本机网络栈干扰,mtr --tcp --port 443 host、多网卡用 -a/-I 指定源地址对比。
小心
把 traceroute 输出里的 * 当成"这一跳坏了"是最常见的错误回答。* 只是"这一跳没有回 ICMP 超时报文",很多骨干设备就是这么配的;真正有意义的是从某一跳开始丢包率持续到最后一跳。面试官追问"那你怎么判断"时,正确答案是"看 mtr 的最后一跳与端到端丢包,而不是中间跳"。
本章小结
| 关键结论 | 一句话表述 | 常见错误 |
|---|---|---|
| 递归与迭代的分界 | 客户端→本地 DNS 是递归(RD=1),本地 DNS→根→TLD→权威是迭代(RD=0),根服务器从不做递归 | 把整条链说成"一层层递归问下去" |
| 缓存优先级 | /etc/hosts 与 DNS 的先后由 nsswitch.conf 的 hosts: 行决定 | 认为 hosts 永远第一,忽略应用自身的 DNS 缓存 |
| 负缓存 | NXDOMAIN 会按 SOA 的 minimum 被缓存,“新域名解析不出来"不一定是配置错 | 把 NXDOMAIN 一律当成"记录没配” |
| TTL 的作用 | TTL 是"最坏生效时间的上界";切换前调小、切换后调回 | 直接改 IP 而 TTL 仍是 86400,部分用户长期打到旧机 |
| CNAME 的限制 | 名字一旦是 CNAME 就不能承载其他类型;zone apex 必须有 NS/SOA | 说"根域名 CNAME 只是不推荐",把规范约束说成风格问题 |
| DNS 负载均衡的缺陷 | 摘除受 TTL 约束、无法感知服务端真实负载,只能做机房级粗粒度分流 | 答"用 DNS 做负载均衡就够了" |
TC 与 EDNS0 | UDP 传统上限 512 字节,超限置 TC=1 必须改 TCP 重查;EDNS0 扩大承载并承载 DO 位 | 把 TC=1 解释成"服务器过载" |
| 慢的分层定位 | 用 curl -w 的差值把问题切到 DNS/建连/TLS/TTFB/传输五段,再进对应工具 | 一上来就抓包;把 TTFB 高归因为"网络慢" |
| 抓包的局限 | 单点抓包只证明"字节到达该点的快慢",不能直接判定责任方 | 由"抓包看到慢"直接得出"服务端慢" |
| 工具边界 | ping 通不代表端口通,traceroute 的 * 不代表该跳故障,telnet 通不代表应用正常 | 用 ICMP 结论推断应用可用性 |
自测题
Q1(推导)某域名的 A 记录 TTL 为 86400,你计划在明天 10:00 把服务从旧机房切到新机房。请给出完整的切换步骤与每一步的等待时长依据,并说明不做这一步会发生什么。
答案与解析
标准答案是两阶段 TTL 收敛:(1) 立即把 TTL 从 86400 改为 60,然后等待至少 86400 s(24 h),让所有仍缓存旧的长 TTL 的解析器过期,等待时长的依据是"旧 TTL 是上界",不能缩短;(2) 在 10:00 把 A 记录指向新机房,等约 60 s 让新值扩散,观察新机房入口流量上升;(3) 确认旧机房流量归零后下线旧机器,再把 TTL 调回 86400。
如果不做第 (1) 步就直接改:最坏情况下,那些在改之前刚查过记录的解析器会继续用旧 IP 长达 24 小时,表现为"一部分用户访问到已下线的旧机房",比例随缓存分布抖动,极难复现。
错在哪:常见错误是答"把 TTL 设成 0 就能立刻生效"——TTL=0 只表示"不要复用",多数解析器有秒级下限,客户端缓存(浏览器、JVM、Go 的 DNS 缓存)更不受你的 TTL 支配。另一个错误是只答"提前调小 TTL"而说不出要等多久,面试官追问"提前多久"就是在验证你是否知道 TTL 是上界而非周期。追问方向:调小 TTL 的代价是什么(解析器查询量上升、权威侧压力变化)。
Q2(辨析)同事说:"example.com 的裸域我直接加一条 CNAME 指向 CDN 就行了。“请指出这条方案的问题,并给出两种可落地的替代。
答案与解析
问题:example.com 是区的顶点(zone apex),按 RFC 1034/1035 它必须承载该区的 NS 与 SOA;而一旦某个名字被声明为 CNAME,别名语义就独占该名字,不允许存在其他类型的数据。二者冲突,所以裸域不能是 CNAME。同理可解释"CNAME 不能与 MX 共存”:example.com 若既是 CNAME 又挂 MX,解析器追到别名后,原名字上的 MX 就永久取不到。
两种替代:(1) 用 A/AAAA 记录直接指向 CDN 提供的固定入口 IP,代价是 IP 变更需跟随;(2) 使用厂商提供的类 CNAME 顶点别名记录(叫法各异,如 ANAME/ALIAS),由解析服务端在应答时把目标解析成 A 记录再返回,从而不违反规范地实现"裸域走 CDN"。
错在哪:把规范约束当成"厂商限制"或"不推荐",回答里说不出"zone apex 必须有 NS/SOA"这个根因。追问方向:MX 指向 CNAME 为什么也被禁止?正确答案是规范要求 MX 的目标不应是 CNAME,它引入额外解析跳数与失败面;进一步还会问你 CNAME 链每一跳对 RTT 与 SERVFAIL 风险的影响。
Q3(计算)一次 curl -w 输出为 time_namelookup=0.004、time_connect=0.052、time_appconnect=0.158、time_pretransfer=0.159、time_starttransfer=1.310、time_total=1.340(秒,--resolve 直连某机房,http_code=200,响应体 12 KB)。请把总耗时拆成五段,指出问题在哪一层,并给出下一步两个验证动作。
答案与解析
分段(全部做减法):
| |
结论:瓶颈在 TTFB(服务端处理时间);建连与 TLS 偏高提示链路 RTT 偏大(可能跨地域或路径绕行),但不是主因。
下一步验证:(1) 用 -H 'Host: ...' 直连源站(绕过 CDN)对比 time_starttransfer,若直连快则问题在 CDN 回源或调度;(2) 在服务端对齐同一时刻的应用日志/链路追踪,判断是下游依赖、慢查询还是排队,同时用 mtr --tcp --port 443 确认 48 ms 的建连是否来自跨地域路由。
错在哪:最常见的是"总耗时 1.34 s,网络慢",把 TTFB 也算进网络;另一个是把 time_appconnect 直接当 TLS 耗时(它是相对命令起点的累计量,必须减去 time_connect)。追问方向:如果 time_total − time_starttransfer 很大而 TTFB 正常,你会怀疑什么?答案是带宽瓶颈、丢包导致拥塞窗口受限、响应体未压缩,或最后一公里质量差。
Q4(判断)“我用 ping www.example.com 能通,说明 443 端口是开的、网站是好的。“请判断并说明,同时给出这个结论会漏掉的三类故障。
答案与解析
判断:错。ping 只证明 ICMP echo 有去有回,即"该主机在 IP 层可达”,完全不涉及 TCP 端口、监听状态与应用健康。反向也不成立:ping 不通而服务完全可用是常见情形,因为很多安全组/防火墙只放行所需的 TCP 端口而显式丢弃 ICMP。
会漏掉的三类故障:(1) 端口未监听或被策略拒绝——ping 通但 443 无监听,SYN 得到 RST;或防火墙 DROP,表现为 SYN 重传后超时;(2) 应用层异常——端口开着但返回 502/503 或响应体错误;(3) TLS 层异常——证书过期、SNI 与证书不匹配、证书链缺中间证书,此时 TCP 通而 HTTPS 请求失败。
正确做法是分层验证:nc -vz -w 3 host 443 验证端口 → curl -v --max-time 5 https://host/ 验证应用与 TLS → openssl s_client -connect host:443 -servername host 单独验证证书链。若 ping 不通而业务正常,先确认 ICMP 是否被策略屏蔽(tcpdump -nn icmp 看是否根本没回包),不要急着改网络。
追问方向:telnet host 443 成功说明了什么?——只说明 TCP 三次握手完成(见第 16 章 16.1 节),不说明应用能正确应答,也不说明 TLS 能协商成功。
Q5(设计)线上告警"某地域用户访问慢”,该地域用户走 CDN。请设计一套从 DNS 到 CDN 到源站的排查顺序,写明每一步的命令、要看的指标,以及"看到什么就跳到下一步"的判据。
答案与解析
按"由外向内、由粗到细"排序,每步都有明确跳转判据:
| |
设计要点(也是加分点):不要从步骤 5 开始,抓包成本最高且最难归因,只在时间指标已把范围收窄后才用;所有时间结论都要多次采样(单次会被抖动骗);并且必须跨地域对比——“某地域慢"本身意味着需要一个对照组的数字,否则无法区分"该地域的网络问题"与"全网都慢只是这个地域先被报出来”。
错在哪:把"用户慢"直接当成"CDN 慢"或"源站慢",跳过 DNS 调度这一步,而调度错误(用户被指到远端节点)恰恰是"某地域慢"最常见的原因。追问方向:如果该地域用户全被调度到同一个远端节点,你先怀疑什么?优先怀疑解析器位置失真(用户使用集中化的公共解析器,geo 判断基于解析器而非用户)与 EDNS Client Subnet 未启用或被剥离。
面试高频问题与陷阱
| 面试问法 | 考查角度 | 陷阱 / 加分点 | 对应本章小节 |
|---|---|---|---|
| 请完整讲一下 DNS 的解析过程,递归和迭代分别发生在哪一段? | 是否把两段责任划分讲清 | 错误回答:把浏览器缓存、根服务器、权威服务器串成一条"递归链",说"根服务器帮我递归查到结果"——根服务器从不做递归;加分:点出 RD/RA 标志位与本地 DNS 服务器是分界点 | 19.1.1 |
| 为什么 CNAME 记录不能和 MX 记录共存?为什么根域名不能用 CNAME? | 对 RRset 语义与区顶点约束的理解 | 错误回答:“因为厂商不支持"或"只是不推荐”——说不出"名字一旦是 CNAME 就不能承载其他类型"以及"zone apex 必须有 NS/SOA";加分:给出 ANAME/ALIAS 这类工程替代 | 19.2.2 |
| 我们准备把服务切到新机房,为什么上线前要先把 TTL 调小? | 是否理解 TTL 是"最坏生效上界" | 错误回答:“TTL 是缓存时间,调小能快点生效”,但说不出要提前多久(必须是旧 TTL 的时长)与调小 TTL 的代价(解析器查询量上升);加分:给出 86400 → 60 的算例 | 19.1.3 |
| 用 DNS 做负载均衡有什么问题? | 是否知道 DNS 的固有缺陷 | 错误回答:“轮询/加权/就近就够了”——说不出摘除受 TTL 约束、DNS 协议里没有回传服务端负载的字段;加分:说明 DNS 只做机房级粗粒度分流,精细负载交给四层/七层 | 19.3 |
ping 通是不是说明服务就是好的?traceroute 里出现 * 是不是说明那一跳坏了? | 工具能力边界,两个问题常连着问 | 错误回答:用 ICMP 结论推断端口/应用可用;把 * 当故障跳。加分:说明 ICMP 常被策略丢弃、* 只表示该跳不回超时报文,真正有意义的是末跳丢包 | 19.8 |
| 用户反馈访问慢,你怎么排查? | 是否有分层方法论而非乱试 | 错误回答:“先抓包看看"或"重启一下”——没有把问题先切到 DNS/建连/TLS/TTFB/传输五段;加分:给出 curl -w 差值定位法与每段根因清单,并强调多次采样 | 19.6 |
| 抓包看到响应很慢,能说明是服务端慢吗? | 因果与观测点 | 错误回答:“抓包慢就是服务端慢”——忽略了抓包点、中间链路与客户端接收窗口;加分:说明必须在两端同时抓并比对时间戳,或先确认接收窗口未为零 | 19.7 |
time_appconnect 是 0,是不是说明 TLS 握手没有耗时? | 指标口径 | 错误回答:“是,TLS 很快”——实际 time_* 是累计量,且非 TLS 请求该值为 0;加分:说明所有 time_* 都要做减法,并区分 TLS 1.2 与 1.3 的往返数 | 19.6.1 |
| CDN 命中率突然下降,你会从哪几个方向查? | 对缓存键与源站指令的理解 | 错误回答:“多加点机器"或"把 TTL 调长”——说不出 URL 随机参数、Vary/Cookie 维度、Cache-Control 指令、请求合并;加分:区分"缓存策略失效"与"内容本就动态"(该走动态加速) | 19.5 |
为什么 dig 比 nslookup 更适合排障? | 工具输出的信息量 | 错误回答:"dig 更新更现代"——说不出 nslookup 不显示 flags/TTL/AUTHORITY 段,因此无法判断答案来自缓存还是权威;加分:提到 +trace、+tcp、+bufsize 才能复现截断与迭代链路 | 19.4 |
延伸阅读
- RFC 1034《Domain names - concepts and facilities》:递归/迭代、委托与 zone 概念的第一手定义,19.1 节的路径划分以它为准。
- RFC 1035《Domain names - implementation and specification》:报文格式、512 字节 UDP 上限与各记录类型的字段定义。
- RFC 2181《Clarifications to the DNS Specification》:CNAME 与 MX/NS 目标关系的澄清,19.2.2 节的规范依据。
- RFC 2308《Negative Caching of DNS Queries》:负缓存机制与 SOA
minimum的口径。 - RFC 6891《Extension Mechanisms for DNS (EDNS(0))》:EDNS0 伪记录、UDP 承载能力协商与
DO位。 - RFC 8499《DNS Terminology》:术语标准化,写文档与答辩时统一用词。
man 1 dig、man 8 tcpdump、man 5 nsswitch.conf、man 5 resolv.conf:本章命令行为与配置文件优先级的权威口径。- Wireshark 官方文档中
Statistics → TCP Stream Graphs一节:吞吐图与 Stevens 时间序列图的读法。