前两篇讲的 L4 和 L7 负载均衡都工作在单站点内部——将流量分发给同一数据中心的多台服务器。但当你的服务部署在多个地理位置(北京、上海、广州、新加坡),就需要一个跨地域的流量调度层——这就是全局负载均衡(Global Server Load Balancing,GSLB)的职责。
GSLB 要回答的核心问题是:这个用户的请求应该发往哪个数据中心?依据是什么?出错时怎么切换?
一、GSLB 的调度维度
1.1 为什么需要全局调度
跨地域部署的动机通常是以下一种或多种:
| 动机 | 说明 | 典型场景 |
|---|---|---|
| 降低延迟 | 用户就近访问,减少网络往返时间 | CDN、全球化 SaaS |
| 容灾 | 某个数据中心故障时流量切换到其他中心 | 金融、电商 |
| 合规 | 数据必须存储和处理在特定地区 | GDPR、数据本地化 |
| 容量扩展 | 单数据中心容量不足,多中心分担 | 大流量应用 |
1.2 调度依据
GSLB 的调度决策可以基于多种因素:
用户请求 → GSLB 决策引擎
│
├── 地理位置 (Geography)
│ └── 用户在哪个城市/国家?→ 选最近的数据中心
│
├── 网络距离 (Network Proximity)
│ └── 用户到各数据中心的延迟/跳数?→ 选网络最近的
│
├── 负载状况 (Load)
│ └── 各数据中心的当前负载?→ 选负载最低的
│
├── 健康状态 (Health)
│ └── 哪些数据中心是健康的?→ 排除故障中心
│
└── 业务规则 (Policy)
└── 数据主权要求?灰度规则?→ 按策略路由
二、DNS-based GSLB
2.1 GeoDNS 原理
最常见的 GSLB 实现是基于 DNS 的地理路由——根据用户的 DNS Resolver IP 判断地理位置,返回最近数据中心的 IP:
用户 (北京) → Local DNS Resolver (北京 ISP)
→ 递归查询 app.example.com
→ 权威 DNS (GSLB)
→ 检查 Resolver IP: 202.106.x.x → 判定为北京
→ 返回北京数据中心 IP: 10.1.1.100
用户 (广州) → Local DNS Resolver (广州 ISP)
→ 递归查询 app.example.com
→ 权威 DNS (GSLB)
→ 检查 Resolver IP: 183.60.x.x → 判定为广州
→ 返回广州数据中心 IP: 10.3.1.100
2.2 GeoDNS 的精度问题
GeoDNS 基于 IP 地理数据库(如 MaxMind GeoIP2)将 Resolver IP 映射到地理位置。但这有几个精度问题:
问题 1:用户和 Resolver 不在同一位置。
实际场景:
用户 (广州) → Google DNS 8.8.8.8 (美国 PoP)
→ GSLB 看到的 Resolver IP 是美国的
→ 返回美国数据中心 IP
→ 用户跨太平洋访问!延迟 200ms+
解决方案:EDNS Client Subnet (ECS)
用户 (广州) → Google DNS + ECS: 183.60.0.0/20
→ GSLB 看到客户端子网 183.60.0.0/20
→ 判定为广州
→ 返回广州数据中心 IP ✓
EDNS Client Subnet(RFC 7871)让递归 DNS 服务器在查询中携带客户端的部分 IP(通常 /24 或 /20),让权威 DNS 能够基于真实客户端位置做决策:
# 用 dig 测试 ECS 支持
dig @8.8.8.8 app.example.com +subnet=183.60.0.0/20
# 查看响应中的 ECS 信息
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
; CLIENT-SUBNET: 183.60.0.0/20/24 ← scope /24 表示缓存粒度问题 2:IP 地理数据库不准确。
移动网络的出口 IP 可能和用户实际位置不同;VPN 用户的 IP 反映的是 VPN 出口位置。GeoIP 数据库对部分 IP 段的准确率只有 60-70%。
问题 3:DNS 缓存导致切换延迟。
DNS 记录有 TTL(生存时间)。即使 GSLB 已经切换了 IP,缓存了旧 IP 的 Resolver 要等到 TTL 过期才会重新查询:
故障发生: T=0
GSLB 切换: T=10s(检测到故障并更新 DNS)
TTL 过期: T=300s(如果 TTL=300)
所有用户切换完成: T=300s+
这 5 分钟内,缓存了旧 IP 的用户仍然访问故障数据中心
2.3 TTL 策略
| TTL 值 | 故障切换速度 | DNS 查询负载 | 适用场景 |
|---|---|---|---|
| 30s | 最快 | 最高 | 高可用要求极高 |
| 60-120s | 快 | 中 | 推荐默认值 |
| 300s | 中 | 低 | 普通 Web 服务 |
| 3600s | 慢 | 最低 | 静态内容/CDN |
生产建议:
- 对外服务用 60-120 秒 TTL
- 内部服务可以用 30 秒甚至更短
- CDN 域名可以用较长 TTL(CDN 本身有故障转移能力)
2.4 GeoDNS 实战配置
# 使用 PowerDNS + GeoIP Backend
# /etc/pdns/pdns.conf
launch=geoip
geoip-database-files=/usr/share/GeoIP/GeoLite2-City.mmdb
geoip-zones-file=/etc/pdns/geo-zones.yaml# /etc/pdns/geo-zones.yaml
domains:
- domain: example.com
ttl: 60
records:
app.example.com:
- soc: "0.0.0.0/0" # 默认
content:
- type: A
value: 10.2.1.100 # 上海(默认)
- soc: "%cn" # 中国
content:
- type: A
value: 10.1.1.100 # 北京
- soc: "%cn-gd" # 广东省
content:
- type: A
value: 10.3.1.100 # 广州
- soc: "%sg" # 新加坡
content:
- type: A
value: 10.4.1.100 # 新加坡# 使用 BIND + GeoIP ACL
# /etc/bind/named.conf
acl "china-north" {
202.106.0.0/16; # 北京联通
61.135.0.0/16; # 北京电信
// ... 更多中国北方 IP 段
};
acl "china-south" {
183.60.0.0/16; # 广东电信
14.17.0.0/16; # 广东联通
// ... 更多中国南方 IP 段
};
view "china-north" {
match-clients { china-north; };
zone "example.com" {
type master;
file "/etc/bind/zones/example.com.north";
};
};
view "china-south" {
match-clients { china-south; };
zone "example.com" {
type master;
file "/etc/bind/zones/example.com.south";
};
};2.5 DNS Failover 的多种模式
除了加权路由,DNS-based GSLB 还支持多种故障转移模式:
模式 1:主备故障转移(Active-Passive Failover)
正常: app.example.com → 10.1.1.100 (北京,PRIMARY)
故障: app.example.com → 10.2.1.100 (上海,SECONDARY)
适用于成本敏感场景——备站点只在主站点故障时启用,平时不承担流量。
模式 2:加权轮询(Weighted Round Robin)
app.example.com → 10.1.1.100 (权重 60)
app.example.com → 10.2.1.100 (权重 40)
DNS 服务器根据权重比例返回不同记录。客户端每次查询可能得到不同 IP。
模式 3:延迟路由(Latency-Based Routing)
用户 (日本) → 探测各站点延迟
北京: 45ms
上海: 38ms
新加坡: 62ms
→ 返回上海 IP (最低延迟)
AWS Route 53 等云 DNS 服务通过在全球部署探测节点,持续测量用户到各数据中心的延迟,返回延迟最低的站点。比 GeoDNS 更精确,因为网络距离不等于地理距离。
模式 4:多值应答(Multi-Value Answer)
# DNS 返回多个 IP,客户端自行选择
dig app.example.com +short
10.1.1.100
10.2.1.100
10.3.1.100
# 每个 IP 关联独立健康检查
# 不健康的 IP 自动从应答中移除多值应答让客户端拥有选择权——现代浏览器实现了 Happy Eyeballs 算法,会并行尝试多个 IP,选择最快响应的那个。
2.6 自建 GSLB 方案
除了云厂商的托管 DNS 服务,也可以使用开源软件自建 GSLB:
# 方案 1:PowerDNS + Lua 脚本实现动态调度
# /etc/pdns/gslb.lua
function preresolve(dq)
if dq.qname:equal(newDN("app.example.com")) then
-- 获取各站点健康状态
local bj_healthy = checkHealth("10.1.1.100")
local sh_healthy = checkHealth("10.2.1.100")
-- 获取客户端子网(ECS)
local ecs = dq.ecs
local region = geoLookup(ecs or dq.remoteaddr)
-- 调度逻辑
if region == "north" and bj_healthy then
dq:addAnswer(pdns.A, "10.1.1.100", 60)
elseif sh_healthy then
dq:addAnswer(pdns.A, "10.2.1.100", 60)
elseif bj_healthy then
dq:addAnswer(pdns.A, "10.1.1.100", 60)
else
-- 所有站点不健康,返回降级页面 IP
dq:addAnswer(pdns.A, "10.0.0.1", 30)
end
return true
end
return false
end# 方案 2:CoreDNS + 自定义插件
# Corefile
example.com {
gslb {
zone example.com
record app {
backend 10.1.1.100 {
region china-north
weight 60
healthcheck http://10.1.1.100:8080/health 5s
}
backend 10.2.1.100 {
region china-south
weight 40
healthcheck http://10.2.1.100:8080/health 5s
}
}
geoip /usr/share/GeoIP/GeoLite2-City.mmdb
ecs true
}
log
errors
}# 方案 3:使用 gdnsd(专为 GeoDNS 设计)
# /etc/gdnsd/config
options => {
listen => [ 0.0.0.0 ]
dns_port => 53
edns_client_subnet => true
}
service_types => {
http_check => {
plugin => http_status
url_path => /health
interval => 5
timeout => 3
}
}
# /etc/gdnsd/zones/example.com
$TTL 60
@ SOA ns1.example.com. admin.example.com. (
2024010101 3600 900 604800 60 )
@ NS ns1.example.com.
@ NS ns2.example.com.
app DYNA geoip!mymap自建 GSLB 的优势是完全可控,但运维负担大——需要自己维护 GeoIP 数据库更新、健康检查系统和 DNS 基础设施的高可用。中小规模团队建议使用云 DNS 服务。
三、Anycast
3.1 Anycast 原理
Anycast 用同一个 IP 地址在多个物理位置同时宣告(通过 BGP),路由器根据 BGP 路由选择将流量发送到最近的节点:
同一 IP: 203.0.113.100
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 北京 PoP │ │ 上海 PoP │ │ 新加坡 PoP │
│ BGP 宣告 │ │ BGP 宣告 │ │ BGP 宣告 │
│ 203.0.113.100│ │ 203.0.113.100│ │ 203.0.113.100│
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
北京用户 → 上海用户 → 新加坡用户 →
路由到北京 PoP 路由到上海 PoP 路由到新加坡 PoP
3.2 Anycast 与 Unicast 的对比
| 维度 | DNS-based GSLB (Unicast) | Anycast |
|---|---|---|
| IP 数量 | 每个站点不同 IP | 所有站点相同 IP |
| 调度层 | DNS 层 | BGP 路由层 |
| 切换速度 | 受 TTL 影响(秒-分钟) | BGP 收敛(秒级) |
| TCP 支持 | 好(连接固定到一个 IP) | 有风险(路由变化可能导致连接重置) |
| UDP 支持 | 好 | 非常好(无状态) |
| 精度 | 依赖 GeoIP 数据库 | 由 BGP 拓扑决定 |
| 配置复杂度 | 中 | 高(需要 BGP 对等) |
3.3 Anycast 的 TCP 问题
Anycast 对 UDP 协议(如 DNS)非常友好,但对 TCP 有一个固有问题——路由漂移(Route Flapping)。当 BGP 路由变化时,已建立的 TCP 连接可能被路由到不同的 PoP,导致连接重置:
T=0: 用户 TCP 连接路由到北京 PoP
T=5: BGP 路由变化(北京 PoP 链路闪断)
T=6: 后续数据包路由到上海 PoP
上海 PoP 没有这个连接的状态 → RST
用户连接断开 ✗
解决方案:
- ECMP + 哈希:在路由器上使用基于五元组的 ECMP 哈希,保证同一连接始终走同一路径。
- Anycast + Unicast 混合:DNS 查询用 Anycast,TCP 连接用 Unicast。CDN 常用这种方式。
- QUIC:QUIC 使用 Connection ID 而非五元组标识连接,天然容忍路由变化。
3.4 Anycast 的故障转移
Anycast 的故障转移是自动的——当一个 PoP 停止 BGP 宣告时,该 PoP 的流量自动路由到下一个最近的 PoP:
# PoP 节点的 BGP 配置 (BIRD)
# /etc/bird/bird.conf
router id 10.0.1.1;
protocol static anycast_routes {
route 203.0.113.100/32 blackhole; # Anycast 路由
}
protocol bgp upstream1 {
local as 65001;
neighbor 10.0.0.1 as 64512;
export filter {
if net = 203.0.113.100/32 then accept;
reject;
};
}
# 健康检查:如果本地服务不健康,撤回 BGP 宣告
protocol static {
route 203.0.113.100/32 blackhole;
}
# 使用 health check 脚本控制路由宣告
# 健康时: birdc enable anycast_routes
# 不健康时: birdc disable anycast_routes#!/bin/bash
# anycast_healthcheck.sh — Anycast 健康检查脚本
# 检查本地服务是否正常,控制 BGP 路由宣告
SERVICE_URL="http://localhost:8080/health"
CHECK_INTERVAL=3
FAIL_THRESHOLD=3
CONSECUTIVE_FAILS=0
while true; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--connect-timeout 2 --max-time 5 "$SERVICE_URL")
if [ "$HTTP_CODE" = "200" ]; then
CONSECUTIVE_FAILS=0
# 确保路由已宣告
birdc show protocols anycast_routes | grep -q "up" || \
birdc enable anycast_routes
else
CONSECUTIVE_FAILS=$((CONSECUTIVE_FAILS + 1))
if [ "$CONSECUTIVE_FAILS" -ge "$FAIL_THRESHOLD" ]; then
echo "$(date): 连续失败 $CONSECUTIVE_FAILS 次,撤回 BGP 宣告"
birdc disable anycast_routes
fi
fi
sleep "$CHECK_INTERVAL"
done3.5 BGP 社区在流量调度中的应用
BGP Community 是 Anycast 精细化流量调度的核心工具。通过设置不同的 Community 值,可以控制路由的传播范围和优先级:
# BIRD 配置:使用 BGP Community 控制路由传播
# /etc/bird/bird.conf
# 定义 Community 常量
define LOCAL_PREF_HIGH = (65001, 100); # 高优先级
define LOCAL_PREF_LOW = (65001, 50); # 低优先级
define NO_EXPORT = (65535, 65281); # 不向外部对等体宣告
define BLACKHOLE = (65001, 666); # 黑洞路由(DDoS 防御)
protocol bgp upstream1 {
local as 65001;
neighbor 10.0.0.1 as 64512;
export filter {
if net = 203.0.113.100/32 then {
bgp_community.add(LOCAL_PREF_HIGH);
accept;
}
reject;
};
}
# 备用上游:设置低优先级,正常情况下不使用
protocol bgp upstream2 {
local as 65001;
neighbor 10.0.0.2 as 64513;
export filter {
if net = 203.0.113.100/32 then {
# AS Path Prepend 降低优先级
bgp_path.prepend(65001);
bgp_path.prepend(65001);
bgp_community.add(LOCAL_PREF_LOW);
accept;
}
reject;
};
}实际应用中,BGP Community 常用于以下场景:
| 场景 | Community 值 | 效果 |
|---|---|---|
| 地区限制 | (ISP, region) |
路由只在特定地区传播 |
| 优先级控制 | (ISP, local-pref) |
设置上游路由器的 Local Preference |
| 黑洞路由 | (ISP, 666) |
上游丢弃匹配流量(DDoS 防御) |
| 不宣告 | no-export |
路由不向外部 AS 传播 |
# 验证 BGP Community
birdc show route 203.0.113.100/32 all
# 输出中应包含设置的 Community 值
# 使用 looking glass 验证全球路由状态
# https://lg.he.net/
# https://bgp.tools/prefix/203.0.113.1003.6 Anycast 性能测量
部署 Anycast 后需要持续监控路由质量,确保用户被路由到最优 PoP:
# 使用 RIPE Atlas 进行全球 Anycast 覆盖测量
# 创建一个从全球 100 个探针到 Anycast IP 的 traceroute 测量
curl -X POST "https://atlas.ripe.net/api/v2/measurements/" \
-H "Authorization: Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"definitions": [{
"type": "traceroute",
"af": 4,
"target": "203.0.113.100",
"description": "Anycast coverage test",
"protocol": "ICMP"
}],
"probes": [{
"type": "area",
"value": "WW",
"requested": 100
}]
}'#!/usr/bin/env python3
"""分析 Anycast 路由分布,验证用户是否被路由到最近的 PoP"""
import json
import subprocess
from collections import Counter
def analyze_anycast_distribution(target_ip, dns_servers):
"""从多个 DNS 视角分析 Anycast IP 的路由分布"""
pop_distribution = Counter()
for dns in dns_servers:
# 通过 HTTP 头判断 Anycast 路由到哪个 PoP
result = subprocess.run(
["curl", "-s", "--connect-timeout", "5",
"--resolve", f"app.example.com:80:{target_ip}",
"http://app.example.com/debug/pop"],
capture_output=True, text=True
)
if result.returncode == 0:
pop = result.stdout.strip()
pop_distribution[pop] += 1
# 输出分布
total = sum(pop_distribution.values())
print(f"\nAnycast PoP 分布 (总计 {total} 次探测):")
for pop, count in pop_distribution.most_common():
pct = count / total * 100
bar = "█" * int(pct / 2)
print(f" {pop:15s}: {count:3d} ({pct:5.1f}%) {bar}")
if __name__ == "__main__":
dns_list = [
"8.8.8.8", "1.1.1.1", "223.5.5.5",
"114.114.114.114", "208.67.222.222",
]
analyze_anycast_distribution("203.0.113.100", dns_list)四、多活架构的流量调度
4.1 多活模式
| 模式 | 说明 | 复杂度 | 适用场景 |
|---|---|---|---|
| 主备(Active-Passive) | 一个中心服务,另一个待命 | 低 | 灾备需求 |
| 双活(Active-Active) | 两个中心同时服务 | 高 | 容量扩展 + 容灾 |
| 多活(Multi-Active) | 三个以上中心同时服务 | 极高 | 全球化服务 |
4.2 流量切换策略
正常状态:
北京: 60% 流量
上海: 40% 流量
故障切换(北京故障):
Step 1: GSLB 检测到北京不健康
Step 2: 更新 DNS(移除北京 IP 或降低权重)
Step 3: 上海接收 100% 流量
关键考量:
- 上海是否有足够容量承接 100% 流量?
- 数据同步是否跟上?是否存在数据不一致窗口?
- 切换后用户 session 是否丢失?
容量规划:多活架构中,每个数据中心必须预留足够容量应对其他中心的故障。如果两个中心各承担 50% 流量,则每个中心的实际容量需要达到 100%——这意味着在正常状态下有 50% 的资源”浪费”。
常见的折中方案是降级切换:故障时允许部分非关键功能降级运行,减少容量需求。
4.3 DNS 权重调整实现
# 使用 AWS Route 53 加权路由
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890 \
--change-batch '{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "app.example.com",
"Type": "A",
"SetIdentifier": "beijing",
"Weight": 60,
"TTL": 60,
"ResourceRecords": [{"Value": "10.1.1.100"}]
}
},
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "app.example.com",
"Type": "A",
"SetIdentifier": "shanghai",
"Weight": 40,
"TTL": 60,
"ResourceRecords": [{"Value": "10.2.1.100"}]
}
}
]
}'
# 故障切换:将北京权重设为 0
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890 \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "app.example.com",
"Type": "A",
"SetIdentifier": "beijing",
"Weight": 0,
"TTL": 60,
"ResourceRecords": [{"Value": "10.1.1.100"}]
}
}]
}'4.4 健康检查驱动的自动切换
# AWS Route 53 健康检查 + 故障转移路由
# 1. 创建健康检查
HealthCheck:
Type: HTTP
FullyQualifiedDomainName: app-beijing.example.com
Port: 443
ResourcePath: /readyz
RequestInterval: 10 # 每 10 秒检查一次
FailureThreshold: 3 # 连续失败 3 次判定不健康
# 2. 配置故障转移路由策略
ResourceRecordSet:
Name: app.example.com
Type: A
Failover: PRIMARY # 主记录
HealthCheckId: hc-beijing
TTL: 60
ResourceRecords:
- Value: 10.1.1.100 # 北京
ResourceRecordSet:
Name: app.example.com
Type: A
Failover: SECONDARY # 备记录
TTL: 60
ResourceRecords:
- Value: 10.2.1.100 # 上海(不需要健康检查)五、GSLB 方案对比
5.1 综合对比
| 特性 | DNS-based GSLB | Anycast | HTTP 重定向 | 混合方案 |
|---|---|---|---|---|
| 切换速度 | 秒-分钟(受 TTL) | 秒级(BGP 收敛) | 实时 | 秒级 |
| 对 TCP 友好 | 好 | 有风险 | 好 | 好 |
| 部署复杂度 | 中 | 高(需要 BGP) | 低 | 高 |
| 精度 | 依赖 GeoIP + ECS | 由 BGP 拓扑决定 | 可用客户端 IP | 最高 |
| 适用场景 | 大多数 Web 服务 | DNS 服务/CDN | 小流量场景 | 大规模全球化 |
5.2 推荐架构
小规模(<3 个数据中心):
DNS-based GSLB(Route 53 / CloudFlare)
+ 各站点 L4/L7 LB
中等规模(3-5 个数据中心):
GeoDNS + Anycast DNS
+ 各站点 L4/L7 LB
+ 自动健康检查和故障转移
大规模(5+ 个数据中心):
Anycast + BGP Anycast
+ ECMP 哈希保证连接亲和
+ 分层架构(Anycast→L4→L7→App)
六、GSLB 监控与可观测性
6.1 关键监控指标
GSLB 系统的可观测性往往被忽视,直到故障切换时才发现监控缺失。以下是必须监控的指标:
| 指标类别 | 具体指标 | 告警阈值 |
|---|---|---|
| DNS 解析 | 各站点的 DNS 查询分布比例 | 偏离预期权重 >15% |
| DNS 延迟 | 权威 DNS 的查询响应时间 | P99 > 100ms |
| 健康检查 | 各站点健康状态变化次数 | 频繁抖动 >5次/小时 |
| 流量分布 | 各站点实际请求量比例 | 与 DNS 权重不匹配 |
| 故障切换 | 切换耗时(从检测到生效) | > 120s |
| ECS 覆盖 | 带 ECS 信息的查询占比 | < 50% |
# Prometheus + Blackbox Exporter 监控 GSLB
# prometheus.yml
scrape_configs:
- job_name: 'dns_gslb'
scrape_interval: 15s
metrics_path: /probe
params:
module: [dns_gslb_check]
static_configs:
- targets:
- 8.8.8.8 # 从 Google DNS 视角探测
- 1.1.1.1 # 从 Cloudflare DNS 视角探测
- 223.5.5.5 # 从阿里 DNS 视角探测
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
# blackbox.yml
modules:
dns_gslb_check:
prober: dns
timeout: 5s
dns:
query_name: app.example.com
query_type: A
transport_protocol: udp
preferred_ip_protocol: ip4
valid_rcodes:
- NOERROR6.2 DNS 流量分析
#!/bin/bash
# gslb_monitor.sh — 持续监控 GSLB 解析结果分布
TARGET="app.example.com"
RESOLVERS=("8.8.8.8" "1.1.1.1" "223.5.5.5" "114.114.114.114")
SAMPLES=50
echo "$(date): GSLB 解析分布监控"
echo "目标域名: $TARGET"
echo "采样次数: $SAMPLES per resolver"
echo "---"
for resolver in "${RESOLVERS[@]}"; do
echo -e "\nResolver: $resolver"
for i in $(seq 1 $SAMPLES); do
dig +short @"$resolver" "$TARGET" 2>/dev/null
done | sort | uniq -c | sort -rn | while read count ip; do
pct=$((count * 100 / SAMPLES))
printf " %-20s %3d/%d (%d%%)\n" "$ip" "$count" "$SAMPLES" "$pct"
done
done
# 告警判断:如果某个 IP 占比为 0,说明可能被移除或故障
echo -e "\n--- 异常检测 ---"
expected_ips=("10.1.1.100" "10.2.1.100")
for resolver in "${RESOLVERS[@]}"; do
results=$(dig +short @"$resolver" "$TARGET" 2>/dev/null | sort -u)
for expected in "${expected_ips[@]}"; do
if ! echo "$results" | grep -q "$expected"; then
echo "警告: $resolver 未返回 $expected"
fi
done
done6.3 故障切换演练
GSLB 故障切换必须定期演练,否则真正故障时可能暴露配置问题:
# 故障切换演练检查清单
#
# 演练前:
# 1. 确认备站点容量充足
# 2. 通知相关团队
# 3. 准备回滚方案
# 4. 开启详细日志
#
# 演练步骤:
# 1. 记录当前流量分布基线
# 2. 手动将主站点设为不健康(或降低 DNS 权重到 0)
# 3. 观察 DNS 解析变化
# 4. 验证流量已切换到备站点
# 5. 检查用户体验指标(错误率、延迟)
# 6. 恢复主站点
# 7. 验证流量恢复到正常分布
#
# 记录:
# - 故障检测耗时
# - DNS 切换耗时
# - 流量完全迁移耗时
# - 期间错误率变化
# - 备站点性能变化
# 执行切换
echo "Step 1: 记录当前基线"
dig +short app.example.com @8.8.8.8
curl -s https://app.example.com/metrics | grep http_requests_total
echo "Step 2: 模拟主站点故障"
# Route 53: 将主站点权重设为 0
# 自建 DNS: 修改健康检查返回 503
echo "Step 3: 监控切换过程"
while true; do
echo "$(date +%H:%M:%S) $(dig +short app.example.com @8.8.8.8)"
sleep 5
done七、排查 GSLB 问题
7.1 DNS 调度验证
# 从不同位置验证 DNS 解析结果
# 使用不同的 DNS Resolver
for dns in 8.8.8.8 1.1.1.1 223.5.5.5 114.114.114.114; do
echo "=== Resolver: $dns ==="
dig @$dns app.example.com +short
done
# 验证 ECS 支持
dig @8.8.8.8 app.example.com +subnet=202.106.0.0/16 # 模拟北京
dig @8.8.8.8 app.example.com +subnet=183.60.0.0/16 # 模拟广州
dig @8.8.8.8 app.example.com +subnet=1.0.0.0/16 # 模拟新加坡
# 查看 TTL 变化(监控缓存行为)
watch -n 1 'dig @8.8.8.8 app.example.com +noall +answer'
# 追踪 DNS 解析链路
dig app.example.com +trace
# 检查 GSLB 返回的权重分布
for i in $(seq 1 100); do
dig +short @8.8.8.8 app.example.com
done | sort | uniq -c | sort -rn
# 结果应接近配置的权重比例7.2 Anycast 路径验证
# 验证 Anycast IP 路由到哪个 PoP
# 通过 HTTP 头判断
curl -s -H "Host: app.example.com" http://203.0.113.100/debug \
| jq '{pop: .server_location, ip: .server_ip}'
# 通过 traceroute 观察路径
traceroute -n 203.0.113.100
# 多地区测试(使用在线工具或 RIPE Atlas)
# ripe-atlas measure --type traceroute --target 203.0.113.100 \
# --from-area WW --probes 50八、总结
DNS-based GSLB 是大多数场景的起点。它简单、成熟、所有云厂商都支持。使用 Route 53、CloudFlare 或自建 PowerDNS,配合健康检查和自动故障转移,可以覆盖大部分跨地域调度需求。
EDNS Client Subnet 是 GeoDNS 精度的关键。没有 ECS,使用公共 DNS(8.8.8.8、1.1.1.1)的用户可能被路由到错误的数据中心。确保你的权威 DNS 支持 ECS。
Anycast 最适合 UDP 服务(DNS)和 CDN。对 TCP 服务慎用 Anycast——BGP 路由变化可能导致连接中断。QUIC 的 Connection ID 机制让 Anycast + TCP 成为可能。
TTL 决定了故障切换速度的下限。60 秒 TTL 意味着最坏情况下 60 秒才能完成切换。对于高可用要求极高的服务,使用 30 秒 TTL + Anycast 做兜底。
多活架构的核心挑战不是调度,而是数据。GSLB 解决的是”流量去哪里”的问题,但”数据一致性”和”跨中心同步”才是多活架构的真正难题。
参考文献
- RFC 7871: Client Subnet in DNS Queries (tools.ietf.org/html/rfc7871)
- Cloudflare Blog: Anycast (blog.cloudflare.com/tag/anycast)
- RIPE NCC: Anycast vs Unicast (labs.ripe.net)
- PowerDNS Documentation: GeoIP Backend (doc.powerdns.com/authoritative/backends/geoip.html)
- AWS Route 53 Developer Guide (docs.aws.amazon.com/Route53)
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
网络工程索引
汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。
【网络工程】DDoS 防御架构:容量型、协议型与应用层攻击
DDoS 攻击分为容量型、协议型和应用层三大类,防御策略截然不同。本文从攻击分类学出发,系统讲解 SYN Flood 的 SYN Cookie 防御、UDP 反射放大的 BGP Flowspec 清洗、HTTP Flood 的速率限制与行为分析,以及 Anycast 清洗中心的工作原理,构建从边缘到源站的多层 DDoS 防御体系。
【网络工程】CDN 架构原理:PoP、边缘节点与 Origin Shield
系统解剖 CDN 的多层缓存架构——从 DNS 调度到 PoP 内部结构、Origin Shield 回源保护、多 CDN 部署策略。结合实际配置和响应头分析,给出 CDN 架构的工程理解。
【网络工程】HAProxy 工程:高级配置、ACL 与运维
系统讲解 HAProxy 的工程实践:Frontend/Backend/Listen 配置模型、ACL 规则引擎的高级用法、Runtime API 的动态管理能力、多线程模型与性能调优、SSL 终止与健康检查策略,建立 HAProxy 从配置到生产运维的完整体系。