nf_conntrack: table full, dropping packet

linux 22-11-11 01:24 8  

查看 netfilter 相关的内核参数: sudo sysctl -a | grep conntrack # 只看超时相关参数(超时时间 = 连接在哈希表里保留的时间) sudo sysctl -a | grep conntrack | grep timeout netfilter模块加载时的bucket和max配置: sudo dmesg | grep conntrack # 找类似这样的记录: # nf_conntrack version 0.5.0 (16384 buckets, 65536 max) 哈希表使用情况: grep conntrack /proc/slabinfo # 前4个数字分别为: # 当前活动对象数、可用对象总数、每个对象的大小(字节)、包含至少1个活动对象的分页数 当前跟踪的连接数: sudo sysctl net.netfilter.nf_conntrack_count # 或 cat /proc/net/nf_conntrack | wc -l 跟踪的每个连接的详情: cat /proc/net/nf_conntrack # 统计里面的TCP连接的各状态和条数 cat /proc/net/nf_conntrack | awk '/^.*tcp.*$/ {count[$6]++} END {for(state in count) print state, count[state]}' # 记录数最多的10个ip cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10 # 记录格式: # 网络层协议名、网络层协议编号、传输层协议名、传输层协议编号、记录失效前剩余秒数、连接状态(不是所有协议都有) # 之后都是key=value或flag格式,1行里最多2个同名key(如 src 和 dst),第1次出现的来自请求,第2次出现的来自响应 # flag: # [ASSURED] 请求和响应都有流量 # [UNREPLIED] 没收到响应,哈希表满的时候这些连接先扔掉 stackoverflow - details of /proc/net/ip_conntrack / nf_conntrack 常用参数说明 # 哈希表里的实时连接跟踪数(只读) net.netfilter.nf_conntrack_count # 值跟 /proc/net/nf_conntrack 的行数一致 有说法是这数字持续超过 nf_conntrack_max 的 20% 就该考虑调高上限了。 # 哈希表大小(只读)(64位系统、8G内存默认 65536,16G翻倍,如此类推) net.netfilter.nf_conntrack_buckets # 最大跟踪连接数,默认 nf_conntrack_buckets * 4 net.netfilter.nf_conntrack_max net.nf_conntrack_max # 跟踪的连接用哈希表存储,每个桶(bucket)里都是1个链表,默认长度为4 # 默认值参考以下公式:(使用内存的 1/16384) # CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32) # (ARCH为你机器CPU的架构,64或32) # HASHSIZE = CONNTRACK_MAX / 4 # (N年前是除8,这数字就是每个桶里的链表长度) 现在凡是有那么点用户量的服务器跟踪20万以上连接很正常,真按系统默认值也勉强能用,但阿里云似乎设了特别保守的默认值,bucket为 16384,max为 65536,这是倒退回了07-11年CentOS 5-6的时代。 # netfilter的哈希表存储在内核空间,这部分内存不能swap # 操作系统为了兼容32位,默认值往往比较保守: # 在32位Linux下,内核空间的虚拟地址空间最多 1G,通常能用的只有前 896M # 1条跟踪记录约 300 字节,给netfilter分配太多地址空间可能会导致其他内核进程不够分配,因此当年默认最多 65535 条,占 20多MB # 64位系统内核空间最多能用虚拟地址空间的一半(128TB),只需要关心物理内存使用多少就行了 # 内存占用参考以下公式: # size_of_mem_used_by_conntrack (in bytes) = CONNTRACK_MAX * sizeof(struct ip_conntrack) + HASHSIZE * sizeof(struct list_head) # sizeof(struct ip_conntrack) 在不同架构、内核版本、编译选项下不一样,192~352字节之间,可以按 352 算 # sizeof(struct list_head) = 2 * size_of_a_pointer(32位系统是4字节,64位是8字节) # 在64位下,当CONNTRACK_MAX为 1048576,HASHSIZE 为 262144 时,最多占350多MB # 对现在的机器来说毫无压力 推荐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 秒 # 主动方的最后1个状态,默认2MSL net.netfilter.nf_conntrack_tcp_timeout_close_wait # 默认 60 秒 # CLOSE_WAIT是被动方收到FIN发ACK,然后会转到LAST_ACK发FIN,除非程序写得有问题,正常来说这状态持续时间很短。 # (我们服务器 nf_conntrack文件里 time_wait 占了99% # 把time_wait超时改成 30 秒后,nf_conntrack_count下降超过一半) net.netfilter.nf_conntrack_tcp_timeout_established # 默认 432000 秒(5天) # 理论上不用这么长,不小于 net.ipv4.tcp_keepalive_time 就行了 # (我们调了看不出效果) net.netfilter.nf_conntrack_generic_timeout # 默认 600 秒(10分钟) # 通用超时设置,作用于4层(传输层)未知或不支持的协议 # (基本不会碰到这种连接,同样调了看不出效果) #net.netfilter.nf_conntrack_tcp_timeout_max_retrans # 默认 300 秒 #net.netfilter.nf_conntrack_tcp_timeout_unacknowledged # 默认 300 秒 调优 A. 调整内核参数 如果不能关掉防火墙,基本思路就是上面说的,调大nf_conntrack_buckets和nf_conntrack_max,调小超时时间。 除了有关联的参数,尽量一次只改一处,记下默认值,效果不明显或更差就还原。 # net.netfilter.nf_conntrack_buckets 不能直接改(报错) # 需要修改模块的设置: echo 262144 > /sys/module/nf_conntrack/parameters/hashsize # 如果不是root: echo 262144 | sudo tee /sys/module/nf_conntrack/parameters/hashsize # 再查看,bucket已经变成设置的大小 sudo sysctl net.netfilter.nf_conntrack_buckets # max设为桶的4倍 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 # 不传配置文件路径默认加载 /etc/sysctl.conf B. 关闭防火墙 对不直接暴露在公网、没有用到NAT转发的服务器来说,关闭Linux防火墙是最简单也是最佳的办法。 通常防火墙一关,sysctl -a里就没有netfilter相关的参数了。如果有例外,照上面调整。 # CentOS 7.x sudo systemctl stop firewalld sudo systemctl disable firewalld # CentOS 6.x sudo service iptables stop # 网上有些文章说关了iptables之后,用 iptables -L -n 之类查看规则也会导致nf_conntrack重新加载,实测并不会 sudo chkconfig --del iptables 【注意】以下是网上有些文章提到的解决方法,其实不好用,只记录下来作备忘。 C. 设置不跟踪连接的规则(不推荐) 对需要防火墙的机器,可以在iptables设置NOTRACK规则,减少要跟踪的连接数 【注意】设置成不跟踪的连接无法拿到状态,可能会导致keep-alive用不了: 我们改之前 ESTAB 的连接1000多,改之后超过1.5w,响应时间几十秒,前端基本连不上。 # 查看所有规则 sudo iptables-save # 这个必须插在第1条,凡是不跟踪的肯定是你想放行的 sudo iptables -I INPUT 1 -m state --state UNTRACKED -j ACCEPT # 设置成不跟踪的连接无法拿到状态,包含状态(-m state --state)的规则统统失效 # iptables处理规则的顺序是从上到下,如果这条加的位置不对,可能导致请求无法通过防火墙 # 不跟踪 127.0.0.1 sudo iptables -t raw -A PREROUTING -i lo -j NOTRACK sudo iptables -t raw -A OUTPUT -o lo -j NOTRACK # 保存规则(否则重启服务后失效) sudo service iptables save # 其实就是把 iptables-save 的内容存到 /etc/sysconfig/iptables # 假如Nginx和应用部署在同一台机子上,增加这规则的收益极为明显 # Nginx连各种upstream使得连接数起码翻了倍,不跟踪本地连接一下干掉一大半 # (其他条件不变,修改前后 nf_conntrack_count:30k+ -> 2.9k+ ,下降 90%!) # sudo iptables -t raw -A PREROUTING -p tcp -m multiport --dports 80,443 -j NOTRACK # sudo iptables -t raw -A OUTPUT -p tcp -m multiport --sports 80,443 -j NOTRACK # (实测少跟踪 7% 左右的链接) 说明: -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" # 查看iptables规则 sudo iptables-save # 把带 -t nat 、-m state 的规则都干掉 # 或删掉 /etc/sysconfig/iptables 里相应内容 # 编辑 iptables 配置文件 sudo vim /etc/sysconfig/iptables-config # 找到 IPTABLES_MODULES ,删掉跟conntrack有关的模块(如果有) # 停掉iptables 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 # 需要再开就 sudo modprobe <name>