订阅更新

生产机 sysctl 到底调哪些——10 个必调内核参数清单

Linux手册 第 18 题 约 13 分钟读完

本文目录
  1. 先说结论
  2. 10 个必调参数完整清单
  3. 配合参数:文件句柄的 limits.conf
  4. 场景还原:那个冲不上 80 万 QPS 的 Kafka
  5. 参数优先级速查
  6. 面试高频追问
  7. 实战踩坑:那次 systemd 坑了半天
  8. 总结

先说结论

第一条:生产机 sysctl 必须显式配置,不能靠发行版默认——CentOS/Ubuntu 默认值保守,为桌面场景服务,和高并发服务端需求差距很大。

第二条:调参分 4 类:网络栈、文件句柄、内存/swap、进程数量。每一类对应一些明确的高频场景。

第三条:所有调参必须写进 /etc/sysctl.d/99-custom.conf 持久化,运行时 sysctl -p 生效,别直接 echo /proc 临时改——重启就没了。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

某短视频平台一个 Kafka 集群,上线初期吞吐一直上不去——单机理论能扛 80 万 QPS,实测到 30 万就开始 TIME_WAIT 堆积、accept 队列溢出、文件句柄报警,像是硬件不行。但我们知道硬件是没问题的,最后发现是 /etc/sysctl.conf 除了系统默认值什么都没动。改完 10 个内核参数重启,吞吐直接冲到 90 万。

这道题考的是'你有没有真正给生产机做过初始化'。很多候选人能背出几个名字(tcp_tw_reuse、somaxconn),但说不清各自的作用和风险——这种半懂不懂的状态在生产上是最危险的。

10 个必调参数完整清单

bash - /etc/sysctl.d/99-custom.conf

# ============ 网络连接层 ============
 
# 1) TCP 半连接队列大小(SYN 收到但未握手完成)
net.ipv4.tcp_max_syn_backlog = 65535
# 默认 128-2048,高并发服务端直接拉到 65535
# 过小的症状:客户端连接偶发 SYN 丢失,netstat -s 看 SYNs to LISTEN dropped
 
# 2) accept 队列大小(握手完成等 accept)
net.core.somaxconn = 65535
# 默认 128,几乎所有生产服务都要改
# 过小的症状:dmesg 有 'possible SYN flooding on port X'
# 配合应用层 listen(fd, 65535) 一起改才生效
 
# 3) TIME_WAIT 快速回收(复用端口)
net.ipv4.tcp_tw_reuse = 1
# 允许 TIME_WAIT 连接被同 socket 复用(客户端侧)
# 注意:tcp_tw_recycle 在 4.12+ 内核已被移除,别再用(NAT 环境下是惨案)
 
# 4) 本地端口范围(客户端连出能用的端口)
net.ipv4.ip_local_port_range = 10240 65535
# 默认 32768-60999,约 2.8 万个
# Kafka/Kafka Connect 这种大量做主动连接的客户端必调
 
# 5) TCP 缓冲区动态范围
net.core.rmem_max = 134217728      # 128M
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
# 高 BDP(带宽-延迟积)场景才需要调大,跨区传输尤其重要
 
# ============ 文件句柄 & 进程数 ============
 
# 6) 系统最大 file descriptor 数
fs.file-max = 2097152
# 默认 ~8 万,高并发服务端远远不够
# 配合 /etc/security/limits.conf 的 nofile 一起改才能真正生效
 
# 7) 单 pid 可监控的 inotify
fs.inotify.max_user_watches = 524288
# 默认 8192,跑 Elastic/Prometheus/K8s 的机器必调
 
# ============ 内存 ============
 
# 8) 避免不必要的 swap
vm.swappiness = 1
# 默认 60,延迟敏感服务必改
 
# 9) overcommit 策略
vm.overcommit_memory = 1
# 0 = 启发式(默认)
# 1 = 总是允许(Redis 官方建议)
# 2 = 严格(不超 RAM + swap * ratio),容易 fork 失败
 
