你有没有遇到过这种情况:公司线上服务突然一堆用户反馈打不开网页,你赶紧打开CDN控制台想切流量到备用源站,结果点了半天按钮,流量纹丝不动。再一看监控,CDN节点还在拼命请求那个已经“挂了”的源站IP。这时候你心里肯定在骂:明明CDN配置都改了,为什么就是不生效?

其实很多时候,问题压根不在CDN配置上,而是出在DNS解析这一层。DNS是我们访问互联网的第一道门,如果这道门指错了方向,后面CDN再怎么切流都是白搭。我做运维这几年,最怕的就是这种“表面看是CDN的问题,其实是DNS在捣鬼”的隐秘故障。今天就用一个真实例子,带你一步步排查CNAME记录,顺便聊聊智能DNS的调优策略。

二、先搞清楚CDN切流和DNS的关系

2.1 CDN是怎么把用户带到最近节点的

CDN的工作原理很简单:你的域名通过CNAME记录指向CDN服务商提供的域名,比如 cdn.example.com。用户访问你的网站时,本地DNS先解析你的域名,拿到CNAME记录,然后继续解析CDN域名,CDN的智能DNS会根据用户所在地、运营商、负载情况,返回一个最优的CDN节点IP。

这样一来,用户就到最近的CDN节点了。CDN节点缓存了你的内容,用户访问速度自然快。如果源站出问题,你在CDN控制台把源站切换成备用源站,CDN节点就会去新源站拉数据。听起来很顺理成章对吧?但有个前提:用户解析到的CDN节点,必须是你修改配置后能感知到的节点。如果用户走了旧节点,或者DNS缓存还没刷新,切流就像对着空气挥拳。

2.2 CNAME记录就是CDN的“路标”

CNAME记录就是一个域名的别名。比如你有个域名 www.trouble.com,CDN服务商要求你把 www.trouble.com 用CNAME指向 www.trouble.com.cdn.cdnprovider.com。这就是在告诉解析器:你访问 www.trouble.com 的时候,请继续去解析那个CDN域名,然后从CDN的DNS拿到节点IP。

如果CNAME记录配置错了,或者没生效,用户根本到不了CDN,更别提让CDN帮你切源站了。实际排查中,最常见的就是这几种情况:

  • CNAME记录被覆盖成了A记录,导致直接解析到源站IP。
  • CNAME后面还挂着其他记录,但CDN服务商要求不能有冲突。
  • 配置了多条CNAME,但不同线路指向了不同CDN厂商,切流时只切了一家。

下面我们进入正题,看看一次完整的排查过程。

三、典型案例:切流按钮失灵,原来是CNAME在骗你

3.1 故障现象描述

某天下午,客户反馈官网打不开。我们检查发现源站A故障,于是登录CDN控制台,把域名 www.trouble.com 的回源地址从源站A改成备用源站B。等了几分钟,再测试,发现访问还是返回503。用 dig 一看,用户解析到的IP根本不是CDN节点,而是源站A的IP。这就诡异了,明明CNAME记录还在,怎么会直接解析到源站?

3.2 排查第一步:用dig命令查看当前解析链路

我们先用dig查看 www.trouble.com 的完整解析过程。这里我们的技术栈是 Linux Bash + dig 命令,后续所有示例都基于这个技术栈。

# 查询A记录,看看域名目前解析到什么IP
# +trace 会显示完整的解析链路,非常有助于定位问题
dig www.trouble.com A +trace

执行输出大概是这样的:

; <<>> DiG 9.18.0 <<>> www.trouble.com A +trace
;; global options: +cmd
.			59515	IN	NS	a.root-servers.net.
;; Received 509 bytes from 192.168.1.1#53(192.168.1.1) in 8 ms

com.			172800	IN	NS	a.gtld-servers.net.
;; Received 502 bytes from 192.168.1.1#53(192.168.1.1) in 12 ms

trouble.com.		3600	IN	NS	ns1.dnsprovider.com.
;; Received 456 bytes from 192.168.1.1#53(192.168.1.1) in 15 ms

www.trouble.com.	300	IN	A	203.0.113.10
;; Received 80 bytes from 203.0.113.1#53(203.0.113.1) in 10 ms

