linux内核参数再调优
Jun 2, 2026 - ⧖ 3 minTip
本文是 vps-kernel-sysctl 的后续审计报告。原始 blog 阐述了如何从 shell 参考脚本移植一套动态 sysctl 生成框架;本文记录对该框架的一次全面上机验证与修正。
之所以有这次 Re-Tuning,因为前两天看到 6神 on X: "买完VPS,搭建节点后,为什么一定要进行TCP调优才行?" / X 这个tweet,想着参考一下他的shell,做个处理。最终结果是,他的shell能提供的借鉴并不多,但是让 AI去做 Re-Tuning,也确实查到很多之前配置有问题的配置项。在此做个记录。
1. 方法论:验证方法
判断准不准,不能靠猜,上机跑一遍最实在。这次用了好几种手段来交叉验证——直接 SSH 上 VPS 逐 key 读写、对比部署态与生效态、翻原始设计文档追溯决策意图、以及对自己的预判做反向验证。下面逐个说。
1.1 SSH 逐 key 读测试
对 lib/vps-sysctl.nix 中每个有疑问的 sysctl key,登录目标 VPS 执行:
sysctl -n < key>
echo "exit: $ ?"
exit 0 表示 key 存在且可读,exit 1(No such file or directory)表示 key 不存在。
1.2 对比部署态与实际生效值
读取 NixOS 生成的 /etc/sysctl.d/60-nixos.conf 确认实际写入了什么;再读 /proc/sys/ 对应文件确认 NixOS activation script 是否在 sysctl.d 之上又做了覆盖(例如 networking.nat 的 ip_forward 覆盖)。
# 部署态配置
cat /etc/sysctl.d/60-nixos.conf
# 实际生效值
cat /proc/sys/net/ipv4/ip_forward
1.3 溯源设计文档
重读原始 blog 的 Changelog,理解每个参数的移植意图:
- "全量补齐脚本参数" → 哪些来自 shell 脚本的平移
- "动态公式覆盖" → 哪些做了定制化
- "风险点与兼容性处理" → 哪些是已知风险
1.4 内部交叉验证
对存疑的判断方向做反向验证。例如:
- 认为
vm.page-cluster(连字符)是 typo → 不仅读,还试了另一种形式vm.page_cluster(下划线) - 认为调度参数太激进 → 检查它们是否真的存在于
sysctl -a输出中 - 认为
ip_forward=0会覆盖 NAT → 对比实际/proc/sys/值
2. 判断证实与证伪
上机之前,我心里有一些预判。等实际跑完一遍 sysctl 逐 key 读之后,才发现有些判对了,有些判错了——而最有意思的恰恰是那些错判,因为它们暴露了我对内核行为的理解盲区。把正误都列出来,以后回头看就知道当时踩过哪些坑。
2.1 证伪(判断错了)
| 预判 | 原理由 | 实测真相 |
|---|---|---|
vm.page-cluster 连字符是 typo |
标准形式是下划线 page_cluster |
连字符正常工作。sysctl CLI 自动规整化 - → _,读写都无问题。下划线形式反而报 No such file |
sched_*_granularity_ns 等值太激进 |
10μs 调度粒度远超通用服务器默认值 (~3ms) | 这些 key 在 kernel 6.18 (EEVDF) 上根本不存在。CFS 已被 EEVDF 替换,这些旧控制参数被移除。写入 sysctl.d 后一直被 silent ignore |
ip_forward=0 会盖过 NAT |
baseline 裸值优先级高于 NAT 模块的 mkDefault | 实际 ip_forward=1。NixOS 的 NAT activation script 在 sysctl.d 应用之后直接写 /proc/sys/,覆盖了文件中的 0 |
删 transparent_hugepage.* 会丢 THP 配置 |
sysctl.d 中删了就没地方设 THP=madvise 了 | THP 已在 [madvise] 状态,由其他 NixOS 机制保障(kernel boot params / systemd tmpfiles),sysctl 条目从始至终没有生效过 |
2.2 证实(判断对了)
| 预判 | 实测 |
|---|---|
busy_poll/busy_read 当前值 = 50 且活跃生效 |
✅ 确认值 50,sysctl -n 返回正常 |
vm.nr_hugepages = 156 永久锁定 ~312MB |
✅ 确认 156 hugepages(×2MB = 312MB) |
vm.panic_on_oom = 1 OOM 触发整机重启 |
✅ 确认值 1 |
kernel.tsc_reliable 不存在 |
✅ sysctl -n 报 No such file |
net.ipv4.tcp_init_cwnd 不存在 |
✅ sysctl -n 报 No such file |
vm.transparent_hugepage.enabled/defrag 不合法 |
✅ 均报 No such file(在 sysfs 不在 procfs) |
rttMs = 1 低估 BDP |
✅ lib/inventory/data.nix 确认写死 1ms |
Thin / overlay IP 包在 rp_filter=1 下会被丢 |
✅ conf.all.rp_filter = 1,Tailscale/flannel 使用非对称路由 |
3. 最终修改与预期效果
验证完就该动手改了。下面按类型分五组,每组说清楚:原来写了什么、改成了什么、为什么改、改完预期会有什么变化。这样以后回溯时能一眼对上账。
3.1 删除不存在的 sysctl key(9 个)
来源:kernel 6.18 (EEVDF) 已移除的 CFS 参数、不合法路径、已废弃的全局 sysctl。
| 删除项 | 原值 | 原所在行 | 原因 |
|---|---|---|---|
kernel.sched_child_runs_first |
0 | lib/vps-sysctl.nix | EEVDF 移除 |
kernel.sched_min_granularity_ns |
10000 | 同上 | 同上 |
kernel.sched_wakeup_granularity_ns |
15000 | 同上 | 同上 |
kernel.sched_latency_ns |
60000 | 同上 | 同上 |
kernel.sched_migration_cost_ns |
50000 | 同上 | 同上 |
kernel.tsc_reliable |
1 | 同上 | 需特殊 kernel config |
vm.transparent_hugepage.enabled |
"madvise" | 同上 | sysfs 路径,非 sysctl |
vm.transparent_hugepage.defrag |
"never" | 同上 | 同上 |
net.ipv4.tcp_init_cwnd |
动态公式 | 同上 | kernel 5.10+ 已移除全局 sysctl |
tcpInitCwnd 变量 |
— | let 块 | 随删除项一同移除 |
nrHugepages 变量 |
动态公式 | let 块 | 改为硬编码 0 |
预期效果:无行为变化(这些 key 本来就被 systemd-sysctl 静默忽略)。/etc/sysctl.d/60-nixos.conf 干净 ~10 行。
3.2 修正 Proxy 场景有害值(4 个)
| 参数 | 旧值 | 新值 | 原因 | 预期效果 |
|---|---|---|---|---|
net.core.busy_poll |
50 | 0 | 参考脚本面向 HFT/通用网络场景。Proxy 高并发 epoll 下忙轮询白耗 CPU | CPU idle 占用降低,连接数越高越明显 |
net.core.busy_read |
50 | 0 | 同上 | 同上 |
vm.nr_hugepages |
156(312MB) | 0 | Proxy 不需要 hugepage,永久锁 312MB 物理 RAM 是浪费 | 回收 5% 物理内存(6GB VPS),可被 page cache、socket buffer、conntrack 表复用 |
vm.panic_on_oom |
1(panic→reboot) | 0(OOM killer) | Proxy 是持续在线服务。OOM 杀掉肇事进程即可,整机重启中断所有连接更坏 | OOM 时服务继续运行,仅失掉一个进程而非全部连接 |
3.3 架构参数调整(3 个)
| 参数 | 旧值 | 新值 | 原因 | 预期效果 |
|---|---|---|---|---|
net.ipv4.conf.all.rp_filter |
1(strict) | 2(loose) | Tailscale + flannel overlay 使用非对称路由。strict 模式下此类包被误判为"源地址欺骗"而丢弃 | 多网卡 overlay 场景网络连通性保障 |
net.ipv4.conf.default.rp_filter |
1(strict) | 2(loose) | 同上 | 同上 |
net.ipv4.ip_forward + conf.*.forwarding(共 3 行) |
0 | 移出 baseline | 交由 NAT、k3s 等功能模块自行管理。NixOS 组合式架构下 baseline 不应硬设 | 消除模块优先级冲突,下次加新模块不必再面对覆盖问题 |
3.4 策略参数修正(1 个)
| 参数 | 旧值 | 新值 | 原因 | 预期效果 |
|---|---|---|---|---|
vm.overcommit_memory |
0(启发式) | 1(始终 overcommit) | 启发式 overcommit 在 Nix 大构建 fork 大量子进程时保守拒绝,导致 "Cannot allocate memory"。改为 1 后 fork 永远成功 | Nix 构建稳定性提升。物理内存真正耗尽时由 OOM killer 处理(配合 panic_on_oom=0,仅杀进程不重启) |
同步移除 hosts/nixos-vps/default.nix 中原先的 mkForce 0 覆盖(该覆盖是应对 mode 2→mode 0 的 workaround,lib 改 1 后不再需要)。
3.5 数据输入修正(1 个)
| 文件 | 参数 | 旧值 | 新值 | 原因 | 预期效果 |
|---|---|---|---|---|---|
lib/inventory/data.nix |
nixos-vps-dev rttMs |
1 | 150 | 原值来自 Speedtest.net 到最近节点的 RTT(0.867ms)。BDP 计算应用于 proxy 场景时应使用用户路径真实 RTT(LA→China ~150ms) | 单 TCP 连接跨境吞吐从瓶颈 ~223Mbps 恢复到线速能力(~800Mbps)。4MB socket buffer → ~30MB 上限 |
附录
A. 根源分析:Shell 移植到 NixOS 的思维差异
这次审计暴露的核心问题不是 "参数值选错了",而是移植方法论的错位:
| Shell 脚本方式 | NixOS 模块方式 | |
|---|---|---|
| 配置写入 | 一次性写入 /etc/sysctl.d/ |
多模块叠加到同一份 boot.kernel.sysctl |
| 优先级 | 最后一个文件写 = 最终值 | 声明式优先级(裸值 > mkDefault > mkForce) |
| 参数来源 | 参考脚本的全量列表,直接使用 | 应只声明本模块关心的项,其他交给其他模块 |
| kernel 兼容 | 仅面向当前机器 | 相同配置可能在多个 kernel 版本间共享 |
此次出问题的三类情况分别对应:
- 不存在的 key:shell 脚本收集于特定 kernel 版本,平移到新版本后发现 key 不存在
- 不匹配的值:shell 脚本的调优目标(HFT/DB/通用网络)≠ proxy 场景的调优目标
- 架构冲突:shell 脚本假设自己是唯一配置来源,NixOS 中模块间需要协作
修正方向:lib/vps-sysctl.nix 保持计算框架不变,但收缩 baseline 边界——只承诺与该库直接相关的参数,将网络功能开关类参数(ip_forward、forwarding)交还给各功能模块。
B. 保留待审项
以下参数有争议但本次未修改,标注出来供日后回溯:
| 参数 | 当前值 | 来源 | 潜在风险 |
|---|---|---|---|
kernel.sched_rt_runtime_us |
980000(98%) | 原始脚本全量补齐 | RT 线程可占用 98% CPU,若某个 RT 线程死循环,系统接近锁死。默认值 950000 |
kernel.sched_cfs_bandwidth_slice_us |
3000 | 原始脚本全量补齐 | EEVDF 下该参数的语义可能与 CFS 不同,未验证 |
vm.overcommit_ratio |
50 | 原始脚本 | overcommit=1 时此参数无效。若将来切回 mode 2 需重新审视 |
vm.dirty_ratio / vm.dirty_background_ratio |
15 / 5 | SSD 策略默认值 | 对大内存 VPS(>16GB)百分比值意味着 GB 级脏页,可能引发延迟尖峰 |
C. 参考命令集
验证过程中使用的命令,便于复现:
# 确认某个 sysctl key 是否存在
sysctl -n vm.page-cluster
echo "exit: $ ?" # 0=存在,1=不存在
# 列出所有可用的调度参数
sysctl -a | grep "^kernel.sched"
# 查看当前 kernel 版本
uname -r
# 查看 NixOS 生成的 sysctl 配置
cat /etc/sysctl.d/60-nixos.conf
# 查看实际生效值(有时 ≠ sysctl.d 中的值)
cat /proc/sys/net/ipv4/ip_forward
# 检查 sysfs 路径(非 procfs/sysctl)的参数
cat /sys/kernel/mm/transparent_hugepage/enabled
# 内核 config 是否编译了某选项
zgrep CONFIG_X86_TSC /boot/config-$( uname -r ) 2 > /dev/null
# systemd-sysctl 服务状态与日志
systemctl status systemd-sysctl
journalctl -u systemd-sysctl --no-pager | tail -20