(服务器用的阿里云主机,CentOS 7.3,彷佛无论内存多少阿里云都把 conntrack_max 设成 65536)php
症状
CentOS服务器,负载正常,但请求大量超时,服务器/应用访问日志看不到相关请求记录。前端
在dmesg或/var/log/messages看到大量如下记录:vim
kernel: nf_conntrack: table full, dropping packet.服务器
缘由
服务器访问量大,内核netfilter模块conntrack相关参数配置不合理,致使新链接被drop掉。网络
详细
nf_conntrack模块在kernel 2.6.15(2006-01-03发布) 被引入,支持ipv4和ipv6,取代只支持ipv4的ip_connktrack,用于跟踪链接的状态,供其余模块使用。架构
最多见的使用场景是 iptables 的 nat 和 state 模块:tcp
nat 根据转发规则修改IP包的源/目标地址,靠nf_conntrack的记录才能让返回的包能路由到发请求的机器。
state 直接用 nf_conntrack 记录的链接状态(NEW/ESTABLISHED/RELATED/INVALID)来匹配防火墙过滤规则。
iptables性能
nf_conntrack用1个哈希表记录已创建的链接,包括其余机器到本机、本机到其余机器、本机到本机(例如 ping 127.0.0.1 也会被跟踪)。测试
若是链接进来比释放的快,把哈希表塞满了,新链接的数据包会被丢掉,此时netfilter变成了一个黑洞,致使拒绝服务。 这发生在3层(网络层),应用程序毫无办法。阿里云
各发行版区别:
CentOS (7.3) 默认加载该模块
Ubuntu (16.10+) 和 Kali Linux (2016.1+) 默认不加载,不会有这问题
查看
netfilter 相关的内核参数:
sudo sysctl -a | grep conntrack
sudo sysctl -a | grep conntrack | grep timeout
netfilter模块加载时的bucket和max配置:
sudo dmesg | grep conntrack
哈希表使用状况:
grep conntrack /proc/slabinfo
当前跟踪的链接数:
sudo sysctl net.netfilter.nf_conntrack_count
跟踪的每一个链接的详情:
cat /proc/net/nf_conntrack
cat /proc/net/nf_conntrack | awk '/^.tcp.$/ {count[$6]++} END {for(state in count) print state, count[state]}'
cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10
stackoverflow - details of /proc/net/ip_conntrack / nf_conntrack
经常使用参数说明
net.netfilter.nf_conntrack_count
有说法是这数字持续超过 nf_conntrack_max 的 20% 就该考虑调高上限了。
net.netfilter.nf_conntrack_buckets
net.netfilter.nf_conntrack_max
net.nf_conntrack_max
如今凡有那么点用户量的服务器跟踪20万以上链接很正常,真按系统默认值也勉强能用,但阿里云彷佛设了特别保守的默认值,bucket为 16384,max为 65536,这是倒退回了07-11年CentOS 5-6的时代。
推荐bucket至少 262144,max至少 1048576,不够再继续加
https://wiki.khnet.info/index.php/Conntrack_tuning, 2008-01
缩短超时时间可让netfilter更快地把跟踪的记录从哈希表里移除。
调优的基本思路是先看 /proc/net/nf_conntrack ,哪一种协议哪一种状态的链接最多,改小对应的超时参数
。
注意要充分测试,确保不影响业务。
net.netfilter.nf_conntrack_tcp_timeout_fin_wait # 默认 120 秒
net.netfilter.nf_conntrack_tcp_timeout_time_wait # 默认 120 秒
net.netfilter.nf_conntrack_tcp_timeout_close_wait # 默认 60 秒
net.netfilter.nf_conntrack_tcp_timeout_established # 默认 432000 秒(5天)
net.netfilter.nf_conntrack_generic_timeout # 默认 600 秒(10分钟)
#net.netfilter.nf_conntrack_tcp_timeout_max_retrans # 默认 300 秒
#net.netfilter.nf_conntrack_tcp_timeout_unacknowledged # 默认 300 秒
调优
A. 调整内核参数
若是不能关掉防火墙,基本思路就是上面说的,调大nf_conntrack_buckets和nf_conntrack_max,调小超时时间。
除了有关联的参数,尽可能一次只改一处,记下默认值,效果不明显或更差就还原。
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
echo 262144 | sudo tee /sys/module/nf_conntrack/parameters/hashsize
sudo sysctl net.netfilter.nf_conntrack_buckets
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576
suod sysctl -w net.nf_conntrack_max=1048576
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=15
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300
用sysctl -w或echo xxx > /pro/sys/net/netfilter/xxx作的修改在重启后会失效。
若是测试过没问题,能够编辑/etc/sysctl.d/下的配置文件(旧系统是/etc/sysctl.conf),系统启动时会加载里面的设置。
sudo vim /etc/sysctl.d/90-conntrack.conf
net.netfilter.nf_conntrack_max=1048576
net.nf_conntrack_max=1048576
net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
net.netfilter.nf_conntrack_tcp_timeout_close_wait=15
net.netfilter.nf_conntrack_tcp_timeout_established=300
sudo sysctl -p /etc/sysctl.d/90-conntrack.conf
B. 关闭防火墙
对不直接暴露在公网、没有用到NAT转发的服务器来讲,关闭Linux防火墙是最简单也是最佳的办法。
一般防火墙一关,sysctl -a里就没有netfilter相关的参数了。若是有例外,照上面调整。
sudo systemctl stop firewalld
sudo systemctl disable firewalld
sudo service iptables stop
sudo chkconfig --del iptables
【注意】如下是网上有些文章提到的解决方法,其实很差用,只记录下来做备忘。
C. 设置不跟踪链接的规则(不推荐)
对须要防火墙的机器,能够在iptables设置NOTRACK规则,减小要跟踪的链接数
【注意】设置成不跟踪的链接没法拿到状态,可能会致使keep-alive用不了:
咱们改以前 ESTAB 的链接1000多,改以后超过1.5w,响应时间几十秒,前端基本连不上。
sudo iptables-save
sudo iptables -I INPUT 1 -m state --state UNTRACKED -j ACCEPT
sudo iptables -t raw -A PREROUTING -i lo -j NOTRACK
sudo iptables -t raw -A OUTPUT -o lo -j NOTRACK
sudo service iptables save
说明:
-t raw 会加载 iptable_raw 模块(kernel 2.6+ 都有)
raw表基本就干一件事,经过-j NOTRACK给不须要被链接跟踪的包打标记(UNTRACKED状态),告诉nf_conntrack不要跟踪链接
raw 的优先级大于 filter,mangle,nat,包含 PREROUTING(针对进入本机的包) 和 OUTPUT(针对从本机出去的包) 链
缺点:很差维护,服务器对外端口较多或有变化时,容易改出问题
D. 禁用相关模块(不推荐)
只要iptables还有规则用到nat和state模块,就不适合关掉netfilter,不然这些规则会失效。
例如这条默认规则(一般写在第1条或很靠前的位置):
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
表示放行已经创建的链接,再也不往下匹配其余规则(第一次创建时已经所有检查过关了)。关掉netfilter会拿不到状态,致使每一个请求都要从头至尾检查一次,影响性能。
所以若是iptables不能关,最好不要禁用netfilter。
若是确实须要禁用:
sudo lsmod | egrep "ip_table|iptable|nat|conntrack"
sudo iptables-save
sudo vim /etc/sysconfig/iptables-config
sudo service iptables stop
sudo chkconfig --del iptables
sudo rmmod iptable_nat
sudo rmmod ip6table_nat
sudo rmmod nf_defrag_ipv4
sudo rmmod nf_defrag_ipv6
sudo rmmod nf_nat
sudo rmmod nf_nat_ipv4
sudo rmmod nf_nat_ipv6
sudo rmmod nf_conntrack
sudo rmmod nf_conntrack_ipv4
sudo rmmod nf_conntrack_ipv6
sudo rmmod xt_conntrack
缺点:若是环境/配置文件不是彻底由你掌控或没有很好的管理,容易出问题