高防CDN内核暗战:从SYN_PROXY到TCP_CORK,榨干每一滴网络性能

一、不是什么机房里都是软路由器

高防CDN的本质是流量清洗+快速转发,但绝大多数人只盯着清洗算法,忽略了底层的内核才是真正的“看门狗”。当你的节点扛着500Gbps攻击流量时,内核协议栈的任何一个参数都可能成为系统崩溃的最后一根稻草。本文直接撕开表面,带你把Linux内核扒层皮,用真正的底层优化扛住DDoS并让正常请求飞起。

别指望调几个sysctl就能搞定,你必须理解连接跟踪的哈希桶、SYN flood的预处理逻辑甚至网卡的中断亲和性。以下全是真实打出来的配置,直接拿过去用就行(但我保证你会在生产环境里踩坑,因为我踩过)。

二、先干掉连接跟踪的瓶颈:conntrack要足够粗才能喂饱攻击

高防CDN最常见的死法就是conntrack表爆满——大量畸形SYN包填满哈希表后,正常的TCP握手建立都会超时。默认的hashsize(16384)在10G线速下瞬间炸穿。

# 调整conntrack哈希桶大小(根据内存,建议总连接数的1/10)
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
# 关闭除必要项外的所有连接跟踪(比如UDP/ICMP只在攻击时启用)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=10
sysctl -w net.netfilter.nf_conntrack_udp_timeout=10
sysctl -w net.netfilter.nf_conntrack_icmp_timeout=10

注意:hashsize必须在模块加载前设置,否则修改无效。建议写入/etc/rc.local或modprobe.d配置文件。另外,如果使用iptables的state模块,必须保留conntrack;如果改用裸机XDP/DPDK,可以直接砍掉整个连接跟踪,性能翻倍。在典型的轻云互联高防物理机上,我见过运维把hashsize设到8M后,并发1200万连接依然稳如狗。

三、SYN flood:别让内核自己玩SYN Cookie,上SYN_PROXY更狠

很多教程告诉你开tcp_syncookies=1就完事,但SYN Cookie有代价:它会丢掉TCP Timestamp选项,导致RTT估算不准,对长连接非常不友好。高防CDN应该使用iptables的SYN_PROXY target,它能在真正握手之前就完成验证,有效减少CPU消耗。

# 在PREROUTING链对源IP实行SYN代理(注意顺序)
iptables -t raw -A PREROUTING -i eth0 -p tcp --syn -m limit --limit 1000/sec --limit-burst 500 -j ACCEPT
iptables -t raw -A PREROUTING -i eth0 -p tcp --syn -j SYNPROXY --mss 1460 --wscale 7 --timestamp

配合内核参数:

sysctl -w net.ipv4.tcp_syn_retries=2
sysctl -w net.ipv4.tcp_max_syn_backlog=65536
sysctl -w net.ipv4.tcp_syncookies=0   # 关闭cookie,因为SYNPROXY已经接管

实战中,SYN_PROXY需要内核支持(>=4.5),并且不要忘记同时调整backlog。攻击高峰时syn_backlog很容易打满,建议配合上述参数把backlog开到16M。我曾经在一台轻云互联的E5-2699 v4服务器上,用SYN_PROXY扛住28Mpps的SYN flood,核心CPU占用才35%。

四、网卡中断与RPS:让每个CPU核都成为暴力转发单元

高防CDN对网络吞吐的依赖极高,绝不能让0号核单挑所有中断。必须把网卡多队列的中断绑定到不同物理核心(NUMA亲和),再用RFS/RPS进一步负载均衡。

先确认网卡支持多队列:

ethtool -l eth0
# 设置队列数(比如16个)
ethtool -L eth0 combined 16
# 查看当前中断亲和性
cat /proc/interrupts | grep eth0

手动绑定中断到指定CPU:

echo "ff" > /proc/irq/123/smp_affinity  # 将0-7号CPU全部绑定到该中断
# 更精细的:每个队列绑定一个CPU
echo 1 > /proc/irq/$(awk -F: '/eth0-TxRx-0/{print $1}' /proc/interrupts)/smp_affinity
echo 2 > /proc/irq/$(awk -F: '/eth0-TxRx-1/{print $1}' /proc/interrupts)/smp_affinity

开启RPS/RFS(软件分发):

# 为每个rx队列设置CPU位图(比如0-7核)
echo "ff" > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo "ff" > /sys/class/net/eth0/queues/rx-1/rps_cpus
# 启用RFS
echo 4096 > /proc/sys/net/core/rps_sock_flow_entries
# 每个队列设置流条目数
echo 2048 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt

特别注意:如果网卡本身支持流转向器(如Intel X710的FDIR),你可以关闭RPS,直接用硬件过滤,延迟更低。但大多数CDN节点用的是廉价网卡,RPS是性价比最高的方案。

五、TCP发送优化:别让CORK和NODELAY打架

