Linux 系统调优黄金法则:/proc/sys/net 核心参数的取舍之道
线上接口出现连接超时、握手慢、监听队列溢出或短连接端口耗尽时,最容易看到的建议是“把 sysctl 参数调大”。这类建议有时能缓解症状,也经常把问题从一个队列移到另一个队列:应用 accept 不及时,增大 somaxconn 只会让更多连接在内存中等待;连接跟踪表满,增大 nf_conntrack_max 可能带来更高内存消耗;把端口范围拉大,可能掩盖连接未复用、未关闭或重试风暴。
/proc/sys/net 是 Linux 网络栈的运行时参数视图,sysctl 是读写这些参数的常用接口。本文以 Ubuntu Server 上由 systemd 管理的服务为例,讲清几个核心参数的适用条件、取证方法、变更顺序、验证和回滚。它不是一份“所有参数都设成最大值”的清单。内核版本、网卡驱动、云负载均衡、容器运行时、NAT、应用协议和服务连接模型都会改变参数含义,任何值都要以实际内核文档、sysctl -a 输出和测试结果为准。
<网卡名>、<服务名>、<端口>、<配置目录>、<基线目录> 是占位符。首次出现时替换为目标机器实际值。本文示例只讨论 Ubuntu 的 systemd 与 procps/sysctl 机制,不混用其他发行版的配置路径或服务管理命令。涉及 sysctl --system、重启服务或修改 qdisc 的步骤均属于生产变更,必须在维护窗口、灰度节点或可回滚范围内执行。 其他尖括号变量也按中文名称替换为本机实际路径、文件、设备、端口、数值或服务标识,不能原样复制到生产主机。
黄金法则:先定位队列和状态,再修改参数
一条 TCP 请求至少会经过网卡接收、软中断、内核协议栈、SYN 队列、accept 队列、进程文件描述符、应用 worker、上游连接和可能的 NAT/conntrack。每一段都有自己的容量与丢弃方式。只有确认瓶颈处在哪一段,才能知道应该调内核参数、应用并发、负载均衡、连接池,还是扩容节点。
| 症状 | 应优先查看的证据 | 可能相关参数 | 不应直接得出的结论 |
|---|
| 新连接偶发超时 | 监听队列、SYN 统计、应用 accept 延迟 | somaxconn、tcp_max_syn_backlog | 一定是 DDoS 或必须关闭 syncookies |
| 已建立连接大量积压 | ss 状态、应用线程池、上游延迟 | keepalive、缓冲区、文件描述符 | 把所有超时调大就会恢复 |
| NAT 后间歇断流 | conntrack 使用率、丢包、规则 | nf_conntrack_max | 连接数高就该无限增大表 |
| 高吞吐丢包 | 网卡统计、softnet、CPU 中断 | netdev_max_backlog、RPS/XPS | 只改一个 sysctl 可解决 NIC 瓶颈 |
| 客户端端口耗尽 | TIME-WAIT、ESTABLISHED、端口范围 | ip_local_port_range | 删除 TIME-WAIT 是正确办法 |
先确认系统、内核和服务的实际版本。某些网上流传的参数已经改变默认语义、被弃用,或只在特定内核中存在。若 sysctl -n 返回不存在,停止复制示例,不要通过创建无效配置文件来“兼容”。
# 代码 1:记录主机、内核和网络栈基本信息uname -alsb_release -asystemctl --versionsysctl -n kernel.osreleaseip -br linkip -br address
这些命令都是只读。内核版本应和性能基线一起保存,因为 TCP 默认值、拥塞控制实现和 conntrack 行为可能随内核升级变化。云镜像有时会带额外 sysctl 文件或代理组件,单看 uname 无法判断最终生效值。
# 代码 2:读取常用网络参数的当前有效值sysctl -n net.core.somaxconnsysctl -n net.core.netdev_max_backlogsysctl -n net.ipv4.tcp_max_syn_backlogsysctl -n net.ipv4.tcp_syncookiessysctl -n net.ipv4.ip_local_port_rangesysctl -n net.netfilter.nf_conntrack_max
输出是当前内核运行时值,不等于某一个配置文件里的值。多个 /etc/sysctl.d 文件、发行版默认文件、云初始化和自动化工具都可能覆盖同名参数。因此,修改前要同时查配置来源和最终值;只改一个文件后立即执行 sysctl -p,可能并不能反映开机后的实际顺序。
# 代码 3:定位参数来自哪些 sysctl 配置文件rg -n --glob '*.conf' \'net\.core\.somaxconn|net\.core\.netdev_max_backlog|tcp_max_syn_backlog|ip_local_port_range|nf_conntrack_max' \ /etc/sysctl.conf /etc/sysctl.d /usr/lib/sysctl.d /run/sysctl.d 2>/dev/nullsystemd-analyze cat-config sysctl.d --no-pager
systemd-analyze cat-config sysctl.d 能展示按目录和文件名排序后的配置内容,适合发现重复定义。不要直接修改 /usr/lib/sysctl.d 中由软件包提供的文件;自定义项应放在 /etc/sysctl.d 的独立文件中,并通过变更管理保留原因、日期、负责人和回滚值。
监听队列:somaxconn 不是应用吞吐开关
TCP 监听端口有应用传给 listen() 的 backlog,也受 net.core.somaxconn 这个上限约束。应用的 backlog 设置过小,单纯增大 somaxconn 不会生效;应用 accept 太慢,队列变大只是延长客户端排队时间。排障时要同时看监听 socket 的 Recv-Q、Send-Q、应用 worker、CPU 和上游耗时。
# 代码 4:查看监听 socket 的队列上限与当前积压ss -ltnpss -ltn 'sport = :<端口>'ss -ltnH 'sport = :<端口>' | awk '{print "recv_q="$2, "send_q="$3, "local="$4}'
对 LISTEN socket,ss 输出中的队列字段用于判断当前排队与配置上限,但不同 iproute2 版本的显示细节可能不同。连续采样比单次快照更有意义:若峰值期间 Recv-Q 接近 Send-Q,同时应用 CPU 未满,可能是 accept 或 worker 调度问题;若队列一直为零,增大 backlog 没有证据支持。
# 代码 5:采集 TCP 监听和握手相关内核统计nstat -az TcpExtListenOverflows TcpExtListenDrops TcpExtSyncookiesSent TcpExtSyncookiesRecvsleep 60nstat -az TcpExtListenOverflows TcpExtListenDrops TcpExtSyncookiesSent TcpExtSyncookiesRecv
nstat 的名称依赖内核导出的网络统计,缺少某个字段时不要猜测含义。应在负载前后对比计数增量,而不是把累计值当作当前速率。ListenOverflows 与 ListenDrops 增长需要结合服务 backlog、somaxconn、应用 accept、连接洪峰和负载均衡健康检查判断。
# 代码 6:保存一次监听队列和服务进程基线#!/usr/bin/env bashset -euo pipefailservice_name="<服务名>"port="<端口>"baseline_dir="<基线目录>"mkdir -p "$baseline_dir"date --iso-8601=seconds > "$baseline_dir/captured_at.txt"ss -ltnp "sport = :$port" > "$baseline_dir/listeners.txt"systemctl status "$service_name" --no-pager > "$baseline_dir/service-status.txt"systemctl show "$service_name" -p LimitNOFILE > "$baseline_dir/limit-nofile.txt"nstat -az TcpExtListenOverflows TcpExtListenDrops > "$baseline_dir/listen-nstat.txt"
脚本只收集状态,不会重启服务。<基线目录> 应位于受控日志目录,文件中可能含有进程 PID、监听地址和服务路径。记录服务的 LimitNOFILE 很重要:文件描述符上限过低时,即使内核队列可容纳连接,应用也可能无法 accept 新连接。
对于确有队列溢出证据、并且已经确认应用 backlog 与 worker 配置合理的服务,可在灰度节点提高上限。下面数值只作为结构示例,不能被理解为任何环境的推荐值。修改前先将当前值和来源备份,修改后要进行语法检查、灰度重载和指标观察。
# 代码 7:备份当前值并创建独立的 sysctl 变更文件#!/usr/bin/env bashset -euo pipefailconfig_dir="<配置目录>"config_file="$config_dir/90-net-listen-tuning.conf"backup_dir="<基线目录>"sudo install -d -m 0750 "$config_dir""$backup_dir"sysctl net.core.somaxconn net.core.netdev_max_backlog net.ipv4.tcp_max_syn_backlog \ | sudotee"$backup_dir/listen-sysctl.before.txt" >/dev/nullsudocp -a "$config_file""$backup_dir/90-net-listen-tuning.conf.before" 2>/dev/null || truesudoedit "$config_file"
这里使用 sudoedit 而非在脚本里直接覆盖配置,目的是让管理员在受控编辑器中输入经过评审的内容。cp 找不到旧文件时被允许失败,其他命令仍受 set -e 保护。不要把密码、令牌或业务配置混入 sysctl 文件。
# 代码 8:/etc/sysctl.d/90-net-listen-tuning.conf 示例# 仅在监听溢出有证据、应用 backlog 已确认时评估这些值。net.core.somaxconn = 4096net.ipv4.tcp_max_syn_backlog = 4096net.core.netdev_max_backlog = 5000net.ipv4.tcp_syncookies = 1
tcp_syncookies=1 通常是保护机制,不应因为看到 SyncookiesSent 就关闭。同步 cookie 可以帮助承受 SYN 队列压力,但计数增长也提示需要检查流量来源、负载均衡重试、连接攻击和队列配置。netdev_max_backlog 影响网卡到协议栈的排队,过大可能提高内存使用和排队延迟;它不是所有丢包问题的第一选择。
# 代码 9:检查、应用并验证监听参数sudo sysctl -p /etc/sysctl.d/90-net-listen-tuning.confsysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.core.netdev_max_backlog net.ipv4.tcp_syncookiesss -ltn "sport = :<端口>"nstat -az TcpExtListenOverflows TcpExtListenDrops
sysctl -p 只读取指定文件,适合灰度验证;sysctl --system 会读取所有目录中的文件,影响范围更广。应用参数不会重启服务,但应用的 listen backlog、worker 数和文件描述符限制可能仍需独立发布。验证要比较一段相同流量窗口内的溢出增量、P95/P99 建连时间、服务错误率和 CPU。
网卡、软中断与接收积压:不要绕过硬件证据
如果丢包发生在驱动、ring buffer、软中断或 CPU 中断分布层,单调 netdev_max_backlog 可能没有效果。先检查网卡错误、丢包、队列统计和 CPU 软中断。虚拟机、云弹性网卡和物理网卡的可见指标不同,缺少 ethtool 统计时不要编造替代结论。
# 代码 10:检查网卡字节、错误与丢包计数ip -s link show dev <网卡名>ethtool <网卡名>ethtool -S <网卡名> 2>/dev/null | head -n 120
ip -s link 给出接口级别的 RX/TX 统计,ethtool -S 常提供驱动级统计,但字段名由驱动决定。比较前后增量时应保持同一网卡、同一流量窗口。RX dropped 不一定全在内核接收队列发生;还可能由虚拟交换、驱动、ring、XDP 或云平台限制造成。
# 代码 11:检查软中断处理与网络栈摘要grep -E 'NET_RX|NET_TX' /proc/softirqshead -n 32 /proc/net/softnet_statcat /proc/net/sockstatcat /proc/net/sockstat6
/proc/net/softnet_stat 的十六进制字段需要结合内核文档和版本解释,不能把每一列直接当作“丢包数”。若要做长期监控,应优先使用经过验证的 node exporter 或 eBPF 采集,而不是在告警规则中解析原始 proc 文件。软中断偏斜还要结合 CPU 使用率、irqbalance、RSS 队列和应用绑核策略判断。
# 代码 12:确认网卡队列和中断分布的基础信息ethtool -l <网卡名>grep -i <网卡名> /proc/interruptsps -eo pid,psr,comm,%cpu --sort=-%cpu | head -n 30
ethtool -l 显示网卡当前和最大 channel 参数,但不是所有驱动都支持修改。中断全部集中在单个 CPU 时,瓶颈可能是 IRQ/RSS 配置而不是 sysctl;变更 IRQ 亲和性、RPS/XPS 或网卡 channel 会影响全机网络,应由熟悉硬件和内核的团队灰度执行。
临时端口与连接生命周期:先看谁在消耗端口
出站短连接、代理层回源、服务网格、NAT 网关和高频 HTTP/1.1 客户端都可能消耗本机临时端口。ip_local_port_range 定义本地自动分配端口的范围,但扩大范围不是首选修复:如果应用没有连接池、存在泄漏、TIME-WAIT 急剧积累或 NAT 设备先耗尽,扩范围只会延后再次失败。
# 代码 13:查看本机临时端口范围与 TCP 状态总览sysctl net.ipv4.ip_local_port_rangess -sss -tan state established | wc -lss -tan state time-wait | wc -l
ss -s 是摘要,不要把它当成精确审计。ESTABLISHED 与 TIME-WAIT 的数量需要结合连接来源、目的地址、服务角色和持续时间判断。对短连接客户端,优先评估 HTTP keep-alive、HTTP/2、gRPC、连接池、重试退避和上游响应速度;这些通常比修改内核回收行为更安全。
# 代码 14:按远端地址识别大量短连接或连接热点ss -tan state established | awk 'NR>1 {print $5}' | sed 's/\[//;s/\]//' \ | sort | uniq -c | sort -nr | head -n 30ss -tan state time-wait | awk 'NR>1 {print $5}' | sed 's/\[//;s/\]//' \ | sort | uniq -c | sort -nr | head -n 30
远端地址热点能帮助定位某个上游、代理或依赖服务,但 IPv6 地址与带端口格式可能需要按环境调整。不要在生产命令输出中直接暴露客户 IP;用于工单或报表时应按安全要求脱敏。发现某一上游主导 TIME-WAIT 后,继续查看应用连接池、DNS、负载均衡重试与上游关闭策略。
# 代码 15:临时端口范围变更示例,必须先确认端口冲突# /etc/sysctl.d/91-net-ports.confnet.ipv4.ip_local_port_range = 20000 60999
端口范围不能与本机静态监听端口、NodePort、代理或安全策略发生冲突。修改前必须记录现有范围、检查服务端口规划和防火墙/NAT 规则;云主机或容器宿主机还要确认外部网络设备的端口映射能力。不要把端口范围设为覆盖所有端口,也不要为了规避问题修改已弃用或语义不清的 TIME-WAIT 参数。
# 代码 16:在灰度主机应用端口范围并复核sudo sysctl -p /etc/sysctl.d/91-net-ports.confsysctl net.ipv4.ip_local_port_rangess -ltnpss -tan state time-wait | wc -l
此变更会影响后续新建的出站连接,影响范围可大于单一服务。应用后应观察连接建立失败、NAT 错误、端口冲突、客户端重试与业务延迟;若出现异常,应立刻恢复备份配置和原有效值,再追查连接生命周期根因。
keepalive 与超时:内核默认不等于应用协议超时
TCP keepalive 只有在应用对 socket 启用了 SO_KEEPALIVE 时才参与连接探测。net.ipv4.tcp_keepalive_time、tcp_keepalive_intvl、tcp_keepalive_probes 是系统默认值;HTTP、gRPC、数据库驱动和服务网格通常还有各自的应用层心跳与超时。把这些值调小可能更早回收死连接,也可能增加探测包和误判风险。
# 代码 17:检查当前 TCP keepalive 默认值sysctl net.ipv4.tcp_keepalive_timesysctl net.ipv4.tcp_keepalive_intvlsysctl net.ipv4.tcp_keepalive_probesss -to state established
ss -to 可以展示部分 TCP 定时器信息,但是否出现 keepalive 取决于连接状态和工具版本。改变全局默认值前,先确认目标应用是否启用 keepalive、上游负载均衡的空闲超时是多少、是否存在移动网络或跨地域链路;否则很难解释连接被哪一层关闭。
# 代码 18:TCP keepalive 默认值的保守变更示例# /etc/sysctl.d/92-net-keepalive.confnet.ipv4.tcp_keepalive_time = 300net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 5
上例只展示完整配置结构,并不代表所有服务应采用 300 秒。总探测窗口近似由首次等待、间隔和次数组成,但实际断开仍受协议、网络设备和应用实现影响。更可控的做法是优先在应用、反向代理或客户端库中针对具体连接设置超时,再评估是否需要改变全局默认。
conntrack:NAT、负载均衡和防火墙的共享表
使用 iptables/nftables stateful 规则、容器网络 NAT、四层代理或节点级 SNAT 时,连接可能占用 conntrack 表。表满的表现可能是新连接失败、内核日志异常或特定流量被丢弃,但必须先确认 count、max、内存和连接速率。盲目扩大表会增加内存开销,也不能修复连接洪峰或规则设计问题。
# 代码 19:检查 conntrack 当前计数、上限和工具可用性sysctl net.netfilter.nf_conntrack_countsysctl net.netfilter.nf_conntrack_maxcommand -v conntrack || truedmesg --level=err,warn | rg -i 'conntrack|nf_conntrack' || true
nf_conntrack_count 接近 max 才说明表容量可能是直接因素;比例计算还应观察增速和持续时间。dmesg 读取需要适当权限,且日志可能已轮转。conntrack 工具不存在时不要在生产临时安装软件包,应由镜像或运维基线提供受控诊断工具。
# 代码 20:若 conntrack 工具已批准,按统计而非全量导出检查conntrack -Sconntrack -L -o extended 2>/dev/null | head -n 50
conntrack -L 可能很慢且输出包含源、目的地址和端口,生产中不要无目的地全量导出或上传。优先使用 conntrack -S 查看统计,只有在明确事件窗口、访问控制和脱敏要求满足时才采样少量条目,确认协议、状态与流量方向。
# 代码 21:conntrack 上限配置示例,先做内存评估# /etc/sysctl.d/93-net-conntrack.confnet.netfilter.nf_conntrack_max = 262144
这个值不是固定推荐值。每个条目占用的内存受内核、扩展、NAT、IPv4/IPv6 和连接状态影响,必须在目标内核上做容量评估。应用前还应确认 hashsize、节点内存余量、连接清理策略和上游负载均衡行为,避免单纯放大表导致节点内存压力。
缓冲区、队列规则与拥塞控制:只在完整链路中优化
net.core.rmem_max、wmem_max、tcp_rmem、tcp_wmem 影响 socket 缓冲区的上限与自动调节范围。它们对大吞吐长肥管道可能有价值,但对小请求 API 未必有明显作用;提高缓冲区还会增加每连接可占用内存。任何调整都要以实际 RTT、带宽、连接数、内存和应用 socket 设置为依据。
# 代码 22:读取 TCP 缓冲区、队列规则与拥塞控制能力sysctl net.core.rmem_max net.core.wmem_maxsysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmemsysctl net.core.default_qdiscsysctl net.ipv4.tcp_congestion_controlsysctl net.ipv4.tcp_available_congestion_control
available_congestion_control 不包含 bbr 时,不能仅写 sysctl 配置并期待生效;需要先确认内核模块和发行版支持。拥塞控制算法与 qdisc 的组合应在实际网络路径上测试,尤其是云网络、跨地域链路和有速率限制的场景。不要仅以单机 iperf 峰值决定线上策略。
# 代码 23:在已验证内核支持时启用 fq 与 bbr 的示例# /etc/sysctl.d/94-net-congestion-control.confnet.core.default_qdisc = fqnet.ipv4.tcp_congestion_control = bbr
这两个参数影响新建 socket 的默认行为。执行前必须确认 BBR 在 tcp_available_congestion_control 中可见,并在灰度节点比较重传、延迟分位数、公网吞吐与 CPU;不能因为别的网络环境收益明显,就假设本环境一定更好。使用其他 qdisc 或存在流量整形时,需由网络团队审查相互作用。
# 代码 24:一次可审计的 sysctl 应用、验证与回滚脚本#!/usr/bin/env bashset -euo pipefailconfig_file="/etc/sysctl.d/<变更文件名>.conf"backup_file="<基线目录>/<变更文件名>.conf.before"test -r "$config_file"sudocp -a "$config_file""$backup_file"sudo sysctl -p "$config_file"sudo sysctl --systemsysctl -a 2>/dev/null | rg '^(net\.core\.somaxconn|net\.ipv4\.tcp_max_syn_backlog|net\.netfilter\.nf_conntrack_max)'
sysctl --system 会重新加载所有 sysctl 目录中的文件,风险明显大于指定文件的 sysctl -p。只有在确认全局配置顺序且灰度验证成功时才使用它;若不需要开机持久化验证,应避免扩大影响面。回滚时恢复 backup_file 到原配置路径,重新加载指定文件并验证最终值、监听队列、连接错误和服务健康状态。不要把“sysctl 命令返回成功”当作性能优化成功的证据。
观测与回滚:参数变更必须可证伪
网络调优最终应回答三个问题:问题指标是否改善、是否引入新副作用、收益能否在同等流量下重复。单次请求成功或日志没有报错都不足以证明结果。应在变更前后保持同一观测窗口,至少比较请求量、连接错误、重传、队列溢出、应用延迟、CPU、内存、conntrack 使用率和下游错误率。
# 代码 25:TCP TIME-WAIT 数量示例,指标以实际 node exporter 暴露为准node_sockstat_TCP_tw
这个查询只是指标表达示例,不等同于临时端口使用率。Prometheus 指标必须以实际 exporter 暴露的指标为准;不同 node exporter 版本可能没有相同的 sockstat 指标。更可靠的仪表盘会同时展示应用连接池、系统 TCP 状态、网卡错误和业务端到端延迟。
# 代码 26:网络接收丢包速率示例,指标以实际 node exporter 暴露为准sum by (instance, device) ( rate(node_network_receive_drop_total[5m]))
接口级丢包不是内核调优的充分证据,应进一步与 driver 统计、softnet、云网络指标和应用错误相关联。告警阈值应基于节点角色、历史基线和流量,不要对所有网卡套用同一个绝对数。
一个可靠的回滚计划应在变更前写好:保存原 sysctl 值和原配置文件;明确如何恢复;明确恢复后需要验证的服务、端口、请求路径和监控项。若变更涉及 ip_local_port_range、conntrack 或 qdisc,回滚后的旧状态也可能需要时间自然收敛,因此不能只看一分钟指标就宣布恢复。
容器与宿主机:先确认参数属于哪个网络命名空间
在 Kubernetes 节点上,不能因为 Pod 内能看到 /proc/sys/net 就假定它修改的是宿主机参数。部分网络 sysctl 属于网络命名空间,部分属于节点级或受 kubelet 准入限制的范围;Kubernetes 还区分 safe 与 unsafe sysctl。对于 unsafe sysctl,节点 kubelet 需要显式允许,工作负载也可能被 Pod Security、准入策略或托管平台拒绝。直接在业务 Pod 内用特权权限写 sysctl,会绕过平台治理并给排障留下难以追溯的漂移,不应作为常规调优方法。
宿主机 sysctl 适用于节点级网络角色,例如承载 NodePort、SNAT、入口控制器、主机网络代理或大量容器流量的节点;但每个节点的实际流量形态可能不同,不能把一台入口节点的参数原样推给 GPU 训练节点、数据库节点或控制平面节点。使用 hostNetwork 的 Pod 与宿主机共享网络命名空间,任何面向它的网络调优都要按节点级影响评估。反过来,普通 Pod 的连接问题也不能先假定调整宿主机全局参数一定有效,应先确认流量实际经过的网络命名空间、CNI 路径、Sidecar 和 NAT 点。
容器化环境中的配置来源也更多:镜像启动脚本、DaemonSet、节点初始化、云初始化、配置管理和手工 sysctl 都可能竞争同一个值。应规定一个唯一的节点配置来源,例如受控镜像或配置管理,并在节点启动、变更窗口和周期巡检时比对期望值与运行时值。这样出现参数漂移时,才能区分是内核升级、平台自动化、应用越权还是人工救火留下的修改。
Linux 网络参数没有“黄金数值”,只有经过证据支持的取舍。先把监听、连接、队列、网卡、NAT 和应用行为放到同一条因果链上,再做最小范围的变更,最后用同一组指标证伪或确认它。这样调优才不会变成一次不可回溯的参数堆砌。
任何无法复现、无法回滚或无法解释的参数变更,都不应进入长期生产基线。