高防CDN负载均衡避坑:源站权重、健康检查、会话保持的底层真相与血泪教训

一、别让“轮询”坑了你——源站权重才是真实力

新手最爱用默认的round-robin,但高防CDN场景下,后端服务器硬件差异 (如CPU、网络带宽) 会直接压垮弱鸡节点。我见过某站用2核4G和8核16G混搭轮询,结果流量全打在弱鸡上,直接OOM。

# /etc/nginx/conf.d/upstream.conf
upstream backend {
    # 默认轮询,不推荐
    # server 192.168.1.10:80;
    # server 192.168.1.20:80;

    # 正确姿势 - 权重分配(根据CPU/带宽实测)
    server 192.168.1.10:80 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.20:80 weight=1 max_fails=3 fail_timeout=30s;
}

坑点1: 权重设太高,一旦该源站故障,故障转移期间会大量502。务必搭配max_failsfail_timeout做熔断。实测fail_timeout设30秒,做负载均衡的节点(如轻云互联高防CDN节点)能自动剔除异常源站。

二、健康检查的“假阳”与“假阴”——别被HTTP 200骗了

多数人只做端口探测(proxy_pass后的health_check),但源站可能端口通但业务挂(例如PHP-FPM满载无响应)。我调试时用以下脚本来模拟真实请求检测:

#!/bin/bash
# 源站健康检测(模拟真实请求)
CHECK_URL="http://192.168.1.10/health.php"
HTTP_CODE=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 3 --max-time 5 "$CHECK_URL")
if [ "$HTTP_CODE" != "200" ]; then
    # 加入iptables临时封禁该源站(配合负载均衡器自动剔除)
    iptables -A INPUT -s 192.168.1.10 -j DROP
    sleep 10
    iptables -D INPUT -s 192.168.1.10 -j DROP
fi

注意:高防CDN节点可能自带屏蔽功能,但手动脚本在初期调试最直观。真实生产建议用ngx_http_upstream_check_module(淘宝开源版)配置两个层级的检测:TCP端口 + 自定义HTTP路径。

# nginx upstream health check (非官方模块)
upstream backend {
    server 192.168.1.10:80;
    server 192.168.1.20:80;
    check interval=3000 rise=2 fall=3 timeout=1000 type=http;
    check_http_send "GET /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

坑点2: 健康检测间隔太小(如1秒)导致源站压力暴增,间隔太大(如30秒)导致故障恢复延迟。经验值:3秒间隔,连续2次失败则下线。

三、会话保持与CDN缓存的“互相伤害”

很多新手在负载均衡里配置ip_hash,期望同一用户落到同一源站。但高防CDN通常有二级缓存(例如边缘节点缓存静态资源),会话保持会导致动态请求无法均衡。更致命的是,CDN节点可能变换客户端IP(如经过代理),导致ip_hash失效。

# 错误示范 - 对动态API也做ip_hash
upstream api {
    ip_hash;
    server 192.168.1.10:8080;
    server 192.168.1.20:8080;
}
# 正确做法:仅对需要session的后台做ip_hash,且打开X-Real-IP透传
upstream admin {
    hash $http_x_real_ip consistent;
    server 192.168.1.10:8080;
    server 192.168.1.20:8080;
}
location /api/ {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://api;
}

坑点3: 静态资源不要用会话保持,不然缓存命中率暴跌。推荐用uricookie做一致性哈希。轻云互联的高防CDN支持在控制台一键切换会话保持模式,但底层原理一样——记得在源站nginx.conf里关闭proxy_set_header Connection的keep-alive过多浪费。

四、突发流量下的“雪崩”——从连接池到TIME_WAIT

源站扛不住突发时,负载均衡器默认会堆积请求,导致反向代理的worker_connections耗尽,进而全局502。建议用限流+降级:

# 限制每个源站的最大连接数
upstream backend {
    server 192.168.1.10:80 max_conns=100;
    server 192.168.1.20:80 max_conns=100;
    queue 10 timeout=30s;
}
# 配合全局请求限制
limit_req_zone $binary_remote_addr zone=one:10m rate=30r/s;
location / {
    limit_req zone=one burst=20;
    proxy_pass http://backend;
}

坑点4: 注意max_connsqueue的结合使用——如果后端全是慢查询,队列会很快填满。我在一次双11前调优时,把queue timeout从60s降到30s,配合keepalive连接池(proxy_http_version 1.1 + proxy_set_header Connection ""),源站TIME_WAIT从3万降到8千。

五、DNS负载均衡的“隐性”陷阱:TTL与健康检查脱节

高防CDN常采用DNS+GSLB多地域调度,但新手容易忽略TTL设置。我曾遇到某被攻击站点,DNS TTL设为300秒,手动切换源站IP后,用户仍持续5分钟打到旧IP(已被黑洞)。

# 生产DNS记录配置(降低TTL提高切换速度)
@   IN A 1.2.3.4  TTL 60

同时,在Nginx负载均衡层必须配合resolvervalid参数:

http {
    resolver 8.8.8.8 valid=60s;
    server {
        location / {
            set $backend "backend.example.com";
            proxy_pass http://$backend;
        }
    }
}

坑点5: 如果CDN节点和源站之间用了内网通信(例如轻云互联的VPC内网回源),确保DNS解析到内网IP,否则公网IP带宽费会让你欲哭无泪。

六、实战排错——tcpdump抓包定位负载均衡异常

当负载均衡出现间歇性502或超时,直接看Nginx日志通常不够。执行以下命令抓包分析回源流量:

# 捕获源站与负载均衡器之间的所有TCP流量
tcpdump -i eth0 -s 0 port 80 -w /tmp/loadbalance.pcap
# 用Wireshark打开,重点过滤 tcp.analysis.retransmission 和 tcp.analysis.fast_retransmission

常见根因:源站开启了tcp_tw_recycle(内核参数)导致NAT环境下的TCP时间戳冲突。高防CDN节点通常有多个出口IP,回源时可能发生时间戳乱序。解决方案:关闭net.ipv4.tcp_tw_recycle(Linux 4.12+已移除该参数),改用net.ipv4.tcp_tw_reuse + tcp_timestamps

# /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
# 不要设置 net.ipv4.tcp_tw_recycle

以上六条来自今年刚帮一个电商客户迁移负载均衡架构时的真实踩坑记录。如果你也用轻云互联的高防CDN产品,记得在控制台开启“回源长连接”和“健康检查自定义”,能省下不少排查时间。最后提一句:负载均衡配置完一定要模拟故障(拔网线、kill进程)看自动切换是否生效,别等到真被DDoS时才发现健康检查根本没配对。