一台跑在骨干边缘的转发节点,在连接数爬到几十万之后开始出问题:新连接偶尔建立不上,dmesg 里刷出 nf_conntrack: table full、neighbour: ndisc_cache: neighbor table overflow,ss -s 看到大量 TIME-WAIT 堆积,端口耗尽。机器本身 CPU 和内存都没打满,瓶颈全在内核的默认限制上。
这类问题在我们的网络里很常见——AS37969 骨干上的边缘机、跨境线路的中转节点、以及前面挂着大量短连接的 CDN/代理机,默认的 net.* 和 fs.* 限制都是为桌面或者轻载服务器准备的,撑不住生产流量。下面这份清单是我们在这类机器上长期沉淀的 /etc/sysctl.conf,按用途分组,逐项说清楚它解决什么、代价在哪。
先说一句重话:这不是一份"抄上去就完事"的配置。里面有几个参数配错会让机器直接重启、或者在 NAT 环境下悄悄丢连接。请按自己的场景取舍,不要整段贴。
内存与 OOM 行为
vm.swappiness = 0
vm.panic_on_oom=1
kernel.panic=10
kernel.softlockup_panic = 1
kernel.sysrq = 0
kernel.printk = 5 4 1 7
| 参数 | 值 | 作用与代价 |
|---|---|---|
vm.swappiness | 0 | 尽量不往 swap 挪页。内存充足的网络节点上能避免延迟抖动;但一旦真的吃紧,内核回收空间更少,OOM 来得更快。只在确定内存够用时设 0。 |
vm.panic_on_oom | 1 | 发生 OOM 时直接 panic,而不是由 OOM killer 挑进程杀。配合下面的 kernel.panic 做"坏了就快速重启"策略。危险:生产机上它意味着一次内存尖峰就能让整机重启,如果你更希望杀掉单个进程保住机器,就不要开。 |
kernel.panic | 10 | panic 后 10 秒自动重启。只有希望机器自愈、有别的节点接管流量时才有意义。 |
kernel.softlockup_panic | 1 | 检测到软锁定(CPU 长时间不调度)时 panic 重启。对一台卡死也无人值守的边缘机是合理的,但会把本可以 dump 现场排查的故障变成一次重启。 |
kernel.sysrq | 0 | 关掉 SysRq 魔术键。纯粹是收紧物理/串口控制台的安全面;如果你靠 SysRq 做应急排障,保留它。 |
kernel.printk | 5 4 1 7 | 控制台日志级别。降低控制台噪声,但别设得太高把有用的告警也挡掉。 |
这一组的核心取舍是:panic_on_oom + kernel.panic 是"宁可重启也不要僵死"的策略,适合有冗余、可以让单台机器牺牲的集群;单机、需要保留现场的场景下应当反过来。
文件句柄
fs.file-max = 65536
高并发代理、大量 SNMP/监控连接、很多打开的 socket,都会吃文件描述符。fs.file-max 是全系统上限。注意它只是内核总上限,进程实际能开多少还受 ulimit -n(/etc/security/limits.conf 里的 nofile)约束,两边要一起放开才有效。对真正重载的机器,65536 往往还偏小,需要按连接数往上调。
网络核心缓冲
net.core.netdev_max_backlog = 40960
net.core.optmem_max = 921600
net.core.rmem_default = 2097152
net.core.wmem_default = 2097152
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.core.somaxconn = 65535
| 参数 | 作用与代价 |
|---|---|
net.core.netdev_max_backlog | 网卡收包速度超过内核处理速度时的排队长度。万兆口、小包风暴下调大能减少丢包;代价是队列积压时延迟变高、内存占用上升。 |
net.core.rmem_max / wmem_max | 单 socket 收/发缓冲的上限。高带宽时延积(BDP)的跨境链路上,缓冲太小会卡住吞吐。 |
net.core.rmem_default / wmem_default | 缓冲默认值。 |
net.core.optmem_max | 每 socket 辅助缓冲(控制信息等)的上限。 |
net.core.somaxconn | accept 队列(已完成握手、等应用 accept)的上限。短连接风暴下设小了会直接丢新连接——但应用自己 listen() 的 backlog 参数也要相应放大,否则这里调了也没用。 |
缓冲不是越大越好:设得过大,单条连接会占住大量内核内存,几十万连接乘起来能把内存吃光,同时也会放大 bufferbloat。按链路的带宽时延积来估,而不是一味往上堆。
TCP / UDP 缓冲
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 87380 8388608
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.udp_rmem_min = 65536
net.ipv4.udp_wmem_min = 65536
net.ipv4.tcp_rmem = 92160 6000000 17476000
tcp_rmem / tcp_wmem 是三元组:最小、默认、最大。内核会在这个范围内自动调整每条连接的缓冲,所以不用也不该把默认值拉得太高——拉高默认值等于给每条空闲连接都预留大内存。
这里要坦白一点:源配置里 net.ipv4.tcp_rmem 出现了两次(4096 87380 8388608 和 92160 6000000 17476000),后者会覆盖前者。我们保留原样贴出,是因为这就是当时那台机器上的实际状态;但在正式环境请只保留一行。后一组把最小值提到 92160,意味着每条 TCP 连接至少占 90KB 接收缓冲,连接数一大就非常费内存,只有在确实需要大 BDP 的少量高吞吐连接上才合适。
连接与 TIME-WAIT 处理
net.ipv4.ip_local_port_range = 1025 65530
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 3600
net.ipv4.tcp_max_syn_backlog = 40960
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_reuse=1
| 参数 | 作用与代价 |
|---|---|
ip_local_port_range | 本地出站端口范围。做正向代理、大量主动外连时,端口耗尽是最常见的瓶颈,放到 1025 65530 能多出近两万个端口。 |
tcp_fin_timeout | FIN-WAIT-2 的超时。调小能更快回收半关闭连接,短连接场景有用;太小会误伤慢速对端。 |
tcp_max_syn_backlog | SYN 半连接队列长度。配合 syncookies 抗 SYN flood。 |
tcp_max_tw_buckets | TIME-WAIT 槽位上限。设大能避免 TW buckets overflow 丢包,代价是这些槽位本身占内存。 |
tcp_syncookies | SYN 队列满时用 cookie 继续握手,抗 SYN flood 的关键开关,建议常开。 |
tcp_syn_retries / tcp_synack_retries | 握手重试次数,调小让失败连接更快放弃。 |
tcp_tw_reuse | 允许把 TIME-WAIT 的连接用于新的出站连接,安全,对大量主动外连的机器很有用。 |
单独拎出 net.ipv4.tcp_tw_recycle=1 说:这个参数在 Linux 4.12 之后已经被彻底移除,而且即使在老内核上也极其危险。它会在 NAT 后面丢包——同一个 NAT 出口后面的多个客户端会被内核当成时间戳回退而拒绝握手,表现为"部分用户随机连不上,且极难复现"。我们保留它只是忠实记录当时的配置,强烈建议不要在任何现代生产机上开启;需要回收 TIME-WAIT 的场景,用 tcp_tw_reuse 就够了。
转发、反向路径与邻居表
net.ipv4.conf.default.rp_filter = 0
net.ipv4.ip_forward = 1
net.ipv4.neigh.default.gc_thresh1 = 8192
net.ipv4.neigh.default.gc_thresh2 = 4092
net.ipv4.neigh.default.gc_thresh3 = 8192
ip_forward = 1 是任何转发/路由/网关角色的前提。rp_filter = 0 关闭反向路径校验——在多线、非对称路由(进和出走不同链路)的环境里必须关,否则正常流量会被当成伪造源地址丢掉;但代价是失去了一层源地址防伪,纯主机角色应该设回 1。
邻居表(ARP/ndisc)的三个 gc_thresh 控制缓存回收:gc_thresh3 是硬上限,表满时会刷 neighbor table overflow 并丢包。同网段设备一多(比如一大片直连的服务器)就要调大。注意源配置里 gc_thresh2(4092)比 gc_thresh1(8192)还小,顺序是反的——正常应满足 thresh1 < thresh2 < thresh3,这里原样保留,实际使用时请修正为递增。
IP 分片
net.ipv4.ipfrag_high_thresh = 5242880
net.ipv4.ipfrag_low_thresh = 2932160
重组 IP 分片的内存高/低水位。隧道、VPN、大量 UDP 分片的场景下调大能减少重组失败;但分片重组本身也是一个放大攻击面,纯业务机能避免分片就不要靠调这两个值兜底。
其它网络开关
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
tcp_sack、tcp_window_scaling、tcp_timestamps 是高带宽时延积链路上拿到吞吐的基础,默认就该开。tcp_no_metrics_save = 1 让内核不缓存上一次连接的性能指标——对端质量不稳定时反而更好,因为不会被一次糟糕的历史拖累。icmp_echo_ignore_broadcasts = 1 挡掉广播 ping,收紧攻击面。
完整参考配置
下面是上文各段拼起来的完整 /etc/sysctl.conf 片段,逐值与我们线上一致。再强调一次:tcp_tw_recycle 和重复的 tcp_rmem 请按自己环境处理,gc_thresh 的顺序建议修正。改完执行 sysctl -p 生效。
vm.swappiness = 0
vm.panic_on_oom=1
kernel.panic=10
fs.file-max = 65536
kernel.sysrq = 0
kernel.printk = 5 4 1 7
kernel.softlockup_panic = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.ip_forward = 1
net.core.netdev_max_backlog = 40960
net.core.optmem_max = 921600
net.core.rmem_default = 2097152
net.core.wmem_default = 2097152
net.core.wmem_max = 8388608
net.core.rmem_max = 8388608
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 87380 8388608
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.udp_rmem_min = 65536
net.ipv4.udp_wmem_min = 65536
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.ipfrag_high_thresh = 5242880
net.ipv4.ipfrag_low_thresh = 2932160
net.ipv4.ip_local_port_range = 1025 65530
net.ipv4.neigh.default.gc_thresh1 = 8192
net.ipv4.neigh.default.gc_thresh2 = 4092
net.ipv4.neigh.default.gc_thresh3 = 8192
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 3600
net.ipv4.tcp_max_syn_backlog = 40960
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_rmem = 92160 6000000 17476000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = 1
net.core.somaxconn = 65535
拿去用之前,先改一台、压一压、看 dmesg 和 ss -s,确认没有新的丢包再铺开。调内核的收益是真实的,翻车也是真实的——先验证,再上线。