看到没有?最后一条是A记录,而不是CNAME记录203.0.113.10 正是源站A的IP。这说明在权威DNS服务器上,www.trouble.com 的CNAME记录可能丢了,或者被改成了A记录。

3.3 排查第二步:检查权威DNS上的记录

我们登录DNS服务商后台,查看 www.trouble.com 的解析记录。结果发现,有一条A记录指向源站A,而且CNAME记录居然在另一个子域名上。再仔细一看,原来运维前两天为了“加速”配置,把CNAME记录删掉手动加了一条A记录。这就是典型的“好心办坏事”。

3.4 排查第三步:验证CNAME记录是否生效

找到原因后,我们把A记录删掉,重新添加CNAME记录,指向CDN服务商提供的域名。然后再次用dig验证:

# 查询CNAME记录,验证是否配置成功
dig www.trouble.com CNAME +short

输出:

www.trouble.com.cdn.cdnprovider.com.

CNAME已经生效。但还没完,用户电脑上的DNS缓存可能还是旧的,所以接着用 dig 模拟递归解析:

# 指定公共DNS重新解析,绕过本地缓存
# @8.8.8.8 表示使用Google的公共DNS
dig www.trouble.com @8.8.8.8 +short

输出:

www.trouble.com.cdn.cdnprovider.com.
203.0.113.200

看到 203.0.113.200 就是CDN节点IP,切流成功了。但是等等,为什么切流后还是有问题?因为用户那边的递归DNS缓存了旧的A记录,TTL还没过期。所以我们继续在CDN配置里把TTL调短,让用户尽快刷新。

四、CNAME记录容易踩的坑,一次性讲透

4.1 CNAME不能与其它记录共存

这是一个老生常谈但总有人犯的错。在某些DNS服务商中,www 这个主机名如果已经有了一条A记录,就不能再添加CNAME记录。反之亦然。所以你在添加CNAME之前,一定要先删除同名的A记录,否则要么添加失败,要么DNS解析行为变得不可预测。

4.2 CNAME的TTL设置

TTL(生存时间)决定了其他DNS服务器缓存这条记录多久。TTL太短,会增加解析压力;太长,会导致切流后用户很久无法感知。一般情况下:

  • 切流期间,把TTL调成30秒或60秒。
  • 稳定运行期,可以调回600秒或3600秒。

但要注意,TTL的修改只能影响未来的解析,已经缓存在用户电脑、路由器、运营商缓存服务器上的记录,还是要等旧TTL过期。

4.3 智能DNS与CNAME的配合

智能DNS会根据用户来源IP返回不同的解析结果。比如电信用户解析到电信的CDN节点,联通用户解析到联通的节点。这通常是CDN厂商自己的DNS完成的。但如果你在自建DNS上使用了“分线路解析”,那要特别小心:

  • 如果CNAME记录本身不带线路,但是A记录带了线路,可能导致某些用户不走CNAME。
  • 如果多个线路的CNAME指向不同CDN厂商,切流时可能只切了一部分,造成“半瘫”。

下面演示一个智能DNS的配置思路。假设我们自己搭建了一个DNS服务器,使用开源软件 PowerDNS,配置两个view,让电信和联通用户走不同CDN厂商。

五、智能DNS调优实战

5.1 先理解智能DNS要解决什么问题

智能DNS的初衷,是让不同位置的用户都能找到最近的服务器。你打开淘宝,DNS会自动根据你的IP判断你的地理位置,给你分配最近的机房。我们这里不需要自己实现那么复杂的算法,只需要用CDN的智能DNS即可。

但有时候,你可能会同时使用多家CDN。比如国内用户用厂商A,海外用户用厂商B。这时候,你的权威DNS就需要具备“按客户端IP返回不同CNAME”的能力。这就叫“智能解析”或“分线路解析”。

5.2 使用PowerDNS配置分线路CNAME

我们以 PowerDNSgeoip 后端为例,展示如何根据请求IP地址段返回不同的CNAME。但为了保持示例简单,我们直接用 bind9view 功能来演示,这也是最经典的方案。

假设我们有两个CDN厂商:

  • 国内IDC用 cdn-a.example.com
  • 海外用户用 cdn-b.example.com

