基于 DNS 的故障切换:最便宜的容灾方案
服务器挂了,能否自动把用户引导到备用机?DNS 故障切换是最便宜的做法,但也有它绕不过的限制。
一、原理
健康检查定时探测主服务器
├─ 正常 → 解析保持指向主服务器
└─ 失败 N 次 → 自动把 A 记录改成备用服务器 IP
主服务器恢复 → 改回来二、TTL 决定一切
这是 DNS 故障切换的核心限制:
用户本地缓存了旧 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 + 一个健康检查:
#!/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
fichmod 600 /opt/scripts/dns-failover.sh
# 每分钟跑一次
* * * * * /opt/scripts/dns-failover.sh关键点
- 健康检查要防抖动:连续失败 2~3 次才切换,避免网络抖动导致误切;
- 脚本要跑在第三台机器上:跑在主服务器上的话,主服务器挂了脚本也挂了;
- 要有防止反复切换的机制(flapping):加冷却时间;
- 切换时发告警,你需要知道发生了什么。
五、它解决不了什么
DNS 故障切换有明显局限:
| 局限 | 说明 |
|---|---|
| 切换慢 | 受 TTL 限制,分钟级 |
| 已建立的连接仍会失败 | DNS 只影响新连接 |
| 不检查服务是否真的可用 | 只能探活端口/URL |
| 用户端缓存不可控 | 有些客户端会缓存更久 |
| 无法做负载分担 | 只是主备,不是负载 |
六、什么时候够用
| 场景 | 是否合适 |
|---|---|
| 个人站点、博客 | ✅ 够用(能接受几分钟不可用) |
| 内网服务、后台系统 | ✅ 够用 |
| 电商、交易 | ❌ 不够,需要负载均衡 |
| 要求 99.99% 可用性 | ❌ 不够 |
七、更简单有效的替代方案
很多时候,与其做 DNS 故障切换,不如:
1. 用 CDN(Cloudflare)
静态资源在 CDN 边缘节点上,源站挂了用户仍能访问缓存内容。等于自动获得了大部分容灾能力,零配置。
# 配合 "Always Online" 功能(Cloudflare 免费版有)
# 源站挂了会显示缓存的页面快照2. 服务本身做高可用
systemd Restart=always + 云厂商的自动重启,能解决大部分「进程崩了」的情况,比切换 DNS 快得多。
3. 静态站多平台部署
静态站可以同时部署到 GitHub Pages、Vercel、自建服务器。真出事了手动改一次 DNS(几分钟),比维护自动切换简单。
八、我的看法
对个人项目,我的排序是:
- 先让服务本身不挂(
Restart=always+ 监控告警); - 再上 CDN(缓存吸收大部分影响);
- 有备用机且愿意折腾,才做 DNS 故障切换;
- 真的要高可用,用云负载均衡。
DNS 故障切换听起来很美,实际收益往往不如前两项。别为了架构的「完整性」增加运维复杂度。
九、如果要做,检查清单
- TTL 设到 60~120 秒
- 健康检查有失败阈值(防抖动)
- 有冷却时间(防反复切换)
- 探测脚本跑在独立的机器上
- 切换时有告警通知
- 定期演练(手动停主服务器测试)
DNS 故障切换的定位很清楚:便宜、简单、慢。 接受它的慢,它就是个不错的方案。
