30秒速览
- DNS轮询不靠谱必须配合健康检查
- 地理路由能砍掉70%以上的跨国延迟
- 权重分流是服务器迁移的神器
- TTL设置要按业务需求精细调整
- Anycast+DNS混合方案打游戏延迟减半
- 完整的DNS监控要看三层指标
“DNS轮询就是垃圾”——这是我三年前的错误认知
2019年双十一,我在某跨境电商平台负责运维,当时天真地以为DNS轮询就能搞定流量高峰。结果大促开始47分钟后,主服务器直接宕机,连带整个CDN雪崩。事后分析才发现,简单的DNS轮询根本不知道后端服务器死活,所有请求还在往已经挂掉的节点上怼。
那次事故后我彻底重新认识了DNS负载均衡。现在给物流公司WMS系统做架构升级时,我用了完全不同的方案。以下是真实配置代码(基于AWS Route 53):
# 健康检查配置 - 每30秒检测一次HTTP 200状态
resource "aws_route53_health_check" "api_healthcheck" {
fqdn = "api.example.com"
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3 # 连续3次失败才标记为不健康
request_interval = 30 # 30秒间隔
# 必须设置SNI否则TLS握手会失败
enable_sni = true
regions = ["us-west-1", "eu-west-1"]
}
这个配置的关键在于failure_threshold和request_interval的平衡。太敏感会导致误判(比如网络抖动),太迟钝又失去意义。经过压测,3次失败+30秒间隔的组合在假阳性和响应速度之间取得了最佳平衡。
地理路由让日本用户请求不再绕道新加坡
物流公司的日本分部一直抱怨系统延迟高,原来DNS默认把请求都解析到新加坡机房。用GeoDNS改造后,延迟直接从380ms降到92ms。Route 53的配置比想象中简单:
# 地理路由策略 - 日本用户定向到东京机房
resource "aws_route53_record" "jp_record" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
# 日本地区专属设置
geolocation_routing_policy {
country = "JP"
}
set_identifier = "tokyo"
alias {
name = "alb-jp-tokyo-123456789.ap-northeast-1.elb.amazonaws.com"
zone_id = "Z14GRHDCWA56QT"
evaluate_target_health = true
}
}
踩坑警告:千万别用CIDR块做地理匹配!我最初尝试用IP段匹配,结果AWS的IP库更新不及时,导致部分用户被错误路由。直接使用国家代码反而更可靠。
权重分流让我优雅应对服务器升级
上周三凌晨做服务器迁移时,我用权重分配实现了无缝过渡。旧集群权重从100逐步降到0,新集群从0升到100,整个过程零停机。监控显示请求成功率始终保持在99.99%以上:
| 时间 | 旧集群权重 | 新集群权重 | 错误率 |
|---|---|---|---|
| 00:00 | 100 | 0 | 0.01% |
| 00:30 | 75 | 25 | 0.008% |
| 01:00 | 50 | 50 | 0.005% |
| 02:00 | 0 | 100 | 0.003% |
对应的Terraform配置片段:
# 权重分流配置 - 新旧集群过渡
resource "aws_route53_record" "weighted_record" {
zone_id = aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
ttl = 60 # 故意设短TTL方便快速切换
weighted_routing_policy {
weight = var.cluster_weight # 通过变量动态控制
}
set_identifier = "legacy-cluster"
records = ["10.0.1.1"]
}
TTL设置是个技术活——30秒与300秒的天壤之别
DNS缓存是把双刃剑。给视频直播平台做优化时,我把TTL从默认的300秒改成30秒,故障转移时间缩短了87%。但代价是DNS查询量暴涨5倍,差点被Cloudflare收费升级。
最终方案是按业务类型区分TTL:
- 支付API:30秒(快速故障转移)
- 商品图片CDN:3600秒(减少查询开销)
- 用户会话服务:300秒(平衡点)
实测发现Python的DNS缓存会无视TTL,必须强制刷新:
import dns.resolver
def resolve_with_ttl(domain):
"""强制遵守TTL的DNS查询"""
resolver = dns.resolver.Resolver()
resolver.cache = dns.resolver.LRUCache() # 使用LRU缓存而非默认缓存
answer = resolver.resolve(domain)
return answer.rrset[0].address
当DNS遇上Anycast:给全球游戏服务器加速的邪道玩法
给某FPS游戏做全球部署时,传统DNS方案在玩家匹配场景下表现糟糕。最终我用Anycast+DNS的混合方案,将亚洲玩家的平均延迟从142ms压到64ms。核心配置如下:
# Anycast BGP通告配置
router bgp 65530
no synchronization
bgp log-neighbor-changes
network 203.0.113.0/24
neighbor 192.0.2.1 remote-as 64512
neighbor 192.0.2.1 route-map ANYCAST_OUT out
!
route-map ANYCAST_OUT permit 10
set community 64512:666
set local-preference 200
配合DNS的延迟路由策略:
# 延迟路由配置
resource "aws_route53_record" "latency_record" {
zone_id = aws_route53_zone.main.zone_id
name = "game.example.com"
type = "A"
latency_routing_policy {
region = "ap-southeast-1"
}
set_identifier = "singapore"
alias {
name = "anycast-sgp-123.elb.amazonaws.com"
zone_id = "Z35SXDOTRQ7X7K"
}
}
这个方案最骚的地方在于:当Anycast节点过载时,DNS会自动把流量切到其他区域,形成双重负载均衡。不过调试BGP的时候我差点把东京机房搞下线三次,不建议网络小白尝试。
监控DNS?99%的人只做了半吊子检测
别以为ping个域名就叫监控了!我在Kubernetes集群里部署的完整监控方案包括:
# Prometheus黑盒监控配置
- module: dns
prober: dns
dns:
preferred_ip_protocol: "ip4"
query_name: "api.example.com"
query_type: "A"
valid_rcodes:
- NOERROR
validate_answer_rrs:
fail_if_none_matches_regexp:
- "10.0..*" # 必须返回内网IP段
validate_authority_rrs:
fail_if_not_matches_regexp:
- "ns-d+.awsdns-d+.org" # 必须使用AWS权威DNS
配合Grafana看板监控这些关键指标:
- DNS解析延迟百分位(P99>200ms就报警)
- TTL违反率(客户端缓存时间超过设定TTL的比例)
- 地理路由错误率(日本用户被解析到美国节点的比例)
去年圣诞节大促时,这个监控系统提前15分钟发现了DNS查询量异常增长,及时扩容DNS服务器避免了一场灾难。
DNS轮询的致命缺陷解剖
那次双十一事故后,我用WireShark抓包分析才发现,传统DNS轮询至少有三大致命伤:
# 这是当时灾难性的DNS配置示例 www.example.com. 300 IN A 192.0.2.1 www.example.com. 300 IN A 192.0.2.2 www.example.com. 300 IN A 192.0.2.3
第一是TTL(300秒)导致切换延迟。当192.0.2.1服务器挂掉时,由于DNS缓存,仍有数百万客户端在持续向这个IP发送请求。更可怕的是第二点——DNS服务器完全不知道后端服务器的健康状态,即便配置了监控告警,从发现问题到人工干预至少需要5分钟。
血泪教训:客户端缓存雪崩
最致命的是第三点:某些运营商DNS会私自延长TTL。我们后来抓包发现某省移动DNS竟然将300秒TTL擅自改为3600秒,导致该省用户持续1小时访问故障节点。这解释了为什么当时监控图上会出现区域性雪崩——某个地区的用户集体失效后,他们的重试请求又集中压垮了其他节点。
现代DNS负载均衡方案演进
现在的方案采用三层防御体系:
- 智能DNS解析:基于GeoDNS的位置路由
- 健康检查熔断:30秒间隔的HTTP健康检查
- 流量染色:通过EDNS Client Subnet传递客户端真实IP
物流公司WMS实战案例
在华东仓库上线新系统时,我们配置了这样的健康检查策略:
health_check {
type = "https"
port = 443
path = "/api/health"
interval = 30s
timeout = 5s
unhealthy_threshold = 2
healthy_threshold = 3
expected_codes = [200, 302]
}
配合Consul实现服务发现后,任何节点异常都能在60秒内自动从DNS记录中剔除。这套方案在618大促期间实现了100%可用性。
TCP协议层的优化技巧
除了DNS层面,传输层也有大文章可做:
- SYN Cookie防护:在Linux内核参数中启用
net.ipv4.tcp_syncookies = 1 - 连接追踪优化:调整
nf_conntrack_tcp_timeout_established为1200秒 - 端口复用:设置
net.ipv4.tcp_tw_reuse = 1缓解TIME_WAIT状态
实测表明,仅TCP优化就使单节点QPS从12k提升到18k。这是我们在测试环境用wrk压测的数据:
# 优化前
Thread Stats Avg Stdev Max +/- Stdev
Latency 85.12ms 26.33ms 342.56ms 85.32%
Req/Sec 1.20k 181.23 1.45k 86.33%
# 优化后
Thread Stats Avg Stdev Max +/- Stdev
Latency 53.67ms 18.92ms 298.43ms 89.01%
Req/Sec 1.81k 202.15 2.12k 91.07%
混合云场景下的特殊处理
当系统跨AWS和阿里云部署时,我们发现DNS解析会出现跨云延迟。最终解决方案是:
- 在AWS Route53和阿里云DNS分别配置智能解析
- 通过Cloudflare Argo Tunnel建立私有通道
- 使用Envoy做全局负载均衡
这是Envoy的关键配置片段:
clusters:
- name: hybrid_cluster
connect_timeout: 1s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: hybrid_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: api.aws.example.com
port_value: 443
- endpoint:
address:
socket_address:
address: api.aliyun.example.com
port_value: 443
第三章:DNS健康检查的魔鬼细节
说出来你可能不信,我们最初用AWS Route53做健康检查时,连续三天凌晨触发误报警。监控系统显示所有节点集体下线,但实际服务器活得好好的——问题出在TCP检查端口用了8080,而我们的安全组配置有个愚蠢的时间段策略:凌晨3点自动关闭非标准端口。
// 错误配置示例(千万别学):
{
"HealthCheckConfig": {
"Type": "TCP",
"Port": 8080,
"RequestInterval": 30,
"FailureThreshold": 3
},
"SecurityGroupRules": [
{
"PortRange": "80",
"TimePolicy": "AlwaysOn"
},
{
"PortRange": "8080",
"TimePolicy": "WorkingHours" // 就是这个坑货!
}
]
}
3.1 HTTP检查的报文玄学
后来我们改用HTTP检查,又栽进新的坑。某次部署后健康检查突然大面积失败,查了半小时才发现Nginx配置里多了个add_header X-Frame-Options DENY;——Route53的健康检查请求居然会被这个安全头拦截!更离谱的是不同DNS服务商对HTTP检查的实现天差地别:
- 阿里云云解析:默认不带Host头,必须手动配置
- Google Cloud DNS:遇到30x跳转直接判定失败
- Azure Traffic Manager:对HTTP/2支持时好时坏
第四章:混合云场景下的DNS骚操作
去年给某车企做车联网升级时遇到个经典问题:他们既有阿里云上的AI训练集群,又有本地数据中心的车辆通信服务器。当我们在DNS里同时配置A记录和AAAA记录时,某些安卓车机的TCP栈会优先尝试IPv6,但他们的内网IPv6隧道根本没调通…
dig +short example.com ANY看全量解析结果,我们后来专门写了个检测脚本:
def check_dns_mixed_environment(domain):
import dns.resolver
answers = dns.resolver.resolve(domain, 'ANY')
has_ipv4 = any(r.rdtype == 1 for r in answers)
has_ipv6 = any(r.rdtype == 28 for r in answers)
if has_ipv4 and has_ipv6:
print("⚠️ 双栈环境需检查客户端IPv6可达性")
if "aws" in domain:
print("建议启用Route53的'Evaluate Target Health'功能")
elif has_ipv4:
print("✅ 纯IPv4环境")
else:
print(" 纯IPv6环境需特殊处理")
4.1 地理位置路由的坑
你以为按国家分流很简单?我们给东南亚业务配置GeoDNS时,发现新加坡用户总被路由到美国节点。抓包才发现当地ISP的DNS出口IP全被识别为美国——后来不得不手动维护一个IP库覆盖规则:
| ISP名称 | 错误识别国家 | 实际覆盖区域 | 修正方案 |
|---|---|---|---|
| Singtel Mobile | US | 新加坡/马来西亚 | ASN覆盖 |
| Telkomsel | GB | 印尼 | EDNS Client Subnet |
第五章:客户端侧的隐藏战争
做过移动端优化的都知道,DNS缓存是个薛定谔的存在。我们实测发现:
- iOS在WiFi切换蜂窝数据时必定刷新DNS
- 某些MIUI版本会无视TTL强制缓存24小时
- Chrome浏览器对失败DNS记录有特殊缓存逻辑
最坑的是某次故障复盘时,发现用户投诉的”无法访问”居然是因为他们的企业路由器把我们的DNS响应包中的TC(Truncated)位错误置1,导致客户端放弃请求——这种问题你让服务端怎么防?