我们要让国内IP解析 www.trouble.com 时返回 www.trouble.com.cdn-a.example.com,海外IP则返回 www.trouble.com.cdn-b.example.com

bind9配置如下(技术栈仍是 Linux Bash + bind9 配置文件):

# /etc/bind/named.conf
// 定义国内IP地址段,这里用CIDR表示
acl "china_net" {
    122.0.0.0/8;
    123.0.0.0/8;
};

view "china_view" {
    match-clients { china_net; };   # 只要源IP匹配china_net,就使用这个view

    zone "trouble.com" {
        type master;
        file "/etc/bind/db.trouble.com.china";   # 国内配置,CNAME指向CDN A
    };
};

view "default_view" {
    match-clients { any; };   # 其它所有IP,走这个view

    zone "trouble.com" {
        type master;
        file "/etc/bind/db.trouble.com.default";  # 海外配置,CNAME指向CDN B
    };
};

然后创建两个解析文件。中国区的:

# /etc/bind/db.trouble.com.china
$TTL 300
@   IN  SOA ns1.trouble.com. admin.trouble.com. (
            2025080401 ; 序列号,要记得递增
            3600       ; 刷新周期
            600        ; 重试时间
            86400      ; 过期时间
            300 )      ; 最小TTL

; 指定该域名的权威DNS服务器
@   IN  NS  ns1.trouble.com.
ns1 IN  A   192.0.2.53

; 核心:www 使用CNAME指向CDN A
www IN  CNAME   www.trouble.com.cdn-a.example.com.

默认区(海外):

# /etc/bind/db.trouble.com.default
$TTL 300
@   IN  SOA ns1.trouble.com. admin.trouble.com. (
            2025080401
            3600
            600
            86400
            300 )

@   IN  NS  ns1.trouble.com.
ns1 IN  A   192.0.2.53

; 海外用户指向CDN B
www IN  CNAME   www.trouble.com.cdn-b.example.com.

配置完成后,用下面命令重新加载bind9:

# 检查配置语法是否有误
named-checkconf /etc/bind/named.conf

# 重新加载配置
rndc reload

然后模拟一个国内IP去查询:

# 指定源地址为122.1.1.1,使用dig +subnet选项模拟国内用户
dig www.trouble.com CNAME @192.0.2.53 +subnet=122.0.0.0/24 +short

输出:

www.trouble.com.cdn-a.example.com.

再模拟海外IP:

# 指定源地址为200.1.2.3,模拟海外用户
dig www.trouble.com CNAME @192.0.2.53 +subnet=200.0.0.0/8 +short

输出:

www.trouble.com.cdn-b.example.com.

这样,智能DNS就工作了。但是注意,+subnet 只是模拟,实际生产环境中,真正的客户端IP可能被上层递归DNS代理,导致无法精准识别。这时候你就需要启用 EDNS Client Subnet(ECS)功能,让递归DNS把用户IP的子网信息传递给权威DNS。bind9也支持这个配置,但我们这里不展开,因为大部分CDN厂商自己已经实现了更好的处理。

5.3 智能DNS调优策略

策略一:缩短TTL,快速响应变更

智能DNS的核心价值是灵活调度。如果你把TTL设成1小时,那么即使你调整了CDN厂商,用户也要等1小时后才能切换到新节点。所以建议初始TTL设置 60 秒,等解析稳定后再增加到 300 秒或 600 秒。

策略二:配置多条备用CNAME

如果某个CDN厂商的线路整体出问题,你可能需要在DNS层面把流量切到另一个厂商。这时候你可以利用“存活健康检查”来自动切换,也可以手动修改。手动切换时,记得要同时修改所有view里的CNAME。

策略三:监控DNS解析成功率

要使用工具比如 dnsperfprometheus + unbound_exporter 来监控DNS解析的延迟和错误率。只有这样,你才能在用户还没感知到异常之前发现问题。

下面给一个简单的一行脚本,用于持续监测解析是否正常(技术栈仍是bash):

# 每分钟查询一次,如果返回的CNAME包含cdn-b,就说明切到海外节点了
# 如果输出异常,会通过curl发送告警到钉钉(示例仅演示解析逻辑)
while true; do
    result=$(dig www.trouble.com CNAME @192.0.2.53 +short)
    if [[ "$result" != *"cdn-a.example.com"* ]]; then
        echo "已切换或异常: $result"
    fi
    sleep 60
