基于 DNS 的故障切换:最便宜的容灾方案

服务器挂了,能否自动把用户引导到备用机?DNS 故障切换是最便宜的做法,但也有它绕不过的限制。

一、原理

bash
健康检查定时探测主服务器
  ├─ 正常 → 解析保持指向主服务器
  └─ 失败 N 次 → 自动把 A 记录改成备用服务器 IP
     主服务器恢复 → 改回来

二、TTL 决定一切

这是 DNS 故障切换的核心限制

bash
用户本地缓存了旧 IP,要等 TTL 过期才会重新查询
TTL最快切换时间
3600s最多 1 小时
300s最多 5 分钟
60s最多 1 分钟

所以做故障切换必须把 TTL 设得很短(60~120 秒)。代价:

  • DNS 查询量增加;
  • 首次访问稍慢(缓存命中率降低);
  • 部分 DNS 服务器不遵守短 TTL(会强制拉长到几分钟)。

现实中的切换时间通常是 2~10 分钟,做不到秒级。需要秒级切换就得用 Anycast 或负载均衡器。

三、谁能提供

服务免费版说明
Cloudflare❌(Load Balancing 是付费)付费版功能完善
阿里云云解析❌(企业版)支持健康检查
DNSPod❌(企业版)
Route 53❌(按量付费)健康检查成熟
自建脚本用 API 自己实现

四、免费方案:自己写脚本

思路很简单,用 Cloudflare API + 一个健康检查:

bash
#!/usr/bin/env bash
# /opt/scripts/dns-failover.sh
set -euo pipefail

CF_TOKEN="..."
ZONE_ID="..."
RECORD_NAME="www.bzii.cn"
REC_ID="..."
PRIMARY="1.2.3.4"
BACKUP="5.6.7.8"

# 探测主服务器
if curl -s --max-time 5 -o /dev/null -w "%{http_code}" https://$PRIMARY/health | grep -q 200; then
  TARGET=$PRIMARY
  STATE="主服务器正常"
else
  TARGET=$BACKUP
  STATE="主服务器故障,切到备用"
fi

# 查当前值
CUR=$(dig +short $RECORD_NAME A @1.1.1.1 | head -1)

if [ "$CUR" != "$TARGET" ]; then
  curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$REC_ID" \
    -H "Authorization: Bearer $CF_TOKEN" \
    -H "Content-Type: application/json" \
    --data "{\"content\":\"$TARGET\",\"ttl\":60}" > /dev/null
  echo "[$(date)] $STATE: $CUR → $TARGET" >> /var/log/failover.log
fi
bash
chmod 600 /opt/scripts/dns-failover.sh
# 每分钟跑一次
* * * * * /opt/scripts/dns-failover.sh

关键点

  1. 健康检查要防抖动:连续失败 2~3 次才切换,避免网络抖动导致误切;
  2. 脚本要跑在第三台机器上:跑在主服务器上的话,主服务器挂了脚本也挂了;
  3. 要有防止反复切换的机制(flapping):加冷却时间;
  4. 切换时发告警,你需要知道发生了什么。

五、它解决不了什么

DNS 故障切换有明显局限:

局限说明
切换慢受 TTL 限制,分钟级
已建立的连接仍会失败DNS 只影响新连接
不检查服务是否真的可用只能探活端口/URL
用户端缓存不可控有些客户端会缓存更久
无法做负载分担只是主备,不是负载

六、什么时候够用

场景是否合适
个人站点、博客✅ 够用(能接受几分钟不可用)
内网服务、后台系统✅ 够用
电商、交易❌ 不够,需要负载均衡
要求 99.99% 可用性❌ 不够

七、更简单有效的替代方案

很多时候,与其做 DNS 故障切换,不如:

1. 用 CDN(Cloudflare)

静态资源在 CDN 边缘节点上,源站挂了用户仍能访问缓存内容。等于自动获得了大部分容灾能力,零配置。

bash
# 配合 "Always Online" 功能(Cloudflare 免费版有)
# 源站挂了会显示缓存的页面快照

2. 服务本身做高可用

systemd Restart=always + 云厂商的自动重启,能解决大部分「进程崩了」的情况,比切换 DNS 快得多。

3. 静态站多平台部署

静态站可以同时部署到 GitHub Pages、Vercel、自建服务器。真出事了手动改一次 DNS(几分钟),比维护自动切换简单。

八、我的看法

个人项目,我的排序是:

  1. 先让服务本身不挂(Restart=always + 监控告警);
  2. 再上 CDN(缓存吸收大部分影响);
  3. 有备用机且愿意折腾,才做 DNS 故障切换;
  4. 真的要高可用,用云负载均衡。

DNS 故障切换听起来很美,实际收益往往不如前两项。别为了架构的「完整性」增加运维复杂度。

九、如果要做,检查清单

  • TTL 设到 60~120 秒
  • 健康检查有失败阈值(防抖动)
  • 有冷却时间(防反复切换)
  • 探测脚本跑在独立的机器上
  • 切换时有告警通知
  • 定期演练(手动停主服务器测试)

DNS 故障切换的定位很清楚:便宜、简单、慢。 接受它的慢,它就是个不错的方案。