高防CDN代理层(如Nginx/HAProxy)经常需要转发海量小文件或短连接,默认的TCP行为会产生大量小包和ACK延迟。你需要精准控制CORK和PUSH。

内核层面调整:

sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_tw_recycle=0   # 千万别开!NAT环境下会丢包
sysctl -w net.ipv4.tcp_fin_timeout=10
sysctl -w net.ipv4.tcp_keepalive_time=120
sysctl -w net.ipv4.tcp_keepalive_intvl=10
sysctl -w net.ipv4.tcp_keepalive_probes=3
# 调整发送缓冲区
sysctl -w net.core.wmem_max=16777216
sysctl -w net.core.rmem_max=16777216
sysctl -w net.ipv4.tcp_wmem='4096 65536 16777216'
# 关闭慢启动后重启
sysctl -w net.ipv4.tcp_slow_start_after_idle=0

应用层配合:Nginx的sendfile、tcp_nopush和tcp_nodelay要按场景组合。比如:

# 代理静态大文件:开启sendfile + tcp_nopush,减少小包
sendfile on;
tcp_nopush on;
tcp_nodelay off;

# 代理实时API短连接:关闭nopush,开启nodelay
sendfile off;
tcp_nopush off;
tcp_nodelay on;

内核中CORK的逻辑是:当设置TCP_CORK时,内核会延迟发送直到缓冲区满或调用TCP_NODELAY(或应用关闭CORK)。如果你在nginx里同时开tcp_nopush和tcp_nodelay,实际上tcp_nodelay会覆盖CORK,造成大量小包。务必理解它们互斥。轻云互联某客户在切换配置后,小包率从42%降到3%,整机PPS提升2倍。

六、内存管理与大页:避免高并发下的OOM扫地

高防CDN节点内存消耗大头在conntrack和socket缓冲区,普通Linux的页面回收策略在突发流量下会频繁扫描,直接导致CPU飙升。调整如下:

# 预留足够内存给内核(防止突发oom)
sysctl -w vm.min_free_kbytes=524288   # 512MB
# 减少脏页回写压力
sysctl -w vm.dirty_ratio=20
sysctl -w vm.dirty_background_ratio=5
# 关闭透明大页(THP会导致内存碎片)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

如果使用DPDK或KVM做CDN加速,直接上1G大页:

# 预留1024个1G大页
echo 1024 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
# 挂载hugetlbfs
mount -t hugetlbfs hugetlbfs /dev/hugepages

大页能显著减少TLB miss,对内存密集型转发提升明显。不过要注意,普通应用程序(如nginx)不一定能直接使用大页,需要修改堆分配器(jemalloc开启hugepage支持)。

七、压箱底:针对高防CDN的龙芯级调优清单

以下是我在多个实战场景中(包括轻云互联的定制方案)总结的内核参数全家桶,直接写进/etc/sysctl.d/99-highdefence.conf:

# 网络核心
net.core.somaxconn=65536
net.core.netdev_budget=600
net.core.netdev_budget_usecs=2000
net.core.optmem_max=65536
net.core.default_qdisc=fq_codel

# IP协议栈
net.ipv4.ip_local_port_range=1024 65535
net.ipv4.ip_nonlocal_bind=1
net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_rmem='4096 87380 33554432'
net.ipv4.tcp_wmem='4096 65536 33554432'
net.ipv4.udp_mem='262144 524288 1048576'
net.ipv4.tcp_mem='786432 1048576 1572864'

# 关闭IPv6(除非必须)
net.ipv6.conf.all.disable_ipv6=1

# 保活与超时
net.ipv4.tcp_fin_timeout=5
net.ipv4.tcp_max_tw_buckets=2000000
net.ipv4.tcp_abort_on_overflow=0   # 避免直接RST,高防场景要优雅拒绝
net.ipv4.tcp_max_orphans=32768
net.ipv4.neigh.default.gc_thresh1=2048
net.ipv4.neigh.default.gc_thresh2=4096
net.ipv4.neigh.default.gc_thresh3=8192

应用后使用sysctl -p加载。注意net.core.netdev_budget这个参数——它控制一次NIC轮询最多处理多少包,默认300,攻击时调大到600能降低软中断开销,但会增加单次轮询时间,需要权衡。

八、写在最后:内核懂你,但别依赖默认

高防CDN不是买几台服务器装上软件就完事了,内核层面的调优决定了你能否在攻击峰值时保持正常服务的三位数在线率。本文给出的参数在真实压测中(100Gbps混合攻击+50万正常连接)能让系统在90% CPU占用下仍保持零丢包。如果你用的是普通VPS,这些参数可能直接让OOM killer出手,所以务必在可控环境测试。 下次再有人跟你说“高防CDN重在清洗策略”,你可以把这篇内核调优拍他脸上,然后告诉他:策略只占30%,剩下的70%是内核给面子。