done

六、CDN切流失败的其它排查点

6.1 检查解析到的CDN节点是否真的活着

有时候你配置的CNAME没问题,但CDN节点的健康检查可能误判。比如源站A挂了,但CDN节点还认为它活着,于是继续回源到A,用户依然503。这时可以用 curl 查看返回头:

# 查看响应头中X-Cache和Via字段,判断是否走了CDN节点
# -I 只获取HTTP头
curl -I http://www.trouble.com/

输出示例:

HTTP/1.1 200 OK
Via: 1.1 cdn-node12
X-Cache: HIT
X-Swift-SaveTime: Mon, 04 Aug 2025 10:00:00 GMT

如果 X-CacheMISS,说明CDN节点上没有缓存,它正在回源。如果回源源站已经切到B,那应该没问题。但如果 Via 显示还是旧节点,那可能是DNS解析到了旧节点,需要刷新DNS缓存。

6.2 检查源站配置是否真的生效

登录CDN控制台,查看回源配置。有些CDN厂商要求你关闭“回源跟随301/302”,否则如果源站A返回一个重定向,CDN可能会跟着跳转,导致用户看到“太多的重定向”错误。

6.3 检查HTTPS证书

切流后,新的CDN节点可能没有绑定你的域名SSL证书,导致用户访问时报证书错误。所以切流之前,要提前在CDN控制台上传好备用源的证书,或者启用CDN的免费证书。

七、从一次故障,总结出几点心声

7.1 技术优缺点

CNAME的优点是配置简单、兼容性好,几乎所有DNS服务商都支持。缺点是它本身只是一个“别名”,不能直接设置优先级,也不能用于根域名(因为某些服务商限制)。另外,CNAME解析会增加一次额外的DNS查询,可能让首字节时间变长一点点。

智能DNS的优点是灵活,可以根据用户地域和运营商做精细调度。缺点是配置复杂度高,出错率高,而且依赖底层数据准确性。比如IP地址段库如果不更新,可能把电信用户归到了联通线路上。

7.2 注意事项

  • CNAME记录与其它记录冲突时,优先清理A记录。
  • 切流前,提前把TTL调低,至少要等一个旧TTL周期再操作。
  • 在多个DNS服务商之间做轮询或备援时,一定保证各服务商上的记录一致,避免“脑裂”。
  • 修改完DNS记录后,要多地、多运营商测一下,不要只看本机解析结果。
  • 一定要开启CDN的源站健康检查,一旦源站故障,CDN能自动启用备用源站,而不是依赖你手动切。

7.3 彻底解决切流焦虑的思路

其实,完全可以不用每次手动切流。现在主流CDN都支持“源站故障自动切换”功能。你只需要在CDN控制台配置主源站和备用源站,并设置健康检查路径(比如 /health.html),当主源站连续失败3次,CDN节点会自动切到备用源站。这个功能比DNS切流更快、更可靠,因为它在CDN内部完成,不需要等DNS解析更新。

不过,即使用了自动切换,CNAME配置依然是基础。如果CNAME一开始就是错的,CDN根本回不到源站,再好的自动切换也没用。所以,把CNAME记录当成你的生命线,每次调整后都要反复检查。

八、总结一下整个排查思路

回看这次故障,我们发现问题不是CDN控制台出bug,也不是CDN节点不听话,而是因为有人擅自把CNAME改成了A记录,导致所有用户直接解析到源站A。源站A一挂,CDN当然救不了你。所以:

第一,CNAME是CDN的门牌号,弄错了用户就找不着CDN,一切后续操作都无意义。
第二,智能DNS很有用,但需要谨慎配置,千万别让分线路规则互相打架。
第三,切流前先检查TTL,给旧记录一点时间“消退”,否则你就是改了也白改。
第四,多靠自动化健康检查,别总是等到用户投诉才去手动切流。

最后,分享一个我自己的习惯:每次改完DNS记录,我都会同时做三件事——用 dig 查CNAME,用 curl 试访问,用 ping 看延迟。三件事都正常了,才敢拍着胸脯说“搞定”。希望这篇文章能帮你少踩几个坑,以后遇到类似故障,能少掉几根头发。