# 10) 脏页写回阈值
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
# 默认 20/10,写密集场景下降 background 比例让 writeback 更平滑

执行方式:sysctl -p /etc/sysctl.d/99-custom.conf 立即生效,同时写进文件确保重启后仍然应用。一定要放到 /etc/sysctl.d/ 目录下而不是直接改 /etc/sysctl.conf,后者会被系统升级覆盖。

配合参数:文件句柄的 limits.conf

fs.file-max 是内核全局限制,但每个用户/进程还有自己的 soft/hard 限制,这俩必须一起改:

bash - /etc/security/limits.d/99-custom.conf

# 格式:<domain> <type> <item> <value>
 
# 所有用户的文件句柄
*  soft  nofile  1048576
*  hard  nofile  1048576
 
# 所有用户的进程数
*  soft  nproc   unlimited
*  hard  nproc   unlimited
 
# 核心转储(生产关掉防磁盘被打爆)
*  soft  core    0
*  hard  core    0
 
# 锁内存(MySQL/Redis memlock 需要)
mysql  soft  memlock  unlimited
mysql  hard  memlock  unlimited
 
# 改完必须重新登录 / 重启服务才生效
# 验证:
ulimit -n    # 看当前 nofile
cat /proc/<pid>/limits  # 看已运行进程的 limit

systemd 的坑:如果服务是 systemd 管理的,limits.conf 可能不生效!systemd 有自己的 LimitNOFILE 设置,必须在 unit 文件里加:[Service] 下面 LimitNOFILE=1048576。否则你改了 limits.conf 但服务启动后 ulimit -n 还是 1024——这个坑我见过太多人踩。

场景还原:那个冲不上 80 万 QPS 的 Kafka

某短视频平台那次 Kafka 上限调优,症状和改动完整记录:

# 症状 1:吞吐打到 30 万时 producer 开始报 LEADER_NOT_AVAILABLE
# 看 broker 日志
dmesg | tail
# TCP: request_sock_TCP: Possible SYN flooding on port 9092. Dropping request.
# ← 经典 accept 队列打满
 
# 确认 somaxconn 和应用 backlog 都很小
cat /proc/sys/net/core/somaxconn
# 128     ← 坑
 
ss -ltn sport = :9092
# State   Recv-Q Send-Q Local Address:Port
# LISTEN  129    128    *:9092
# Recv-Q 已经打满 backlog
 
# 症状 2:连接数涨到 1 万时报 too many open files
ulimit -n
# 1024  ← 发行版默认
 
# 症状 3:客户端大量 TIME_WAIT 吃光本地端口
ss -s
# TCP: 1234 (estab 456, closed 5432, orphaned 12, timewait 25000/0)
 
# 三处同时改:
cat > /etc/sysctl.d/99-kafka.conf <<EOF
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65535
fs.file-max = 2097152
vm.swappiness = 1
EOF
sysctl -p /etc/sysctl.d/99-kafka.conf
 
# systemd unit 里加 LimitNOFILE
systemctl edit kafka
# [Service]
# LimitNOFILE=1048576
systemctl daemon-reload && systemctl restart kafka

改完后单机吞吐从 30 万直接上到 90 万,QPS 瓶颈消失,整个集群按预期运转。这种「默认值没改所以跑不满硬件」的故事,在生产上太常见了——每次新机器上线忘了调 sysctl,都会踩一次。

参数优先级速查

参数默认推荐影响场景
somaxconn12865535所有 TCP 服务端
tcp_max_syn_backlog102465535高并发入站
tcp_tw_reuse01客户端大量短连接
ip_local_port_range32768-6099910240-65535客户端连出多
fs.file-max~80k2M所有服务端
nofile (limits)10241048576所有服务
swappiness601延迟敏感服务
overcommit_memory01Redis/fork 多
inotify.max_user_watches8192524288Prometheus/ES/K8s
tcp_fin_timeout6015客户端短连接

面试高频追问

追问一:为什么 tcp_tw_recycle 在 4.12+ 内核被移除了?

