引言:为什么内核参数调优是运维工程师的必备技能
Linux 内核参数(kernel parameters)是操作系统运行时行为的控制开关,覆盖了内存管理、进程调度、网络协议栈、文件系统、虚拟内存、安全策略等方方面面。这些参数在系统安装时的默认值是面向通用场景设定的保守值——适用于从笔记本到小型服务器的各类环境,但未必适合你的生产服务器。
举几个常见的业务场景:Nginx 作为反向代理处理海量并发连接时,默认的 fs.file-max 和 net.core.somaxconn 会限制最大连接数;MySQL 数据库在高并发写入场景下,默认的 vm.dirty_ratio 会导致频繁的磁盘 I/O 阻塞;Redis 做消息队列大量使用内存时,默认的 overcommit_memory 设置可能引发 OOM killer 误杀进程;Java 应用启动时,默认的 shmmax / shmmni 可能导致 JVM 无法申请足够的共享内存。
这些问题的共同点是:系统行为异常、但日志里没有明显报错、CPU 和内存利用率也不高——问题往往出在"系统层面"的配置限制,而非应用本身。这就是为什么一个合格的运维工程师,必须掌握通过 sysctl 调整内核参数的方法。
本文面向初中级 Linux 运维工程师,系统讲解:
sysctl 的工作原理和正确用法
生产环境最常用的内核参数分类详解
Nginx、MySQL、Redis、Docker 等常见应用的参数调优
风险控制:哪些参数不能随便改、改错了怎么救
实战案例:从问题现象到参数调整的完整闭环
第一章:sysctl 的工作原理与正确用法
1.1 什么是 sysctl
sysctl 是 Linux 内核提供的一个接口,用于在系统运行时读取和修改内核参数。它操作的对象是 /proc/sys/ 下的虚拟文件系统——这些文件并不存在于磁盘上,而是内核在内存中动态生成的视图,每次读取时由内核实时计算返回。因此 sysctl 修改的是运行中的内核行为,不会持久化到磁盘(除非你写入配置文件)。
常见的内核参数以层级路径的形式组织,例如:
net.ipv4.tcp_tw_reuse:IPv4 协议栈的 TCP 参数,属于 net/ipv4/ 分类
vm.swappiness:虚拟内存子系统,控制交换分区使用倾向
fs.file-max:文件系统子系统的最大文件句柄数
参数名用点号(.)分隔层级,用斜杠(/)对应 /proc/sys/ 下的路径——两者是等价的:sysctl net.ipv4.tcp_tw_reuse 等价于 cat /proc/sys/net/ipv4/tcp_tw_reuse。
1.2 常用 sysctl 命令
# 查看所有内核参数(输出很长,建议配合 grep 过滤) sysctl -a # 查看单个参数的值 sysctl net.ipv4.tcp_tw_reuse # 临时修改参数值(立即生效,但重启后丢失) sysctl -w net.ipv4.tcp_tw_reuse=1 # 从配置文件加载参数(启动时系统自动执行,也是手动刷新的方式) sysctl -p /etc/sysctl.conf # 不指定文件时,默认加载 /etc/sysctl.conf sysctl -p # 查看参数生效状态(显示来源:来自哪个配置文件) sysctl -a --values | grep tcp_tw_reuse
1.3 配置文件 /etc/sysctl.conf
/etc/sysctl.conf 是 sysctl 参数的持久化配置文件。每行一个参数,格式为:
# 格式:参数名 = 值 # 井号开头的是注释 # 开启 IP 转发(容器/路由器场景) net.ipv4.ip_forward = 1 # 减少 TIME_WAIT socket 的回收周期 net.ipv4.tcp_tw_reuse = 1 # 最大文件句柄数 fs.file-max = 655360
系统启动时,/lib/systemd/systemd-sysctl.service 会自动读取并应用 /etc/sysctl.conf 中的配置。因此,在生产环境修改内核参数的正确步骤是:
先用 sysctl -w 临时修改并验证效果(无需重启,立刻生效)
验证生效后,将参数写入 /etc/sysctl.conf(echo '参数名 = 值' >> /etc/sysctl.conf)
用 sysctl -p 重新加载配置(确认配置文件无误)
下次系统重启时,配置自动从 /etc/sysctl.conf 加载
1.4 修改内核参数需要的权限
调整内核参数需要 root 权限。普通用户执行 sysctl -w 会报错:
sysctl: setting key "net.ipv4.ip_forward": Operation not permitted
在容器内部(尤其是非特权容器)修改某些内核参数也会受限,这是容器安全的正常限制。如果容器内确实需要调整内核参数,需要在启动容器时加入 --privileged 参数,或者用 --sysctl 参数指定需要调整的参数名。
1.5 关键原则:修改前先记录原值
在修改任何内核参数之前,必须先记录原始值,以便在出问题后恢复。这是运维的铁律:
# 记录原值 sysctl net.ipv4.tcp_tw_reuse >> /tmp/sysctl_backup_$(date +%Y%m%d).conf # 或者用 sysctl -a > backup.txt 保存完整快照 sysctl -a > /tmp/sysctl_full_backup_$(date +%Y%m%d).txt # 批量记录所有以 net. 开头的参数 sysctl -a | grep '^net.' >> /tmp/sysctl_net_backup_$(date +%Y%m%d).conf
第二章:网络参数调优——并发连接与 TCP 协议栈
网络参数是生产环境调优最频繁的领域。当你的服务器处理大量并发连接(Nginx、Redis、MySQL Proxy、API 网关等场景),或者作为网络通信的中间节点(负载均衡器、VPN 网关、防火墙等),网络内核参数的调整直接决定了系统的吞吐能力和稳定性。
2.1 文件描述符与连接数限制
Linux 对所有资源都有人为限制,文件描述符(file descriptor)是其中最常用的限制维度。每个 TCP 连接占用一个文件描述符、每次打开一个文件占用一个文件描述符。如果文件描述符用尽,新的连接会被系统拒绝。
相关参数:
# 系统级最大文件句柄数(整个系统所有进程共用) fs.file-max = 655360 # 单个进程能拥有的最大文件描述符数(soft limit) fs.nr_open = 655360 # 每个用户最大进程数/文件描述符数(可通过 ulimit 修改) # 在 /etc/security/limits.conf 中配置: # * soft nofile 655360 # * hard nofile 655360
排查命令:
# 查看当前系统已使用的文件描述符总数 cat /proc/sys/fs/file-nr # 输出格式:已分配(已使用但未关闭) 已分配但未使用 最大值 # 例如:5120 0 655360 # 查看某个进程打开的文件描述符数 ls -la /proc/$(pgrep -f nginx | head -1)/fd | wc -l # 查看系统级文件句柄数上限 cat /proc/sys/fs/file-max
调优建议:对于承载高并发连接的服务器(如 Nginx、API 网关),fs.file-max 建议设置为 655360 或更高,具体取决于预估的并发连接数。每个连接至少占用一个 fd,高并发场景下还需要考虑每个连接可能打开多个 fd(主连接 + 多个子连接/文件)。
2.2 TCP 连接数相关参数
**net.core.somaxconn**:定义了每个监听端口的全连接队列(accept queue)的最大长度。当服务器处理速度跟不上连接建立速度时,全连接队列会积压,积压满后新的连接请求会被丢弃。
# 默认值通常是 128,在高并发场景下严重不足 sysctl -w net.core.somaxconn=65535
Nginx 的 backlog 参数、MySQL 的 back_log 参数,都会受 net.core.somaxconn 上限的限制。如果应用层设置了更大的 backlog 但内核参数限制了上限,则实际队列长度以内核参数为准。
**net.ipv4.tcp_max_syn_backlog**:定义了每个监听端口的半连接队列(SYN queue)最大长度。SYN queue 积压通常发生在 SYN Flood 攻击或突发连接建连阶段。
# 默认值 128 或 1000,视内核版本而定 sysctl -w net.ipv4.tcp_max_syn_backlog=65535
实战建议:Nginx 作为反向代理时,通常将 net.core.somaxconn 和 net.ipv4.tcp_max_syn_backlog 都设置为 65535,同时在 Nginx 配置中设置 listen 80 backlog=65535,三者取最小值生效。
**net.ipv4.ip_local_port_range**:定义了本机向外发起连接时使用的源端口范围(用于作为客户端访问其他服务时)。
# 默认通常是 32768 61000,端口范围约 28000 个 # 对于作为 API 客户端的服务器,如果向大量不同目标发连接,端口可能不够用 sysctl -w net.ipv4.ip_local_port_range="1024 65535"
**net.ipv4.tcp_tw_reuse**:允许将 TIME_WAIT 状态的 socket 重新用于新的 TCP 连接,减少端口占用。
# 默认为 0(关闭),对服务器端(监听端口)影响不大,对客户端侧(主动发起连接)有益 # 在作为客户端访问大量目标的场景下(如爬虫、API 聚合服务),开启此选项可以显著减少端口耗尽问题 sysctl -w net.ipv4.tcp_tw_reuse=1
注意:在 NAT 环境或负载均衡器后面的服务器上,开启 tcp_tw_reuse 需要谨慎,因为它可能导致旧连接的报文被新连接误用。但在直接的互联网服务器上,安全性风险较低。
**net.ipv4.tcp_fin_timeout**:TCP 连接关闭时,FIN-WAIT-2 状态持续多久后自动关闭。
# 默认为 60 秒 sysctl -w net.ipv4.tcp_fin_timeout=15
这个参数影响本端关闭连接后、对端还未确认的连接能维持多久。值越小,释放连接越快,但可能导致对端还有数据未发送完就断开。
2.3 TCP 内存与缓冲区
TCP 协议栈在内存中为每个连接维护发送缓冲区和接收缓冲区。这些缓冲区的大小直接影响每个连接能缓存多少未确认的数据,以及系统能承载的最大并发连接数。
net.ipv4.tcp_rmem 和 **net.ipv4.tcp_wmem**:TCP 连接的接收缓冲区和发送缓冲区大小。格式为三个值:最小值 默认值 最大值,单位是字节。
# TCP 接收缓冲区:最小4KB、默认87KB、最大6MB sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456" # TCP 发送缓冲区:最小4KB、默认87KB、最大6MB sysctl -w net.ipv4.tcp_wmem="4096 87380 6291456"
**net.core.rmem_max 和 net.core.wmem_max**:所有协议栈(包括 TCP)的接收和发送缓冲区最大值,用于限制 tcp_rmem/tcp_wmem 的上限。
sysctl -w net.core.rmem_max=12582912 sysctl -w net.core.wmem_max=12582912
**net.ipv4.tcp_mem**:TCP 协议栈整体内存使用阈值。格式为三个值(单位是页数,每页通常 4KB):低于第一个值时不限制、达到第二个值时开始限制、达到第三个值时拒绝新连接。
# 示例:系统有 32GB 内存时 sysctl -w net.ipv4.tcp_mem="786432 1048576 1572864"
调优原则:高并发长连接场景(如 Redis 客户端、数据库代理),需要适当增大缓冲区以提升吞吐;高并发短连接场景,则更关注连接数上限而非缓冲区大小。
2.4 TIME_WAIT 问题的根因与解决
TIME_WAIT 是 TCP 连接关闭时,本端收到对端的 FIN 后进入的等待状态,持续 2MSL(通常约 60 秒)。在高并发的 HTTP API 服务器上,TIME_WAIT 状态的连接会大量占用本地端口(ip_local_port_range),可能导致端口耗尽。
完整的 TIME_WAIT 解决方案:
# 方案1:开启 tcp_tw_reuse(推荐,用于客户端侧) net.ipv4.tcp_tw_reuse = 1 # 方案2:降低 MSL 时间(需谨慎,影响 TCP 可靠性) net.ipv4.tcp_fin_timeout = 15 # 方案3:启用 tcp_tw_recycle(已废弃,现代 Linux 内核不再支持,因为会破坏 NAT 环境下的连接) # 不要设置 net.ipv4.tcp_tw_recycle = 1 # 方案4:客户端使用连接池复用连接,从根本上减少短连接数量
风险提醒:不要在生产环境盲目开启 tcp_tw_recycle。该参数在 Linux 4.12 之后已被移除,因为它会导致 NAT 环境下严重的连接故障。如果端口耗尽问题严重,应该从架构层面(使用连接池、HTTP/1.1 keep-alive、HTTP/2)解决,而不是依赖有争议的内核参数。
2.5 网络参数调优配置示例
以下是一份面向高并发 Web 服务的完整网络参数调优配置:
# === 文件描述符与连接数 === fs.file-max = 655360 fs.nr_open = 655360 # === TCP 连接队列 === net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 # === TCP 端口范围 === net.ipv4.ip_local_port_range = 1024 65535 # === TCP 时间参数 === net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_tw_reuse = 1 # === TCP 缓冲区 === net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_wmem = 4096 87380 6291456 net.core.rmem_max = 12582912 net.core.wmem_max = 12582912 net.ipv4.tcp_mem = 786432 1048576 1572864 # === TCP 性能参数 === net.ipv4.tcp_slow_start_after_idle = 0 # 关闭空闲后的慢启动,适合长连接场景 net.ipv4.tcp_keepalive_time = 300 # 空闲长连接保活时间(秒) net.ipv4.tcp_keepalive_probes = 3 # 保活探测次数 net.ipv4.tcp_keepalive_intvl = 10 # 保活探测间隔(秒) # === 其他网络参数 === net.ipv4.conf.all.rp_filter = 1 # 开启反向路径过滤,防 IP 欺骗 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.all.accept_redirects = 0 # 禁用 ICMP 重定向 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0
第三章:内存与虚拟内存参数调优
3.1 交换分区(swap)相关参数
**vm.swappiness**:控制内核将页面换出到交换分区的倾向,取值范围 0-100。值越高越倾向于使用 swap,越低越倾向于使用物理内存。
# 默认值通常是 60 # 对于有足够物理内存的 MySQL/Redis 服务器,建议设置为 1 甚至 0 # 这样可以尽量避免内核将热点数据换出,保证数据库性能 sysctl -w vm.swappiness=1
注意:swappiness=0 在某些旧版内核(如 RHEL/CentOS 6 的早期版本)中可能不生效,因为内核的实现逻辑有差异。如果需要彻底禁用 swap 而非仅调整倾向,需要同时使用 swapoff -a(但生产环境轻易不要关闭 swap,因为它是防止 OOM 的最后一道防线)。
vm.dirty_ratio 和 **vm.dirty_background_ratio**:控制文件系统的脏页(修改过但尚未写入磁盘的页面)在内存中的比例。
# vm.dirty_background_ratio:后台刷新进程开始刷新脏页的阈值(占可用内存的百分比) # vm.dirty_ratio:同步刷新开始前的最大脏页阈值 # # 默认 dirty_background_ratio = 10,dirty_ratio = 20 # # 对于 MySQL 等频繁写入的场景,过低的值会导致频繁的磁盘 I/O # 过高的值会导致一次性大量写入,造成 I/O 突刺 # 建议根据应用写入模式调整: sysctl -w vm.dirty_background_ratio=5 sysctl -w vm.dirty_ratio=10
vm.dirty_writeback_centisecs 和 **vm.dirty_expire_centisecs**:控制内核多久检查一次脏页过期、以及脏页多久后必须写回。
# dirty_writeback_centisecs:后台刷新线程的检查间隔(1/100秒) # 默认 500,即每5秒检查一次 sysctl -w vm.dirty_writeback_centisecs=500 # dirty_expire_centisecs:脏页被认为"可写回"的时间阈值(1/100秒) # 默认 3000,即30秒 sysctl -w vm.dirty_expire_centisecs=3000
3.2 共享内存参数
共享内存是数据库(MySQL InnoDB、Oracle)、Java JVM(IPC 通信)常用的进程间通信机制。如果共享内存参数不足,数据库启动时会报错或者使用较慢的磁盘文件模拟。
**kernel.shmmax**:单个共享内存段的最大字节数。
# MySQL 建议:设置为服务器物理内存的一半或更大 # 例如 64GB 内存的服务器: sysctl -w kernel.shmmax=34359738368 # 32GB = 32*1024*1024*1024 # Oracle 建议:设置为 SGA 大小的 1.5-2 倍
**kernel.shmall**:系统范围内所有共享内存页面的总上限(单位是页面数,x86_64 下每页 4KB)。
# 可用内存总量控制:总共享内存字节数 / 页面大小 = 页面数 # 64GB 内存:64*1024*1024*1024 / 4096 = 16777216 sysctl -w kernel.shmall=16777216
**kernel.shmmni**:系统最大共享内存段数量。
# 默认通常是 4096,对于运行多个数据库实例的场景可能不足 sysctl -w kernel.shmmni=8192
3.3 OOM(Out of Memory)相关参数
当系统内存耗尽时,Linux 内核的 OOM Killer 会选择并杀掉某个进程来释放内存。这个行为虽然保护了系统不因内存完全耗尽而崩溃,但也可能导致重要服务(如数据库)被误杀。
**vm.overcommit_memory**:控制内核对内存分配请求的"过度分配"策略。
# 0:默认值,内核保守地估计是否可分配(大多数场景用这个) # 1:始终允许分配,不做检查(适合知道自己不会有 OOM 问题的场景) # 2:严格限制,分配不能超过 (swap + RAM * overcommit_ratio) sysctl -w vm.overcommit_memory=1 # MySQL/Redis 推荐设为1
**MySQL 为什么推荐 overcommit_memory=1**:InnoDB Buffer Pool 的内存分配是"大块连续内存"请求,如果内核过度保守地拒绝分配,可能导致 MySQL 无法启动或运行不稳。设置 overcommit_memory=1 让内核不做过度分配检查,由应用层自己管理。
**vm.overcommit_ratio**:当 overcommit_memory=2 时,此参数控制可分配的内存比例(百分比)。
# 默认 50,即最多分配 swap + (RAM * 50%) sysctl -w vm.overcommit_ratio=95
**vm.oom_dump_tasks**:OOM 时是否导出任务列表(有助于分析为什么 OOM)。
# 建议开启,便于故障后分析 sysctl -w vm.oom_dump_tasks=1
3.4 内存参数调优配置示例
# === Swap 与内存管理 === vm.swappiness = 1 # 有充足内存的数据库服务器 vm.overcommit_memory = 1 # MySQL/Redis 推荐 vm.overcommit_ratio = 95 vm.oom_dump_tasks = 1 # === 脏页刷新 === vm.dirty_background_ratio = 5 vm.dirty_ratio = 10 vm.dirty_writeback_centisecs = 500 vm.dirty_expire_centisecs = 3000 # === 共享内存 === kernel.shmmax = 34359738368 # 32GB kernel.shmall = 16777216 # 64GB / 4096 kernel.shmmni = 8192
第四章:进程与 IPC 参数调优
4.1 进程数与线程数限制
**kernel.pid_max**:系统最大 PID 号。超过此值的 PID 会回绕重用。
# 默认值 32768,在高并发场景下可能不够用 # 建议设置为比预估最大进程数大一个数量级 sysctl -w kernel.pid_max=655360
**kernel.threads-max**:系统最大线程数。
# 默认值:内存页数/4,或者 32768,取两者中的较小值 # 高并发 Java 应用服务器建议: sysctl -w kernel.threads-max=655360
4.2 IPC 信号量与消息队列
**kernel.sem**:控制 System V 信号量集合的参数。格式为四个值:SEMMSL(每个信号量集合最大信号量数)、SEMMNI(最大信号量集合数)、SEMMNS(整个系统最大信号量数)、SEMOPM(每个 semop 调用最大操作数)。
# Oracle 等大型数据库需要较高的信号量参数 # 示例值:250 32000 100000000 500 sysctl -w kernel.sem="250 32000 100000000 500"
kernel.msgmnb 和 **kernel.msgmni**:消息队列单个队列最大字节数 和 最大消息队列数。
sysctl -w kernel.msgmnb=65536 sysctl -w kernel.msgmni=8192
第五章:常见应用的 sysctl.conf 调优实战
5.1 Nginx 服务器
Nginx 作为反向代理或 Web 服务器,最需要关注的是连接数限制和文件描述符。
需要调整的参数:
# 文件描述符 fs.file-max = 655360 fs.nr_open = 655360 # TCP 连接队列 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 # 端口范围(如果 Nginx 作为上游客户端访问后端) net.ipv4.ip_local_port_range = 1024 65535 # TCP 时间参数 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_tw_reuse = 1 # TCP 缓冲区 net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_wmem = 4096 87380 6291456 net.core.rmem_max = 12582912 net.core.wmem_max = 12582912
Nginx 配置中的配合设置:
# worker_rlimit_nofile 需要与 fs.file-max 配合
worker_rlimit_nofile 655360;
events {
worker_connections 65535;
use epoll; # Linux 下使用 epoll 多路复用
multi_accept on; # 每个 worker 尽可能多地接受新连接
}
http {
keepalive_timeout 65;
keepalive_requests 10000; # 长连接最大请求数
sendfile on;
tcp_nopush on; # 优化 TCP 包发送
tcp_nodelay on; # 禁用 Nagle 算法,适合 HTTP
}
5.2 MySQL 数据库服务器
MySQL(InnoDB 引擎)的调优重点是内存管理和 InnoDB Buffer Pool、redo log 相关的内核参数。
需要调整的参数:
# 共享内存(MySQL 强烈建议) kernel.shmmax = 34359738368 # 32GB kernel.shmall = 16777216 # 64GB 内存 # 内存与 swap vm.overcommit_memory = 1 # InnoDB 内存分配策略 vm.swappiness = 1 # 避免 swap 污染 Buffer Pool # 文件描述符 fs.file-max = 655360 # TCP 参数(MySQL 连接数相关) net.ipv4.tcp_max_syn_backlog = 65535 net.core.somaxconn = 65535 net.ipv4.ip_local_port_range = 1024 65535 net.ipv4.tcp_fin_timeout = 15
MySQL 配置中的配合设置(my.cnf):
[mysqld] # 最大连接数(需配合 ulimit -n) max_connections = 10000 # InnoDB Buffer Pool 大小(通常设为物理内存的 60-80%) innodb_buffer_pool_size = 48G # redo log 文件大小和数量(MySQL 8.0 推荐每个 512MB,共 4-8 个) innodb_log_file_size = 512M innodb_log_files_in_group = 4 # 最大打开文件数 open_files_limit = 655360 # 临时表和排序缓冲区大小 tmp_table_size = 256M sort_buffer_size = 4M
风险提醒:innodb_buffer_pool_size 不能超过物理内存减去操作系统和其他进程的内存需求。如果设置过大导致系统 swap,反而严重影响性能。另外,修改 innodb_log_file_size 需要关闭 MySQL 并删除旧的 redo log 文件才能生效(MySQL 8.0 支持在线调整)。
5.3 Redis 服务器
Redis 是内存数据库,对内存管理尤其敏感。
需要调整的参数:
# 内存管理 vm.overcommit_memory = 1 # Redis 必须 vm.swappiness = 1 # 尽量不 swap vm.max_map_count = 655360 # Redis AOF 和子进程需要大量 mmap # 文件描述符 fs.file-max = 655360 # TCP 参数 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535
Redis 配置中的配合设置(redis.conf):
# 最大内存(根据物理内存设置,不要超过物理内存) maxmemory 32gb # 内存淘汰策略(生产环境建议 volatile-lru 或 allkeys-lru) maxmemory-policy allkeys-lru # AOF 持久化(开启 always 或 everysec,不要用 no) appendfsync everysec # 最大连接数 maxclients 65536
**vm.max_map_count**:Redis 在开启 AOF 并使用 bgrewriteaof 子进程进行重写时,会使用 mmap 系统调用映射大量内存页。如果 max_map_count 不足,Redis 会报错:
MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk
这个报错实际上常常是因为 vm.max_map_count 不足,而非真正的磁盘问题。
5.4 Docker 主机
Docker 运行容器时,会为每个容器设置自己的网络命名空间、进程命名空间等。大量运行容器时,需要调高相关内核参数。
需要调整的参数:
# 进程与线程 kernel.pid_max = 655360 kernel.threads-max = 655360 # 共享内存(容器共享内存场景,如 POSIX semaphore) kernel.shmmax = 68719476736 kernel.shmall = 16777216 kernel.shmmni = 8192 # 网络(大量容器需要大量 IPConntrack) net.netfilter.nf_conntrack_max = 1048576 net.nf_conntrack_max = 1048576 # 文件描述符 fs.file-max = 655360 # mmap 数量限制 vm.max_map_count = 655360
Docker 配置中的配合设置(/etc/docker/daemon.json):
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 655360,
"Soft": 655360
}
},
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2"
}
第六章:修改内核参数的标准流程与风险控制
6.1 标准操作流程
第一步:评估当前状态
# 查看当前关键参数的默认值 sysctl net.core.somaxconn sysctl fs.file-max sysctl vm.swappiness sysctl vm.overcommit_memory # 查看当前系统资源使用情况 free -h # 内存使用 df -h # 磁盘使用 cat /proc/meminfo | grep -E 'SwapFree|SwapTotal' uptime # 负载
第二步:记录原始值
# 备份所有 sysctl 参数
sysctl -a > /tmp/sysctl_backup_original_$(date +%Y%m%d).conf
# 单独备份计划修改的参数
{
echo "# Backup $(date)"
sysctl net.core.somaxconn
sysctl fs.file-max
sysctl vm.swappiness
sysctl vm.overcommit_memory
sysctl net.ipv4.tcp_tw_reuse
sysctl net.ipv4.tcp_fin_timeout
sysctl vm.max_map_count
} > /tmp/sysctl_tuned_backup_$(date +%Y%m%d).conf
第三步:逐个修改并验证
# 修改单个参数 sysctl -w net.core.somaxconn=65535 # 验证是否生效 sysctl net.core.somaxconn # 验证应用层是否受影响(如果应用在运行) # 例如:查看 Nginx 当前连接数 ss -s | grep -i socket ss -ant | grep -E ':80|:443' | wc -l
第四步:写入配置文件
# 将参数追加到配置文件(不覆盖原有内容) echo 'net.core.somaxconn = 65535' >> /etc/sysctl.conf # 重新加载所有配置,确认无报错 sysctl -p # 如果只想验证特定文件的语法: sysctl --load=/etc/sysctl.conf
第五步:应用验证
# 重启应用,验证服务正常运行 systemctl restart nginx # 检查应用日志 journalctl -u nginx -n 50 --no-pager # 检查应用状态 systemctl status nginx
6.2 改错了怎么办
如果修改参数后系统出现异常(如网络不通、服务起不来、SSH 断开),按以下顺序恢复:
立即恢复单个参数:
# 如果是远程操作,先尝试单个恢复 # 从备份文件中找到原始值,然后: sysctl -w net.core.somaxconn=128 # 恢复为默认值
一键恢复所有参数:
# 从备份文件恢复所有参数 sysctl -p /tmp/sysctl_backup_original_20260514.conf
如果已经覆盖了 /etc/sysctl.conf:
# 从系统包管理器恢复默认配置(如果有备份) # Debian/Ubuntu: # dpkg --configure -a # 不会恢复 sysctl.conf # 如果有备份直接恢复: cp /tmp/sysctl_backup_original_20260514.conf /etc/sysctl.conf sysctl -p # 如果完全无法恢复,用手敲默认值重建 /etc/sysctl.conf # 常见默认值: # net.core.somaxconn = 128 # net.ipv4.tcp_max_syn_backlog = 128 # fs.file-max = 808912 # vm.swappiness = 60 # vm.overcommit_memory = 0
最坏情况:如果 SSH 断开且无法恢复,需要通过带外管理(iLO/iDRAC/KVM/IPMI)进入服务器操作,或者联系机房值班人员协助。
6.3 不能随便改的参数
以下参数在生产环境修改需要特别谨慎,最好有充分的测试和回滚方案:
| 参数 | 默认值 | 风险描述 | 建议 |
|---|---|---|---|
| net.ipv4.conf.all.rp_filter | 1 | 改为 0 会关闭反向路径过滤,失去防 IP 欺骗能力 | 不要在生产环境改为 0 |
| net.ipv4.conf.all.accept_redirects | 1 | 改为 1 会接收 ICMP 重定向,可能导致路由劫持 | 生产环境保持 0 |
| net.ipv4.conf.all.accept_source_route | 0 | 允许源路由包,可能导致流量被劫持 | 保持 0 |
| kernel.pid_max | 32768 | 改为过大的值可能导致 PID 回绕周期变长,不易察觉进程泄漏 | 根据实际需求调整,不要过度贪大 |
| vm.overcommit_memory | 0 | 设为 1 可能导致真正的 OOM 时系统无响应(因为内核不会拒绝分配) | 设为 1 只在确认应用能自己管理内存时使用 |
| net.ipv4.tcp_tw_reuse | 0 | 设为 1 在 NAT 环境可能导致旧连接复用 | 在反向代理/NAT 场景设为 0 |
| net.ipv4.conf.all.forwarding | 0 | 设为 1 开启 IP 转发,服务器变成路由器 | 只在需要时才开启,不需要时严禁打开 |
6.4 容器环境中的 sysctl 参数
在 Kubernetes 或 Docker 环境中运行容器时,容器默认共享宿主机的内核命名空间,但某些敏感参数在容器内无法修改。Kubernetes 提供了 securityContext.sysctls 来安全地设置容器级 sysctl 参数。
Kubernetes Pod 级别设置:
apiVersion: v1 kind: Pod metadata: name: sysctl-pod spec: securityContext: sysctls: - name: net.core.somaxconn value: "65535" - name: vm.swappiness value: "1" containers: - name: app image: nginx
注意:Kubernetes 中只有"安全的" sysctl 参数才能在 Pod 级别设置。以下参数被标记为不安全,集群管理员必须使用 privileged Pod 或者在 kubelet 配置中启用:
# 不安全的 sysctl(需要额外配置): # net.ipv4.conf.all.route_localnet # net.ipv4.conf.default.forwarding # net.ipv4.conf.all.forwarding # net.ipv4.conf.default.accept_redirects # net.ipv4.conf.all.accept_redirects
第七章:生产环境实战案例
7.1 案例:Nginx 报 502,但服务明明在运行
场景描述:某台 Nginx 反向代理服务器,上游配置了 5 台后端 Python API 服务器。在业务高峰期,前端频繁出现 502 Bad Gateway 错误,但后端服务日志显示请求量正常、没有错误。
初步分析:502 表示 Nginx 无法连接到上游服务器。可能原因:上游连接数达到上限、上游服务器拒绝连接、网络丢包导致连接超时。
排查过程:
# 第1步:检查 Nginx 错误日志 $ tail -100 /var/log/nginx/error.log | grep -E 'connect|upstream' 2026/05/14 1045 [error] 12345#0: *98765 connect() failed (99: Cannot assign address) while connecting to upstream, client: 10.10.10.50
错误信息 Cannot assign address 通常是端口资源耗尽的典型表现——Nginx 作为客户端向上游发起连接时,本地端口(由 ip_local_port_range 控制)不够用了。
# 第2步:检查当前端口使用情况 $ sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_port_range = 32768 61000 # 范围只有约 28000 个端口 # 检查当前已使用的端口数 $ ss -ant | wc -l 34521 # 已使用 34000+,接近上限 # 检查 TIME_WAIT 连接数 $ ss -ant state time-wait | wc -l 21543 # 大量 TIME_WAIT
根因定位:ip_local_port_range 只有约 28000 个端口,但已有 34000+ 连接(包含 TIME_WAIT),端口即将耗尽。同时 tcp_tw_reuse=0(默认),导致 TIME_WAIT 连接无法快速回收。
修复方案:
# 立即生效 sysctl -w net.ipv4.ip_local_port_range="1024 65535" sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_fin_timeout=15 # 写入配置文件 echo 'net.ipv4.ip_local_port_range = 1024 65535' >> /etc/sysctl.conf echo 'net.ipv4.tcp_tw_reuse = 1' >> /etc/sysctl.conf echo 'net.ipv4.tcp_fin_timeout = 15' >> /etc/sysctl.conf # 重新加载 sysctl -p # 验证 $ sysctl net.ipv4.ip_local_port_range net.ipv4.ip_local_port_range = 1024 65535
同时优化 Nginx 配置,减少向上游的短连接:
upstream backend {
server 10.10.11.11:8000 max_fails=3 fail_timeout=30s;
server 10.10.11.12:8000 max_fails=3 fail_timeout=30s;
keepalive 100; # 使用长连接池,减少短连接数量
}
server {
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1; # 必须用 HTTP/1.1 才能使用 keepalive
proxy_set_header Connection "";
}
}
验证修复:
# 持续监控连接数 watch -n 2 'ss -ant | wc -l' # 观察 Nginx 502 错误是否消失 tail -f /var/log/nginx/error.log | grep 502 # 1小时后检查 TIME_WAIT 数量 ss -ant state time-wait | wc -l # 应该从 20000+ 降到 5000 以下
7.2 案例:MySQL 启动失败,共享内存不足
场景描述:新采购的服务器(128GB 内存),安装 MySQL 5.7 后无法启动,日志报错:
[ERROR] InnoDB: Cannot allocate memory for the buffer pool [ERROR] Plugin 'InnoDB' initialization function failed. [ERROR] Server exit(-1) insulating in start-up.
排查过程:
# 检查 MySQL 配置的 innodb_buffer_pool_size $ grep innodb_buffer_pool_size /etc/my.cnf innodb_buffer_pool_size = 96G # 设置了 96GB # 检查 kernel.shmmax 当前值 $ sysctl kernel.shmmax kernel.shmmax = 33554432 # 只有 32MB,远低于 96GB # 检查实际可分配的共享内存 $ sysctl kernel.shmall kernel.shmall = 2097152 # 只有 8GB (2097152 * 4KB)
根因:kernel.shmmax(单个共享内存段最大大小)只有 32MB,而 innodb_buffer_pool_size=96G 需要申请一个至少 96GB 的连续共享内存段。内核无法分配如此大的连续空间,导致 MySQL 启动失败。
修复方案:
# 调整为合适的值(设置为物理内存的 50-75%) sysctl -w kernel.shmmax=68719476736 # 64GB = 64*1024*1024*1024 sysctl -w kernel.shmall=33554432 # 64GB / 4096 = 16777216,但 shmmax 用 64GB 时需要更大的 shmall # shmall 的单位是页(4KB),64GB / 4096 = 16777216 sysctl -w kernel.shmall=16777216 # 写入配置 cat >> /etc/sysctl.conf << 'EOF' kernel.shmmax = 68719476736 kernel.shmall = 16777216 EOF # 重新加载 sysctl -p # 验证 sysctl kernel.shmmax sysctl kernel.shmall # 启动 MySQL systemctl start mysqld systemctl status mysqld
验证 MySQL 正常运行:
# 检查 InnoDB 内存分配是否成功 mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';" mysql -u root -p -e "SELECT @@innodb_buffer_pool_size / 1024 / 1024 / 1024 AS 'Buffer Pool GB';" # 检查物理内存实际使用 free -h ps aux | grep mysqld | grep -v grep
7.3 案例:Java 应用 OOM,但系统还有大量空闲内存
场景描述:运行在物理机上的 Java 应用(16GB 物理内存,JVM Heap 8GB)每隔几天就会 OOM 崩溃。运维检查发现:崩溃时系统还有 6GB+ 空闲内存,free -m 显示内存并没有耗尽。JVM 的堆转储文件显示是 DirectByteBuffer 申请了过多堆外内存。
排查过程:
# 检查系统内存使用
$ free -m
total used free shared buff/cache available
Mem: 15876 14230 234 64 1440 1234
Swap: 2047 512 1535
# 观察发现 used=14GB, available=1.2GB, buff/cache=1.4GB
# 系统内存其实已经紧张了,但 Java 进程的 RSS 看起来不高
# 检查 Java 进程的内存映射
$ cat /proc/$(pgrep -f java)/smaps | grep -E '^[0-9a-f]' | awk '{sum+=$2} END {print sum/1024 " MB mapped"}'
# 或直接看 N个进程的总内存映射
$ for pid in $(pgrep -f java); do cat /proc/$pid/smaps; done | awk '/^[0-9a-f]/{sum+=$2} END {print sum/1024 " MB total mapped"}'
# 检查 overcommit 设置
$ sysctl vm.overcommit_memory
vm.overcommit_memory = 0
# 0 表示默认(启发式计算),对于 Java 应用不够准确
根因:vm.overcommit_memory=0(默认),内核会保守地估算是否允许内存分配。Java NIO 的 DirectByteBuffer 使用堆外内存(off-heap),当系统内存碎片化或接近耗尽时,内核可能拒绝新的内存分配,导致 OOM。但 free 命令显示的"空闲内存"并不等于"可分配连续内存",因为 buff/cache 虽然可以回收但需要时间。
修复方案:
# 对 Java 应用,设为 1(允许过度分配,由应用自己管理内存) sysctl -w vm.overcommit_memory=1 # 写入配置 echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf # 重新加载 sysctl -p # 验证 sysctl vm.overcommit_memory # 重启 Java 应用 systemctl restart java-app
配合 JVM 参数,限制 DirectByteBuffer 最大堆外内存:
# 在 Java 启动参数中限制 MaxDirectMemorySize java -XX:MaxDirectMemorySize=2g -jar your-app.jar
复盘:本案例的问题不在于 JVM Heap 不足,而在于堆外内存(DirectByteBuffer)和系统内存碎片化。overcommit_memory=1 让内核不再保守拒绝分配,但根本解决思路是限制应用的最大堆外内存使用,防止应用本身耗尽系统内存。
总结:内核参数调优的工程化实践
核心原则回顾
先记录,后修改。任何内核参数修改前,必须备份原始值,制定回滚方案。
小步快跑,逐步验证。不要一次性修改十几个参数,每次只改 1-2 个参数,观察效果,确认无副作用后再继续。
理解参数含义再改。不要抄一个配置清单而不理解每个参数的意思。如果不确定某个参数的作用,先查文档(man sysctl、man proc),确认后再应用。
生产环境的修改必须走变更流程。内核参数修改属于对系统行为的重大变更,需要有变更申请、审批、实施、验证、备份的标准流程。
调优要有依据和目标。不要为了"优化"而优化。每次调优都应基于明确的性能问题或容量瓶颈,有量化指标(如连接数、延迟、吞吐量)作为前后对比依据。
调优检查清单
在完成内核参数调优后,用以下清单做最终验证:
#!/bin/bash # sysctl_verify.sh - 调优后验证脚本 echo "=== 内核参数调优验证 ===" # 1. 文件描述符 echo "[1] 文件描述符上限: $(sysctl fs.file-max)" echo "[2] 单进程最大 fd: $(sysctl fs.nr_open)" # 2. 网络参数 echo "[3] somaxconn: $(sysctl net.core.somaxconn)" echo "[4] tcp_max_syn_backlog: $(sysctl net.ipv4.tcp_max_syn_backlog)" echo "[5] ip_local_port_range: $(sysctl net.ipv4.ip_local_port_range)" echo "[6] tcp_tw_reuse: $(sysctl net.ipv4.tcp_tw_reuse)" echo "[7] tcp_fin_timeout: $(sysctl net.ipv4.tcp_fin_timeout)" # 3. 内存参数 echo "[8] swappiness: $(sysctl vm.swappiness)" echo "[9] overcommit_memory: $(sysctl vm.overcommit_memory)" echo "[10] max_map_count: $(sysctl vm.max_map_count)" echo "[11] shmmax: $(sysctl kernel.shmmax)" echo "[12] shmall: $(sysctl kernel.shmall)" # 4. 进程参数 echo "[13] pid_max: $(sysctl kernel.pid_max)" echo "[14] threads-max: $(sysctl kernel.threads-max)" echo "=== 验证完成 ==="
常见参数默认值速查表
| 参数 | CentOS 7 默认值 | Ubuntu 默认值 | 推荐生产值 |
|---|---|---|---|
| net.core.somaxconn | 128 | 128 | 65535 |
| net.ipv4.tcp_max_syn_backlog | 128 | 128 | 65535 |
| fs.file-max | 808912 | 808912 | 655360+ |
| vm.swappiness | 60 | 60 | 1(数据库服务器)/ 10(通用服务器) |
| vm.overcommit_memory | 0 | 0 | 1(MySQL/Redis/Java) |
| vm.max_map_count | 65530 | 65530 | 655360(Elasticsearch/Redis AOF) |
| kernel.shmmax | 33554432 | 68719476736 | 物理内存 50-75% |
| net.ipv4.ip_local_port_range | 32768 61000 | 32768 60999 | 1024 65535 |
| kernel.pid_max | 32768 | 32768 | 655360 |
内核参数调优是运维工程师从"会用命令"到"懂系统行为"的一道门槛。参数本身不难记,难的是理解每个参数背后影响的是什么系统机制、在什么场景下需要调整、以及调整后会有什么副作用。掌握了这些,你就具备了系统级的问题排查和性能优化能力,这正是一线运维工程师最核心的价值所在。
全部0条评论
快来发表一下你的评论吧 !