概览

Linux服务器内核优化,包括优化系统环境、内核和网络,相关内核优化脚本可参考centos_init.sh

/etc/sysctl.conf内核参数:

  1. 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

  1. 高并发情况下系统同时处理的“任务”优化

相关参数: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
  1. 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状态的连接数量

  1. 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 或白屏错误(视浏览器而定)。在考虑启用此选项前应先设法优化提高服务端应用程序的性能,使之能更快接管、处理连接。

  1. 大流量系统配置提高并发能力
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这类服务的时候,这 个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它)

  1. 虚拟内存优化

相关参数:swappiness

相关链接:

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内核参数:

  1. 打开文件句柄数量优化

文件句柄内核参数设置,默认过小会报“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
  1. 内存分配优化

参考:

优化指令:

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

  1. Linux IO调度器优化

参考:

I/O调度程序的总结

  1. 当向设备写入数据块或是从设备读出数据块时,请求都被安置在一个队列中等待完成
  2. 每个块设备都有它自己的队列
  3. I/O调度程序负责维护这些队列的顺序,以更有效地利用介质,I/O调度程序将无序的I/O操作变为有序的I/O操作
  4. 内核必须首先确定队列中一共有多少个请求,然后才开始进行调度

I/O调度的4种算法

  1. 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次请求.

  1. NOOP(适用于SSD固态硬盘)

在新兴的固态硬盘比如SSD、Fusion IO上,最简单的NOOP反而可能是最好的算法,因为其他三个算法的优化是基于缩短寻道时间的,而固态硬盘没有所谓的寻道时间且IO响应时间非常短。

特点:

在Linux2.4或更早的版本的调度程序,那时只有这一种I/O调度算法.

NOOP实现了一个简单的FIFO队列,它像电梯的工作主法一样对I/O请求进行组织,当有一个新的请求到来时,它将请求合并到最近的请求之后,以此来保证请求同一介质

NOOP倾向饿死读而利于写

NOOP对于闪存设备,RAM,嵌入式系统是最好的选择

电梯算法饿死读请求的解释:

因为写请求比读请求更容易

写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中

读请求需要等到它前面所有的读操作完成,才能进行下一次读操作,在读操作之间有几毫秒时间,而写请求在这之间就到来,饿死了后面的读请求

  1. Deadline(截止时间调度程序)

特点:

通过时间以及硬盘区域进行分类,这个分类和合并要求类似于noop的调度程序

Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限,这样就防止了写操作因为不能被读取而饿死的现象.

Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择

  1. AS(预料I/O调度程序)

特点:

本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度

可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价

它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量

AS适合于写入较多的环境,比如文件服务器

AS对数据库环境表现很差

调度操作

  1. 查看当前系统的I/O调度:
cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]

方括号里面的是当前选定的调度策略,当前系统采用的是cfg调度算法

  1. 修改I/O调度
echo deadline > /sys/block/sda/queue/scheduler
  1. 永久的更改I/O调度

修改内核引导参数,加入elevator=调度程序名

vim /boot/grub/menu.lst

更改到如下内容:

kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet
  1. 重启之后,查看调度方法:
cat /sys/block/sda/queue/scheduler 
noop anticipatory [deadline] cfq