因为它在 NAT 环境下是灾难。tcp_tw_recycle 开启后,内核会根据源 IP + 时间戳快速回收 TIME_WAIT,但如果多个客户端在同一个 NAT 后面(公网出口是同一个 IP),它们的时间戳不同步,内核会把晚到的合法 SYN 包当作'重复的旧包'直接丢弃,表现为大量客户端间歇性连接失败,问题非常难排查。Linux 社区从 4.12 开始直接删掉这个参数——任何面试你看到有人建议开 tcp_tw_recycle,基本可以确定对方没踩过生产坑。现在正确的做法是只用 tcp_tw_reuse,它不依赖时间戳,只管客户端出站场景,没有 NAT 副作用。

追问二:调大 somaxconn 必须同时改应用吗?

对。somaxconn 只是内核的上限,应用 listen(fd, backlog) 时传的那个 backlog 参数是应用自己指定的队列长度。内核最终用的是 min(somaxconn, 应用传的 backlog)。如果应用传的是 50(很多框架默认值),你把 somaxconn 调到 65535 也没用——内核仍然用 50。Nginx 有 listen 80 backlog=65535;Tomcat 有 server.xml 的 acceptCount;Java 的 ServerSocket 默认 50,要显式 new ServerSocket(port, 65535)。两边都要改才真生效,这是常见坑。

追问三:你怎么确认 sysctl 改完的参数真的生效了?

最直接的是 sysctl -a | grep <参数名> 看当前值,但这只能证明内核参数生效——文件句柄、进程数这种要看 /proc//limits 才能证明对目标进程生效。我的标准流程:① 改 /etc/sysctl.d/99-x.conf 写入;② sysctl -p 加载;③ sysctl -a | grep 确认值;④ 重启目标服务;⑤ cat /proc//limits 确认 limits 生效;⑥ ss -ltn 确认 backlog 生效。六步跑完才能说调参成功。面试时讲出这套流程,面试官立马知道你是真改过不是背过。

实战踩坑:那次 systemd 坑了半天

某短视频平台那次 Kafka 调参还有一个插曲:我们按标准流程改了 sysctl.conf + limits.conf,service restart 后验证 ulimit -n 却还是 1024。查了半个小时才发现是 systemd 的问题——systemd 启动的服务完全不走 /etc/security/limits.conf,要在 unit 文件里单独设置 LimitNOFILE。

bash - 完整的 systemd unit 示例

# /etc/systemd/system/kafka.service
[Unit]
Description=Kafka
After=network.target
 
[Service]
Type=simple
User=kafka
ExecStart=/opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/server.properties
Restart=on-failure
 
# 以下都是 systemd 特有限制,不看 limits.conf
LimitNOFILE=1048576     # 文件句柄
LimitNPROC=unlimited    # 进程数
LimitMEMLOCK=infinity   # 锁内存
LimitCORE=0             # 禁 core dump
 
# OOM 保护(重要服务让 OOM killer 优先杀别人)
OOMScoreAdjust=-500
 
[Install]
WantedBy=multi-user.target

复盘:这种'改了但是不生效'的坑会把人搞崩溃。后来我把完整的'服务端机器初始化清单'写成了 Ansible playbook,新机器上线一次跑完:sysctl + limits + systemd unit template + 常用工具(htop/iftop/iotop/nethogs/smem)。这个 playbook 跑 3 分钟,省掉无数半夜的排错时间。

总结

sysctl 调参看似琐碎,其实是生产机的'体检报告'——默认值适合桌面,你得自己调到适合服务端的状态。10 个参数抄下来背熟、写进 Ansible role 里复用,是大数据/运维岗的基本功。面试时你能讲出 somaxconn vs 应用 backlog 的联动、tcp_tw_recycle 为什么在 4.12+ 被删、systemd 不走 limits.conf 这三点,基本就是'调过生产'的铁证。

下一篇预告:OOM Killer 为什么总是杀错进程——oom_score / overcommit / cgroup 联合解读。

你生产机上 sysctl 调过哪些特殊参数?评论区互相抄作业。

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器