为了防止失控的进程破坏系统的性能,Unix和Linux会跟踪进程使用的大部分资源,并允许用户和系统管理员使用对进程的资源限制,例如控制某个进程打开的系统文件数、对某个用户打开系统进程数进行限制等,一般限制手段包括:软限制和硬限制。
**软限制(soft limit)**是内核实际执行的限制,任何进程都可以将软限制设置为任意小于等于对进程限制的硬限制的值,(noproc)最大线程数和(nofile)文件数;
**硬限制(hard limit)**是可以在任何时候任何进程中设置,但硬限制只能由超级用户修改。
Linux系统一切皆文件,对Linux进行各种操作,其实是对文件进行操作,文件可分为:普通文件、目录文件、链接文件和设备文件。而文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其值一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。
Linux系统默认已经打开的文件描述符包括:STDIN_FILENO 0表示标准输入、STDOUT_FILENO 1表示标准输出、STDERR_FILENO 2表示标准错误输出,默认打开一个新文件,它的文件描述符为3。每个文件描述符与一个打开文件相对应,不同的文件描述符可以指向同一个文件。相同的文件可以被不同的进程打开,也可以在同一个进程中被多次打开。
Linux系统为每个进程维护了一个文件描述符表,该表的值都从0开始的,在不同的进程中你会看到相同的文件描述符,相同文件描述符有可能指向同一个文件,也有可能指向不同的文件。Linux内核对文件操作,维护了3个数据结构概念如下:
进程级的文件描述符表;
系统级的打开文件描述符表;
文件系统的i-node表。
其中进程级的描述符表的每一个条目记录了单个文件描述符的相关信息,例如控制文件描述符操作的一组标志及对打开文件句柄的引用。Linux内核对所有打开的文件都维护了一个系统级的描述符表(open file description table)。将描述符表中的记录行称为打开文件句柄(open file handle),一个打开文件句柄存储了与一个打开文件相关的全部信息,详细信息如下:
当前文件偏移量;
打开文件时所使用的状态标识;
文件访问模式;
与信号驱动相关的设置;
对该文件i-node对象的引用;
文件类型和访问权限;
指针,指向该文件所持有的锁列表;
文件的各种属性。
默认Linux内核对每个用户设置了打开文件最大数为1024,对于高并发网站,是远远不够的,需要将默认值调整到更大,调整方法有两种:
Linux每个用户打开文件最大数临时设置方法,重启服务器该参数无效,命令行终端执行如下命令:
ulimit -n 65535
Linux每个用户打开文件最大数永久设置方法,将如下代码加入内核限制文件/etc/security/limits.conf的末尾:
* soft noproc 65535
* hard noproc 65535
* soft nofile 65535
* hard nofile 65535
如上设置为对每个用户分别设置nofile、noproc最大数,如果需要对Linux整个系统设置文件最大数限制,需要修改/proc/sys/fs/file-max中的值,该值为Linux总文件打开数,例如设置为:echo 3865161233 >/proc/sys/fs/file-max。
Linux /proc/sys目录下存放着多数内核的参数,并且可以在系统运行时进行更改,一般重新启动机器就会失效。而/etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。/proc/sys下内核文件与配置文件sysctl.conf中变量存在着对应关系,即修改sysctl.conf配置文件,其实是修改/proc/sys相关参数,所以对Linux内核优化只需修改/etc/sysctl.conf文件即可。如下为企业生产环境/etc/sysctl.conf内核完整参数:
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 10000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_wmem = 4096 16384 4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144
net.core.somaxconn = 262144
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.ip_local_port_range = 1024 65535
Linux内核常见参数详解:
# 该参数控制RFC 1323 时间戳与窗口缩放选项
net.ipv4.tcp_timestamps = 1
# 选择性应答(SACK)是 TCP 的一项可选特性,可以提高某些网络中所有可用带宽的使用效率
net.ipv4.tcp_sack = 1
# 打开FACK(Forward ACK) 拥塞避免和快速重传功能
net.ipv4.tcp_fack = 1
# 打开重传重组包功能,为0的时候关闭重传重组包功能
net.ipv4.tcp_retrans_collapse = 1
# 对于一个新建连接,内核要发送多少个SYN 连接请求才决定放弃
net.ipv4.tcp_syn_retries = 5
# tcp_synack_retries显示或设定Linux在回应SYN要求时尝试多少次重新发送初始SYN,ACK封包后才决定放弃
net.ipv4.tcp_synack_retries = 5
# 系统所能处理不属于任何进程的TCP sockets最大数量
net.ipv4.tcp_max_orphans = 131072
# 系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息;
默认为180000,设为较小数值此项参数可以控制TIME_WAIT套接字的最大数量,避免服务器被大量的TIME_WAIT套接字拖死
net.ipv4.tcp_max_tw_buckets = 5000
# 如果某个TCP连接在空闲30秒后,内核才发起probe(探查);
net.ipv4.tcp_keepalive_time = 30
# 如果probe 3次(每次3秒既tcp_keepalive_intvl值)不成功,内核才彻底放弃,认为该连接已失效;
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 3
# 放弃回应一个TCP 连接请求前﹐需要进行多少次重试
net.ipv4.tcp_retries1 = 3
# 在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试
net.ipv4.tcp_retries2 = 15
# 表示如果套接字由本端要求关闭,这个参数决定了它保持在 FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout = 30
# 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
# 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数
net.ipv4.tcp_max_syn_backlog = 8192
# TCP建立连接的 3 次握手过程中,当服务端收到最初的 SYN 请求时,会检查应用程序的syn_backlog队列是否已满,启用syncookie,可以解决超高并发时的Can’t Connect` 问题。但是会导致 TIME_WAIT 状态fallback为保持2MSL时间,高峰期时会导致客户端无可复用连接而无法连接服务器
net.ipv4.tcp_syncookies = 1
# 关闭TCP连接之前重试多少次
net.ipv4.tcp_orphan_retries = 0
# net.ipv4.tcp_mem[0]: 低于此值,TCP没有内存压力;
# net.ipv4.tcp_mem[1]: 在此值下,进入内存压力阶段;
# net.ipv4.tcp_mem[2]: 高于此值,TCP拒绝分配socket;
net.ipv4.tcp_mem = 178368 237824 356736
# 表示开启重用,允许将TIME-WAIT sockets重新用于新的TCP连接
net.ipv4.tcp_tw_reuse = 1
# 表示用于向外连接的端口范围
net.ipv4.ip_local_port_range = 1024 65000
# 在内核内存中netfilter可以同时处理的“任务”(连接跟踪条目)
net.ipv4.ip_conntrack_max = 655360
# 开启恶意icmp错误消息保护
net.ipv4.icmp_ignore_bogus_error_responses = 1
# 开启SYN洪水攻击保护
net.ipv4.tcp_syncookies = 1
企业生产环境Linux服务器正常运行,由于某种原因会导致内核报错或者抛出很多信息,根据系统SA可以快速定位Linux服务器故障,Linux内核日志一般存在messages日志中,可以通过命令tail -fn 100 /var/log/messages查看Linux内核日志,如下为Linux内核常见报错日志及生产环境解决报错的方案:
3.1 Linux内核抛出net.ipv4.tcp_max_tw_buckets错误
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
Jun 12 16:33:55 localhost kernel: TCP: time wait bucket table overflow
根据TCP协议定义的3次握手及四次断开连接规定,发起socket主动关闭的一方Socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime)。 如果该值设置过小导致,当系统Time wait数量超过默认设置的值,即会抛出如上的警告信息,需要增加net.ipv4.tcp_max_tw_buckets的值,警告信息消除。
当然也不能设置过大,对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的Socket,甚至比处于Established状态下的Socket多的多,严重影响服务器的处理能力,甚至耗尽可用的Socket而停止服务,TIME_WAIT是TCP协议用以保证被重新分配的Socket不会受到之前残留的延迟重发报文影响的机制,是TCP传输必要的逻辑保证。
3.2 Linux内核抛出Too many open files错误
Benchmarking localhost (be patient)
socket: Too many open files (24)
socket: Too many open files (24)
socket: Too many open files (24)
socket: Too many open files (24)
socket: Too many open files (24)
每个文件描述符与一个打开文件相对应,不同的文件描述符可以指向同一个文件。相同的文件可以被不同的进程打开,也可以在同一个进程中被多次打开。Linux内核对应每个用户打开的文件最大数一般为1024,需要将该值调高满足大并发网站的访问。
Linux每个用户打开文件最大数永久设置方法,将如下代码加入内核限制文件/etc/security/limits.conf的末尾,Exit退出终端,重新登录即生效。
* soft noproc 65535
* hard noproc 65535
* soft nofile 65535
* hard nofile 65535
3.3 Linux内核抛出possible SYN flooding on port 80. Sending cookies错误
Jun 12 16:33:55 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:33:58 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:05 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:15 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:21 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:30 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:37 localhost kernel: possible SYN flooding on port 80. Sending cookies.
Jun 12 16:34:39 localhost kernel: possible SYN flooding on port 80. Sending cookies.
此问题是由于SYN 队列已满,而触发SYN cookies,一般是由于大量的访问,或者恶意访问导致,也称之为SYN Flooding洪水攻击,与DDOS攻击类似。
完整的TCP连接的三次握手,假设一个用户A向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。
一个用户出现异常导致服务器的一个线程等待1分钟并不是什么很大的问题,但如果有一个恶意的攻击者大量模拟这种情况,服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源,数以万计的半连接,即使是简单的保存并遍历也会消耗非常多的CPU时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。
实际上如果服务器的TCP/IP栈不够强大,最后的结果往往是堆栈溢出崩溃,即使服务器端的系统足够强大,服务器端也将忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应,服务器拒绝提供服务,服务器受到了DDOS攻击,这里攻击的手段为DDOS中SYN Flood攻击(SYN洪水攻击)。
防护DDOS攻击有两种手段,一是基于硬件专业防火墙、二是基于Linux内核简单防护,如果攻击流量特别大,单纯配置内核参数是无法抵挡的,还得依靠专业级硬件防火墙,如下为Linux内核防护DDOS优化参数,加入如下代码即可:
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 8000
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
3.4 Linux内核抛出ip_conntrack: table full, dropping packet.错误
Jun 12 16:36:07 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:13 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:34 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:12 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:07 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:13 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:11 localhost kernel: nf_conntrack:table full, dropping packet.
Jun 12 16:36:25 localhost kernel: nf_conntrack:table full, dropping packet.
由于该服务器开启了iptables防火墙,WEB服务器收到了大量的连接,iptables会把所有的连接都做链接跟踪处理,这样iptables就会有一个链接跟踪表,当这个表满的时候,就会出现上面的错误。ip_conntrack是linux NAT的一个跟踪连接条目的模块,ip_conntrack模块会使用一个哈希表记录 tcp 通讯协议的established connection记录。
如果是CentOS6.x系统,需执行:modprobe nf_conntrack命令,然后在内核优化文件中加入如下代码,sysctl –p使其内核文件生效,即可解决该报错:
net.nf_conntrack_max = 655360
net.netfilter.nf_conntrack_tcp_timeout_established = 36000
如果是CentOS5.x系统,需执行:modprobe ip_conntrack命令,然后在内核优化文件中加入如下代码,sysctl –p使其内核文件生效,即可解决该报错:
net.ipv4.ip_conntrack_max = 655350
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 10800
影响企业生产环境Linux服务器性能的因素有很多,一般分为两大类,分别为操作系统层级和应用程序级别。如下为各级别影响性能的具体项及性能评估的标准:
4.1 操作系统级别
内存;
CPU;
磁盘I/O;
网络I/O带宽。
4.2 应用程序及软件
Nginx;
MySQL;
Tomcat;
PHP;
应用程序代码。
4.3 Linux系统性能评估标准如下所示:
影响性能因素 | 好 | 坏 | 糟糕 |
---|---|---|---|
CPU | user% + sys%< 70% | user% + sys%= 85% | user% + sys% >=90% |
内存 | Swap In(si)=0 Swap Out(so)=0 | Per CPU with 10 page/s | More Swap In & Swap Out |
磁盘 | iowait % < 20% | iowait % =35% | iowait % >= 50% |
4.4 Linux系统性能分析工具
常用系统性能分析命令;
vmstat、sar、iostat、netstat、free、ps、top、iftop等;
常用系统性能组合分析命令:
top、uptime 检查系统整体的负载、承受能力;
vmstat、sar、iostat 、top 检测是否是CPU瓶颈;
free、vmstat 检测是否是内存瓶颈;
iostat 检测是否是磁盘I/O瓶颈;
netstat、iftop 检测是否是网络带宽瓶颈
Linux服务器性能评估与优化是一项长期的工作,需要随时关注网站服务器的运行状态,及时作出相应的调整,如下为Linux服务器性能评估及优化方案。
5.1 Linux系统整体性能评估
uptime命令主要用于查看当前服务器整体性能,例如CPU、负载、内存等值的总览,如下为uptime命令应用案例及详解:
[root@localhost ~]# uptime
16:50:05 up 1 min, 1 user, load average: 0.34, 0.14, 0.05
Load average负载有三个值,分别表示:最近1分钟、5分钟、15分钟系统的负载,三个值的大小一般不能大于系统逻辑CPU核数的2倍,例如Linux操作系统有4个逻辑CPU,如果load average的三个值长期大于8时,说明CPU很繁忙,负载很高,可能会影响系统性能,但是偶尔大于8时,可以不用担心,一般不会影响系统性能。
如果load average的输出值小于CPU逻辑个数的2倍,则表示CPU还有空闲的时间片,例如案例中CPU负载为6.22,表示CPU或者服务器是比较空闲的。基于此参数不能完全确认服务器的性能瓶颈,需要借助其他工具进一步判断。
5.2 CPU性能评估
利用vmstat命令监控系统CPU,该命令可以显示关于系统各种资源之间相关性能的简要信息,主要用它来查看CPU负载及队列情况。
如图所示,为vmstat命令在某个系统的输出结果:
Vmstat输出结果详解:
r 列表示运行和等待cpu时间片的进程数,这个值如果长期大于系统CPU的个数,说明CPU不足,需要增加CPU;
b 列表示在等待资源的进程数,比如正在等待I/O、或者内存交换等;
us 列显示了用户进程消耗的CPU 时间百分比。us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期大于50%,就需要考虑优化程序或算法;
sy 列显示了内核进程消耗的CPU时间百分比。Sy的值较高时,说明内核消耗的CPU资源很多;
us+sy的参考值为80%,如果us+sy大于80%说明可能存在CPU资源不足。
利用sar命令监控系统CPU,sar功能很强大,可以对系统的每个方面进行单独的统计,但是使用sar命令会增加系统开销,不过这些开销是可以评估的,对系统的统计结果不会有很大影响。如图所示,为sar命令对某个系统的CPU统计输出:
Sar输出结果详解:
%user 列显示了用户进程消耗的CPU 时间百分比;
%nice 列显示了运行正常进程所消耗的CPU 时间百分比;
%system 列显示了系统进程消耗的CPU时间百分比;
%iowait 列显示了IO等待所占用的CPU时间百分比;
%idle 列显示了CPU处在空闲状态的时间百分比;
%steal 列显示了在内存相对紧张的环境下page in强制对不同的页面进行的steal操作。
5.3 内存性能评估
利用free指令监控内存,free是监控linux内存使用状况最常用的指令,如图所示为服务器内存使用情况:
一般而言,服务器内存可以通过如下方法判断是否空余:
应用程序可用内存/系统物理内存>70%时,表示系统内存资源非常充足,不影响系统性能。
应用程序可用内存/系统物理内存<20%时,表示系统内存资源紧缺,需要增加系统内存,20%<应用程序可用内存/系统物理内存<70%时,表示系统内存资源基本能满足应用需求,暂时不影响系统性能。
5.4 磁盘I/O性能评估
利用iostat评估磁盘性能,监控磁盘IO读写及带宽,如图所示:
Iostat输出结果详解:
Blk_read/s 表示每秒读取的数据块数。
Blk_wrtn/s 表示每秒写入的数据块数。
Blk_read 读入块的总数。
Blk_wrtn 写入块的总数。
kB_read/s 每秒从驱动器读入的数据量,单位为 K。
kB_wrtn/s 每秒向驱动器写入的数据量,单位为 K。
kB_read 读入的数据总量,单位为 K。
kB_wrtn 写入的数据总量,单位为 K。
wrqm/s 将写入请求合并后,每秒发送到设备的写入请求数。
r/s 每秒发送到设备的读入请求数。
w/s 每秒发送到设备的写入请求数。
rsec/s 每秒从设备读入的扇区数。
wsec/s 每秒向设备写入的扇区数。
rkB/s 每秒从设备读入的数据量,单位为 K。
wkB/s 每秒向设备写入的数据量,单位为 K。
avgrq-sz 发送到设备的请求的平均大小,单位是 扇区。
avgqu-sz 发送到设备的请求的平均队列长度。
await I/O 请求平均执行时间,包括发送请求和执行的时间。单位是 毫秒。
svctm 发送到设备的 I/O 请求的平均执行时间。单位是 毫秒。
%util 在 I/O 请求发送到设备期间,占用 CPU 时间的百分比。用于显示设备的带宽利用率。
当这个值接近 100% 时,表示设备带宽已经占满。
可以通过Blk_read/s和Blk_wrtn/s的值对磁盘的读写性能有一个基本的了解,如果Blk_wrtn/s值很大,表示磁盘的写操作很频繁,可以考虑优化磁盘或者优化程序,如果Blk_read/s值很大,表示磁盘直接读取操作很多,可以将读取的数据放入内存中进行操作。
利用sar评估磁盘性能,通过sar -d组合,可以对系统的磁盘IO做一个基本的统计,如图所示:
Sar输出结果详解:
await表示平均每次设备I/O操作的等待时间(以毫秒为单位);
svctm表示平均每次设备I/O操作的服务时间(以毫秒为单位);
%util表示一秒中有百分之几的时间用于I/O操作;
磁盘IO性能,评判标准:正常情况下svctm应该是小于await值的,而svctm的大小和磁盘性能有关,CPU、内存的负荷也会对svctm值造成影响,过多的请求也会间接的导致svctm值的增加。
await值的大小一般取决与svctm的值和I/O队列长度以及I/O请求模式,如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢,此时可以通过更换更快的硬盘来解决问题。
%util项的值也是衡量磁盘I/O的一个重要指标,如果%util接近100%,表示磁盘产生的I/O请求太多,I/O系统已经满负荷的在工作,该磁盘可能存在瓶颈。长期下去,势必影响系统的性能,可以通过优化程序或者通过更换更高、更快的磁盘来解决此问题。
5.5 网络性能评估
通过ping命令检测网络的连通性
通过netstat –i组合检测网络接口状况
通过netstat –r组合检测系统的路由表信息
通过sar -n组合显示系统的网络运行状态
通过iftop -i ens33 查看网卡流量,详细参数如下:
<= 客户端流入的流量;
=> 服务器端流出的流量;
TX 发送流量;
RX 接收流量;
TOTAL 总流量;
Cumm 运行iftop到目前时间的总流量;
peak 流量峰值;
rates 分别表示过去 2s 10s 40s 的平均流量。