云服务器 Linux 内核网络参数介绍和常见问题处理
Linux系统内核参数的查看和修改方法
通过/proc/sys/目录
/proc/sys/目录是Linux内核在系统启动后生成的“伪目录”,在该目录下会有一个名为net的文件夹,其中存放着当前系统中生效的所有内核参数,目录树结构与参数的完整名称相关,如net.ipv4.tcp_tw_recycle,它对应的文件即是/proc/sys/net/ipv4/tcp_tw_recycle,文件的内容就是参数值。
查看内核参数
直接使用cat查看对应文件的内容即可,比如要查看net.ipv4.tcp_tw_recycle的值,直接使用以下命令即可:cat /proc/sys/net/ipv4/tcp_tw_recycle
修改内核参数
直接对内核参数对应的文件进行修改,推荐使用echo进行修改,比如要修改net.ipv4.tcp_tw_recycle的值为“0”,直接使用以下命令即可:echo "0" > /proc/sys/net/ipv4/tcp_tw_recycle
注意:使用这种方法修改的参数值仅在本次运行中生效,系统重启后就会恢复成原先的值,修改/proc/sys/net目录的方法一般用于临时性的验证修改的效果,若需要永久性的修改,请使用sysctl命令或修改sysctl.conf文件的方法进行操作。
通过 sysctl.conf 文件
查看内核参数
可以通过命令sysctl -a来查看当前系统中生效的所有参数,命令的运行结果类似以下的输出(部分内容):
net.ipv4.tcp_app_win = 31
net.ipv4.tcp_adv_win_scale = 2
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_frto = 2
net.ipv4.tcp_frto_response = 0
net.ipv4.tcp_low_latency = 0
net.ipv4.tcp_no_metrics_save = 0
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_tso_win_divisor = 3
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_abc = 0
net.ipv4.tcp_mtu_probing = 0
net.ipv4.tcp_base_mss = 512
net.ipv4.tcp_workaround_signed_windows = 0
net.ipv4.tcp_challenge_ack_limit = 1000
net.ipv4.tcp_limit_output_bytes = 262144
net.ipv4.tcp_dma_copybreak = 4096
net.ipv4.tcp_slow_start_after_idle = 1
net.ipv4.cipso_cache_enable = 1
net.ipv4.cipso_cache_bucket_size = 10
net.ipv4.cipso_rbm_optfmt = 0
net.ipv4.cipso_rbm_strictvalid = 1
修改内核参数
可以通过/sbin/sysctl -w kernel.domainname="example.com"来修改指定的参数值,如sysctl -w net.ipv4.tcp_tw_recycle="0"
可以直接修改/etc/sysctl.conf文件,修改将会在下次重启时生效
NAT 哈希表满导致服务器丢包
本节涉及到的内核参数
net.netfilter.nf_conntrack_buckets
net.nf_conntrack_max
问题现象
发现 Linux服务器出现间歇性丢包,连接无法建立,通过 tracert、mtr 等手段排查,外部网络未见异常。
同时,如下图所示,在系统日志中重复出现大量(table full, dropping packet.)错误信息:
Feb 6 16:05:07 i-*** kernel: nf_conntrack: table full, dropping packet.
Feb 6 16:05:07 i-*** kernel: nf_conntrack: table full, dropping packet.
Feb 6 16:05:07 i-*** kernel: nf_conntrack: table full, dropping packet.
Feb 6 16:05:07 i-*** kernel: nf_conntrack: table full, dropping packet.
说明: ip_conntrack 是 Linux 系统内 NAT 的一个跟踪连接条目的模块。ip_conntrack 模块会使用一个哈希表记录 tcp 通讯协议的 established connection 记录,当这个哈希表满了的时候,便会导致 nf_conntrack: table full, dropping packet 错误。
解决思路
Linux系统会开辟一个空间用来维护每一个TCP链接,这个空间的大小与nf_conntrack_buckets、nf_conntrack_max相关,后者的默认值是前者的4倍,而前者在系统启动后无法修改,所以一般都是建议增大nf_conntrack_max。但系统维护连接是需要消耗内存,所以请在确认系统空闲内存充足的情况下调大该值,且调大的具体数值需要根据系统的情况而定。
time wait bucket table overflow 报错
本节涉及到的内核参数
net.ipv4.tcp_max_tw_buckets
问题现象
1、查询服务器 /var/log/message日志,发现全部是类似kernel: TCP: time wait bucket table overflow 的报错信息,提示time wait bucket table溢出,如下:
Feb 18 12:28:38 i-*** kernel: TCP: time wait bucket table overflow
Feb 18 12:28:44 i-*** kernel: printk: 227 messages suppressed.
Feb 18 12:28:44 i-*** kernel: TCP: time wait bucket table overflow
Feb 18 12:28:52 i-*** kernel: printk: 121 messages suppressed.
Feb 18 12:28:52 i-*** kernel: TCP: time wait bucket table overflow
Feb 18 12:28:53 i-*** kernel: printk: 351 messages suppressed.
Feb 18 12:28:53 i-*** kernel: TCP: time wait bucket table overflow
Feb 18 12:28:59 i-*** kernel: printk: 319 messages suppressed.
2、通过netstat -ant|grep TIME_WAIT|wc -l统计处于 TIME_WAIT 状态的 TCP 连接数,可以发现系统中处于TIME_WAIT状态的TCP连接非常多(一般在上千的数量级)
解决思路
该问题和参数net.ipv4.tcp_max_tw_buckets相关,该值可以调整内核中管理TIME_WAIT状态的数量,当实际处于TIME_WAIT及需要转换为TIME_WAIT状态连接数之和超过了net.ipv4.tcp_max_tw_buckets所规定的值的时候,内核就会在message日志中打印这条错误信息,同时超出规定值的TCP连接会直接被关闭,而不会进入TIME_WAIT状态。
解决该问题的方法同样是调高这个内核参数的值,但一味的调高可能会导致内存额外的消耗,我们建议您可以根据实际情况适当调高,同时明确系统中为何会有如此多的TIME_WAIT,可以从业务层面去改进TCP连接的行为。
FIN_WAIT2 状态的 TCP 链接过多
本节涉及到的内核参数
net.ipv4.tcp_fin_timeout
问题现象
在TCP协议标准中,存在“半连接”的概念,FIN_WAIT2的状态就是在本端主动关闭连接,但对端没有主动关闭连接的情况下的状态,而 TCP/IP 协议栈对FIN_WAIT2状态是没有超时的,所以如果远端不关闭,这个 FIN_WAIT_2 状态将保持到系统重新启动,越来越多的FIN_WAIT_2状态会致使内核 crash。
解决思路
因为现如今还使用“半连接”的应用非常少,那么长时间处于FIN_WAIT2状态一般来说是一个非正常的现象,可以适当修改net.ipv4.tcp_fin_timeout参数来调整处于FIN_WAIT2状态的TCP连接的超时时间,减少这个数值以便加快系统回收处于FIN_WAIT2状态的TCP连接。
注意:由于FIN_WAIT2状态的TCP连接会进入TIME_WAIT状态,该问题建议结合“time wait bucket table overflow 报错”这一节一起阅读。
出现大量 CLOSE_WAIT 的原因及解决方法
本节涉及到的内核参数
暂无
问题现象
通过命令netstat -atn|grep CLOSE_WAIT|wc -l查看当前系统中处于CLOSE_WAIT状态的TCP连接非常多。
问题原因
当远端主动关闭连接时,TCP连接断开时需要进行四次挥手,TCP连接的两端都可以发起关闭连接的请求,若对端发起了关闭连接,但本地没有进行后续的关闭连接操作,那么该链接就会处于CLOSE_WAIT状态。虽然该链接已经处于半开状态,但是已经无法和对端通信,需要及时的释放掉该链接。
解决思路
建议从业务层面及时判断某个连接是否已经被对端关闭,即在程序逻辑中对连接及时进行关闭检查。
在编程语言中对应的读、写函数一般已经包含了检测 TCP 连接是否已经断开的功能,如 Java 中可以通过 read 方法来判断,当 read 方法返回 -1 时则表示流已经到达末尾,就可以使用 close 方法关闭该链接。C 语言中检查 read 的返回值,若是 0 则可以关闭该连接,若小于 0 则查看一下 errno,若不是 AGAIN 则同样可以关闭连接。
客户端做了 NAT 的情况下无法访问主机
本节涉及到的内核参数
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_timestamps
问题现象
本地客户端做了NAT(包括VPC内通过SNAT让没有公网的主机可以访问外网),此时访问其他的云产品会出现无法连接的情况,抓包的结果可以看到远端对客户端发送的 SYN 包没有任何响应。
问题原因
若远端服务器中net.ipv4.tcp_tw_recycle和net.ipv4.tcp_timestamps同时被开启(即同时为1),那么服务器会检查每一个报文中的时间戳(TCP协议中Timestamp这个option),若Timestamp不是递增的关系,那么是不会响应这个报文的。而做了 NAT 后,服务器看到来自不同的客户端的源IP都是一样的,但 NAT 前每一台客户端的时间可能会有偏差,在服务器上就会看到 Timestamp 不是递增的情况。
解决思路
分两种情况:
1、远端服务器为云主机
在这种情况下可以将服务器中的net.ipv4.tcp_tw_recycle置为0,只要net.ipv4.tcp_tw_recycle和net.ipv4.tcp_timestamps两个参数不是同时置为1,那么服务器就不会去检查 Timestamp 是否是递增的情况了。
2、远端服务器为虚拟主机等PaaS服务
由于虚拟主无法直接修改内核参数,那么就需要在客户端上做修改,建议将net.ipv4.tcp_tw_recycle和net.ipv4.tcp_timestamps两个参数都置为0,让客户端不要在 TCP 数据段中加入 Timestamp ,从而解决问题。
TCP 连接建立时常见的内核参数
本节涉及到的内核参数
net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_syncookies
net.ipv4.tcp_synack_retries
net.ipv4.tcp_abort_on_overflow
net.core.somaxconn
net.core.netdev_max_backlog
参数说明
前三个参数一般被用来抵御 SYN Flood 攻击,建议您在充分了解参数的实际作用、业务的实际情况来调整这些参数。
net.ipv4.tcp_max_syn_backlog:这个参数决定了系统中处于SYN_RECV状态的TCP连接数量,SYN_RECV状态指的是当系统收到SYN后,作了SYN+ACK响应后等待对方回复三次握手阶段最后一个ACK的阶段。
net.ipv4.tcp_syncookies:当这个参数值被设置为1且SYN_RECV队列满了之后,内核会对 SYN 包的回复做一定的修改:在响应的 SYN+ACK 包中,初始的序列号会由源IP和端口、目的IP和端口及当前时间这五个参数共同计算出一个值组成,恶意攻击者对于linux内核精心组装的 TCP 包不会响应或作错误的响应(ACK包中确认的序列号并不是之前计算出的值),而正常的请求者会根据收到的 SYN+ACK 包做正确的响应,以便达到识别攻击者的功能。注意:当tcp_syncookies启用后,net.ipv4.tcp_max_syn_backlog值会被忽略。
net.ipv4.tcp_synack_retries:这个参数指明了处于SYN_RECV状态时重传 SYN+ACK 包的次数。
net.ipv4.tcp_abort_on_overflow:若设置为1,当系统在短时间内收到了大量的请求,而相关的应用程序未能处理时,就会发送 Reset 包直接终止这些链接。默认值是0,在一般的情况下都建议先通过优化应用程序的效率来提高处理能力,而不是简单的让内核来 Reset 这些过多的连接。
net.core.somaxconn:这个值和net.ipv4.tcp_max_syn_backlog是有关联的,后者指的是还在三次握手的半连接的上限,而这个值指的是处于ESTABLISHED的数量上限。若您的服务器是一个高负载的业务服务器,那么是有必要调高这个数值的。同时请注意在listen(2)函数中有一个参数backlog,这个参数同样是指明这个监听的端口处于ESTABLISHED的数量上限,当backlog>net.core.somaxconn,系统会选择内核中的net.core.somaxconn参数值。
net.core.netdev_max_backlog:当内核处理速度比网卡接收速度慢时,这部分多出来的包就会被保存在网卡的接收队列上,而这个参数说明了这个队列的数量上限。