概览
Linux服务器内核优化,包括优化系统环境、内核和网络,相关内核优化脚本可参考centos_init.sh
/etc/sysctl.conf内核参数:
- accept的网络连接数优化
相关参数:net.core.somaxconn
web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,加大队列长度为,可以容纳更多等待连接accept的网络连接数
方式1
终端执行:
echo "net.core.somaxconn = 2048" >> /etc/sysctl.conf
方式2
echo 2048 > /proc/sys/net/core/somaxconn
但这样系统重启后保存不了
让配置生效
sysctl -p /etc/sysctl.conf
注:backlog过小,可能会出现accept速度跟不上,A。B队列满了,导致新的客户端无法连接,backlog对程序支持的连接数并无影响,backlog影响的只是还没有被accept取出的连接,具体详见:http://blog.csdn.net/ordeder/article/details/21551567
- 高并发情况下系统同时处理的“任务”优化
相关参数:net.nf_conntrack_max
相关链接:http://www.cnblogs.com/mydomain/archive/2013/05/19/3087153.html
conntrack_max是在内核内存中netfilter可以同时处理的“任务”(连接跟踪条目)
如果设置数据过低且开启iptables,在高并发情况下系统/var/messages日志会报“ip_conntrack: table full, dropping packet.”错误,ping也丢包很严重
方式1
终端执行:
echo "net.nf_conntrack_max = 655350" >> /etc/sysctl.conf
echo "net.netfilter.nf_conntrack_tcp_timeout_established = 1200 " >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf
注:如果报错:
error: "net.nf_conntrack_max" is an unknown key
则需要使用modprobe载入ip_conntrack模块,lsmod查看模块已载入。
modprobe ip_conntrack
如果报错:
error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key
error: "net.bridge.bridge-nf-call-iptables" is an unknown key
error: "net.bridge.bridge-nf-call-arptables" is an unknown key
modprobe bridge
注:实际上修改了sysctl.conf里的net.nf_conntrack_max参数并执行
sysctl -p
,将会同步刷新/proc/sys/net/netfilter/nf_conntrack_max数值
方式2
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
echo 655350 >/proc/sys/net/ipv4/ip_conntrack_max
内核2.6是另外一种执行
echo 655350 > /proc/sys/net/netfilter/nf_conntrack_max
注意要先执行iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT触发生成/proc/sys/net/ipv4/各变量文件,再个性相应的/proc/sys/net/ipv4/ip_conntrack_max值
要实现每次开机后都生效的话,要加到开机启动脚本/etc/rc.local里,其他开机自动运行的脚本也行
查看conntrack类型
lsmod |grep conntrack
要查看当前系统ip_conntrack的大小可以通过命令:
cat /proc/sys/net/ipv4/netfilter/ip_conntrack_count
内核2.6是另外一种执行
cat /proc/sys/net/netfilter/nf_conntrack_count
- time_wait网络连接优化
参考:http://coolshell.cn/articles/11564.html
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
这个命令会输出类似下面的结果:
LAST_ACK 16
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18098
我们只用关心TIME_WAIT的个数,在这里可以看到,有18000多个TIME_WAIT,这样就占用了18000多个端口。要知道端口的数量只有65535个,占用一个少一个,会严重的影响到后继的新连接。这种情况下,我们就有必要调整下Linux的TCP内核参数,让系统更快的释放TIME_WAIT连接。
echo "net.ipv4.tcp_syncookies = 0" >> /etc/sysctl.conf
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
echo "net.ipv4.tcp_tw_recycle = 1" >> /etc/sysctl.conf
echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.conf
各参数说明:
net.ipv4.tcp_tw_reuse = 1
表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout = 30
修改系統默认的 TIMEOUT 时间,如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。经过这样的优化配置之后,你的服务器的TCP并发处理能力会显著提高。以上配置仅供参考,用于生产环境请根据自己的实际情况
netstat -apn | grep "TIME_WAIT" | wc -l
查看TIME_WAIT状态的连接数量
- ddos防御优化
net.ipv4.tcp_syncookies = 0
当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; 关于SYN Flood攻击:一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后,就下线了,于是服务器需要默认等63s才会断开连接,这样,攻击者就可以把服务器的syn连接的队列耗尽,让正常的连接请求不能处理。于是,Linux下给了一个叫tcp_syncookies的参数来应对这个事——当SYN队列满了后,TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去(又叫cookie),如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie发回来,然后服务端可以通过cookie建连接(即使你不在SYN队列中)。请注意,请先千万别用tcp_syncookies来处理正常的大负载的连接的情况。因为,synccookies是妥协版的TCP协议,并不严谨。对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是:tcp_synack_retries 可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大SYN连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。 查看是否synccookie开启:cat /proc/sys/net/ipv4/tcp_syncookies
net.ipv4.tcp_syn_retries = 0
参考:http://tech.uc.cn/?p=1790
tcp_synack_retries = 0,表示回应第二个握手包(SYN+ACK包)给客户端IP后,如果收不到第三次握手包(ACK包)后,不进行重试,加快回收“半连接”,不要耗光资源。
不修改这个参数,模拟攻击,10秒后被攻击的80端口即无法服务,机器难以ssh登录; 用命令netstat -na |grep SYN_RECV检测“半连接”hold住180秒;
修改这个参数为0,再模拟攻击,持续10分钟后被攻击的80端口都可以服务,响应稍慢些而已,只是ssh有时也登录不上;检测“半连接”只hold住3秒即释放掉。
修改这个参数为0的副作用:网络状况很差时,如果对方没收到第二个握手包,可能连接服务器失败,但对于一般网站,用户刷新一次页面即可。这些可以在高峰期或网络状况不好时tcpdump抓包验证下。
根据以前的抓包经验,这种情况很少,但为了保险起见,可以只在被tcp洪水攻击时临时启用这个参数。
net.ipv4.tcp_abort_on_overflow = 0
参考:http://www.zgxue.com/165/1653717.html
当 tcp 建立连接的 3 路握手完成后,将连接置入ESTABLISHED 状态并交付给应用程序的 backlog 队列时,会检查 backlog 队列是否已满。若已满,通常行为是将连接还原至 SYN_ACK状态,以造成 3 路握手最后的 ACK 包意外丢失假象 —— 这样在客户端等待超时后可重发 ACK —— 以再次尝试进入ESTABLISHED 状态 —— 作为一种修复/重试机制。如果启用tcp_abort_on_overflow 则在检查到 backlog 队列已满时,直接发 RST 包给客户端终止此连接 —— 此时客户端程序会收到 104Connection reset by peer 错误。
警告:启用此选项可能导致高峰期用户访问体验到 104:Connection reset by peer 或白屏错误(视浏览器而定)。在考虑启用此选项前应先设法优化提高服务端应用程序的性能,使之能更快接管、处理连接。
- 大流量系统配置提高并发能力
echo "net.ipv4.tcp_keepalive_time = 1200" >> /etc/sysctl.conf
echo "net.ipv4.ip_local_port_range = 10000 65000" >> /etc/sysctl.conf
echo "net.ipv4.tcp_max_syn_backlog = 8192" >> /etc/sysctl.conf
echo "net.ipv4.tcp_max_tw_buckets = 5000" >> /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 1200
表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟
net.ipv4.ip_local_port_range = 10000 65000
表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为10000到65000
注意:这里不要将最低值设的太低,否则可能会占用掉正常的端口!
net.ipv4.tcp_max_syn_backlog = 65536
记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。
net.ipv4.tcp_max_tw_buckets = 5000
这个参数是系统同时保持timewait套接字的最大数量。如果超过这个数字,time-wait套接字将立刻被清除并打印警告信息,这个限制仅仅是为了防止简单的DoS攻击。绝对不能过分依靠它或者人为地减小这个值,如果网络实际需要大于缺省值,更应该增加这个值(如果增加了内存之后)。默认为180000,改为5000
注意如此此值过小或者系统网络连接较多时会导致系统(/var/log/messages)报“TCP: time wait bucket table overflow、__ratelimit: 855 callbacks suppressed”错误,此时如果真的是系统负载确实高需要适当调大此数值
查看当前系统配置tcp_max_tw_buckets值
cat /proc/sys/net/ipv4/tcp_max_tw_buckets
查看TIME_WAITE哪个端口占用最大
netstat -n | grep TIME_WAIT | awk -F ' ' '/^tcp/ {print $4}' | sort | uniq -c | sort -brn | head
/proc/sys/net/ipv4/tcp_max_syn_backlog
记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。如果服务器不堪重负,试试提高这个值。注意!如果你设置这个值大于1024,最好同时调整include/net/tcp.h中的TCP_SYNQ_HSIZE,以保证TCP_SYNQ_HSIZE*16 ≤tcp_max_syn_backlog,然后重新编译内核。
此参数过小并且在系统负载较高的情况下,排除恶意流量攻击,则会报“kernel: possible SYN flooding on port”错误
/proc/sys/net/ipv4/tcp_abort_on_overflow
缺省值是0
当守护进程太忙而不能接受新的连接,就象对方发送reset消息,处理不过来干脆就直接拒绝连接了,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。 只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。(对待已经满载的sendmail,apache这类服务的时候,这 个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它)
- 虚拟内存优化
相关参数:swappiness
相关链接:
- http://blog.itpub.net/29371470/viewspace-1250975
- http://bbs.51cto.com/thread-1016268-1.html
- http://blog.csdn.net/woshiwu/article/details/6923463
swappiness的值的大小对如何使用swap分区是有着很大的联系,swappiness=0的时候表示最大限度使用物理内存,然后才是swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面,对于centos的默认设置,这个值等于60,建议修改为20。
可以将值设为0,这并不会禁止你对swap的使用,而是使你的系统对于swap的写入尽可能的少,同时尽可能多的使用你的实际内存,这对于你在切换应用程序时有着巨大的作用,因为这样的话它们是在物理内存而非swap分区中。
查看系统里面的swappiness
cat /proc/sys/vm/swappiness
默认输出为60,也就是说系统内存在使用到100-60=40%的时候(即内存使用到了40%时才用swap区),就开始出现有交换分区的使用。大家知道,内存的速度会比磁盘快很多,这样子会加大系统io,同时造的成大量页的换进换出,严重影响系统的性能,所以我们在操作系统层面,要尽可能使用内存,对该参数进行调整
临时调整的方法如下,我们调成20
sysctl vm.swappiness=20
永久调整的话,需要将在/etc/sysctl.conf修改,加上:
vim /etc/sysctl.conf,添加内容vm.swappiness=20
让sysctl各内核参数生效
sysctl -p
查看所有内核参数
sysctl -a
/etc/security/limits.conf内核参数:
- 打开文件句柄数量优化
文件句柄内核参数设置,默认过小会报“Socket/File: Can’t open so many files”,nginx则会报“Nginx accept() failed (24: Too many open files)” 查看当前系统打开的句柄
lsof | wc -l
优化配置如下:
优化用户最大打开文件数量,缺省是1024,可通过ulimit -n查看
echo "* soft nofile 32768" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
也可以
echo "* - nofile 409600" >> /etc/security/limits.conf
优化用户最大进程数量,可通过ulimit -u查看
echo "* soft nproc 32768" >> /etc/security/limits.conf
echo "* hard nproc 65536" >> /etc/security/limits.conf
也可以
echo "* - nproc 409600" >> /etc/security/limits.conf
- 代表针对所有用户,noproc 是代表最大进程数,nofile 是代表最大文件打开数
也可以针对不同的用户做出不同的限制注意,如下:
echo "@faier soft nproc 32768" >> /etc/security/limits.conf
echo "@root soft nproc 65536" >> /etc/security/limits.conf
这个当中的硬限制是实际的限制,而软限制,是warnning限制,只会做出warning,其实CentOS ulimit命令本身就有分软硬设置,加-H就是硬,加-S就是软默认显示的是软限制
修改完exit退出重新登录就可以见到
注意:如果用* - nproc 409600来配置limits.conf还需要注意参数也受/etc/security/limits.d/90-nproc.conf影响,具体参照 http://blog.csdn.net/kumu_linux/article/details/8301760、http://blog.csdn.net/cnbird2008/article/details/8666620 需要将/etc/security/limits.d/90-nproc.conf改成内容如下
* soft nproc unlimitedroot soft nproc unlimited
显示当前的各种用户进程限制
ulimit -a
查看当前打开进程总数
pstree -p `ps -e | grep faier | awk '{print $1}'` | wc -l
- 内存分配优化
参考:
- https://blog.csdn.net/tiantao2012/article/details/78562612
- https://blog.csdn.net/qq_16097611/article/details/52816908
- https://www.cnblogs.com/aina5626/articles/5335953.html
优化指令:
echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
查看系统申请的总内存:
grep -i commit /proc/meminfo
CommitLimit: 16622124 kBCommitted_AS: 15853564 kB
这里的CommitLimit为当前系统可以申请的总内存,Committed_AS为当前已经申请的内存,记住是申请,因此当你的free查看有很多大量可用的内存的时候,实际Committed_AS可能已经申请了大量的内存。
Overcommit的策略:
Linux下overcommit有三种策略(Documentation/vm/overcommit-accounting):
0:启发式策略,合理的overcommit会被接受,不合理的overcommit会被拒绝
1:任何overcommit都会被接受
2:当系统分配的内存超过swap+N%*物理RAM(N%由vm.overcommit_ratio决定)时,会拒绝commit
有三种方式修改内核参数,但要有root权限:
1、编辑/etc/sysctl.conf ,改vm.overcommit_memory=1,然后sysctl -p使配置文件生效
2、sysctl vm.overcommit_memory=1
3、echo 1 > /proc/sys/vm/overcommit_memory
- Linux IO调度器优化
参考:
I/O调度程序的总结
- 当向设备写入数据块或是从设备读出数据块时,请求都被安置在一个队列中等待完成
- 每个块设备都有它自己的队列
- I/O调度程序负责维护这些队列的顺序,以更有效地利用介质,I/O调度程序将无序的I/O操作变为有序的I/O操作
- 内核必须首先确定队列中一共有多少个请求,然后才开始进行调度
I/O调度的4种算法
- CFQ(完全公平排队I/O调度程序)
特点:
在最新的内核版本和发行版中,都选择CFQ做为默认的I/O调度器,对于通用的服务器也是最好的选择,CFQ试图均匀地分布对I/O带宽的访问,避免进程被饿死并实现较低的延迟,是deadline和as调度器的折中
CFQ对于多媒体应用(video,audio)和桌面系统是最好的选择
CFQ赋予I/O请求一个优先级,而I/O优先级请求独立于进程优先级,高优先级的进程的读写不能自动地继承高的I/O优先级
工作原理:
CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,以此来保证每个进程都能被很好的分配到I/O带宽,I/O调度器每次执行一个进程的4次请求.
- NOOP(适用于SSD固态硬盘)
在新兴的固态硬盘比如SSD、Fusion IO上,最简单的NOOP反而可能是最好的算法,因为其他三个算法的优化是基于缩短寻道时间的,而固态硬盘没有所谓的寻道时间且IO响应时间非常短。
特点:
在Linux2.4或更早的版本的调度程序,那时只有这一种I/O调度算法.
NOOP实现了一个简单的FIFO队列,它像电梯的工作主法一样对I/O请求进行组织,当有一个新的请求到来时,它将请求合并到最近的请求之后,以此来保证请求同一介质
NOOP倾向饿死读而利于写
NOOP对于闪存设备,RAM,嵌入式系统是最好的选择
电梯算法饿死读请求的解释:
因为写请求比读请求更容易
写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中
读请求需要等到它前面所有的读操作完成,才能进行下一次读操作,在读操作之间有几毫秒时间,而写请求在这之间就到来,饿死了后面的读请求
- Deadline(截止时间调度程序)
特点:
通过时间以及硬盘区域进行分类,这个分类和合并要求类似于noop的调度程序
Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限,这样就防止了写操作因为不能被读取而饿死的现象.
Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择
- AS(预料I/O调度程序)
特点:
本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度
可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价
它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量
AS适合于写入较多的环境,比如文件服务器
AS对数据库环境表现很差
调度操作
- 查看当前系统的I/O调度:
cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]
方括号里面的是当前选定的调度策略,当前系统采用的是cfg调度算法
- 修改I/O调度
echo deadline > /sys/block/sda/queue/scheduler
- 永久的更改I/O调度
修改内核引导参数,加入elevator=调度程序名
vim /boot/grub/menu.lst
更改到如下内容:
kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet
- 重启之后,查看调度方法:
cat /sys/block/sda/queue/scheduler
noop anticipatory [deadline] cfq