nf_conntrack 调优
nf_conntrack 调优
netfilter
netfilter是linux内在的一个软件框架,用来管理网络数据包。
netfilter提供了5个hook来进行管理网络包。如下图:
- PREROUTING, 所有包都会经过这个hook
- LOCAL INPUT, 进入本机的包会经过这个hook
- FORWARD, 不进入本机的包,做转发的包会经过这个hook
- LOCAL OUTPUT, 从本机出去的包会经过这个hook
- POSTROUTING, 所有出去的包都会经过这个hook
netfilter进行包的管理,则需要记录每个连接的状态信息。这就是nf_conntrack的工作了。
nf_conntrack
nf_conntrack是netfilter的一个子系统。它记录了每个连接的状态信息。
nf_conntrack记录的信息包括,源ip、端口,目标ip、端口,连接状态,协议等。
连接状态包含以下几种:
- NEW, 新创建的连接,发起连接方发出包后,还没收到回包,都处理这种状态。
- ESTABLISHED, 已建立的连接,发起连接后,收到回包,这时处理已连接状态。
- RELATED, 与其他连接相关联,其他的连接与此连接有关联。如ftp的控制连接和数据连接。
- INVALID, 非法的连接,比如包的行为不合法。
nf_conntrack需要保存这些信息在它自己的数据结构中。其数据结构如下:
它是一个开链的哈希表,链表是一个双向表。每个哈希节点称为一个bucket,计算出同样哈希值的连接放到链表里连起来。 每个节点记录了请求方向、响应方向的消息。
哈希表的大小,也就是说哈希表的节点数,由nf_conntrack_buckets配置。
nf_conntrack能跟踪的最大连接数由nf_conntrack_max配置。
nf_conntrack默认值
CentOS6.10 x86_64
系统内存小于1GiB时
- nf_conntrack_buckets=RAMSIZE (in bytes) / 16384 / (ARCH / 32)
- nf_conntrack_max=nf_conntrack_buckets * 4
系统内存大于1GiB时
- nf_conntrack_buckets=16384
- nf_conntrack_max=65536
CentOS7.9 x86_64
系统内存小于1GiB时
- nf_conntrack_buckets=RAMSIZE (in bytes) / 16384 / (ARCH / 32)
- nf_conntrack_max=nf_conntrack_buckets * 4
系统内存大于1GiB且小于4GiB
- nf_conntrack_buckets=16384
- nf_conntrack_max=65536
系统内存大于4GiB
- nf_conntrack_buckets=65536
- nf_conntrack_max=262144
备注:
- ARCH是指操作系统位数,取值32或64
- 这里的系统内存不是指物理机或虚拟机所分配的内存,而是系统能识别的内存总量,也就是通过free命令查看到的总内存。
nf_conntrack表满的表现
默认值在大并发场景时可能会引起nf_conntrack被占满而导致服务不可用。当nf_conntrack满的时候,新连接过来就会直接被netfilter丢掉,导致连接不上。 在监控上看,可以看到脉冲式的请求。另外我们可以用dmesg命令或查看/var/log/messages看到系统的日志显示nf_conntrack表满的提示。
监控图一般会有以下的表现:
dmesg可以看到以下的输出:
什么情况下要调优
当出现以下一种或几种情形,应考虑调整nf_conntrack的相关参数
- 系统启用了iptables或firewalld等使用了netfilter模块的软件,并且系统需要承载大量并发请求
- nf_conntrack_count / nf_conntrack_buckets > 0.7,这时哈希表大部分桶不为空,哈希冲突的概率会增大,性能从 O(1) 退化为读链表的 O(n),建议及时扩容
- 明确发现nf_conntrack满时,应及时扩容
调优方向:
- 禁用netfilter模块
- 哈希表扩容(nf_conntrack_buckets、nf_conntrack_max)
- 让里面的元素尽快释放(超时相关参数)
- 不追踪连接(iptables规则设置NOTRACK选项)
nf_conntrack设置
每个bucket的平均链表长度为bucket_len = nf_conntrack_max / nf_conntrack_buckets
。系统默认是4。 每个数据包的链表查询时间复杂度为bucket_len。当bucket_len太大时,则每个数据包要花太多时间在链接的查找上。 所以,我们不能把bucket_len配得太长。按照linux的默认配置,nf_conntrack_buckets 与 nf_conntrack_max 的值比为 1:4 即可。
过大的nf_conntrack会消耗一定量的内存,其计算公式在下面一节中给出。
对于内存还算充裕的服务器而言,通常建议设置为:
nf_conntrack_buckets = 262144
nf_conntrack_max = 1048576
nf_conntrack_buckets
立即生效:
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize
该方法已在CentOS6/CentOS7上测试通过,重启失效
永久生效:
方法1:
cat > /etc/sysconfig/modules/nf_conntrack_hashsize.modules << "EOF" #!/bin/sh exec /sbin/modprobe nf_conntrack hashsize=262144 EOF chmod +x /etc/sysconfig/modules/nf_conntrack_hashsize.modules
该方法已在CentOS6/CentOS7上测试通过,重启生效方法2:
cat > /etc/modprobe.d/nf_conntrack_hashsize.conf << "EOF" options nf_conntrack hashsize = 262144 EOF
该方法在CentOS6/CentOS7上测试不通过,nf_conntrack模块无法加载
nf_conntrack_max
立即生效:
sysctl -w net.netfilter.nf_conntrack_max=1048576
永久生效:
将net.netfilter.nf_conntrack_max=1048576
写入/etc/sysctl.conf 或/etc/sysctl.d/xxx.conf
nf_conntrack内存占用计算
计算公式:
total_mem_used(bytes) = conntrack_max * sizeof(struct ip_conntrack) + conntrack_buckets * sizeof(struct list_head)
其中,sizeof(struct ip_conntrack)
,sizeof(struct list_head)
的值在不同的发行版可能会不一样,可以使用以下python命令获得。
import ctypes
LIBNETFILTER_CONNTRACK = '/usr/lib64/libnetfilter_conntrack.so.3.6.0' #这个是nf_conntrack的动态库所在路径
nfct = ctypes.CDLL(LIBNETFILTER_CONNTRACK)
print 'sizeof(struct nf_conntrack):', nfct.nfct_maxsize()
print 'sizeof(struct list_head):', ctypes.sizeof(ctypes.c_void_p) * 2
以CentOS6和CentOS7为例,计算当设置了nf_conntrack_buckets = 262144
和nf_conntrack_max = 1048576
时最多会占用多少内存。
执行上面的python命令得到:
CentOS6 x86_64
- sizeof(struct nf_conntrack): 296
- sizeof(struct list_head): 16
CentOS7 x86_64
- sizeof(struct nf_conntrack): 376
- sizeof(struct list_head): 16
计算结果:
CentOS6 x86_64
1048576 * 296 + 262144 * 16 = 314572800 (bytes)
即 300MiB
CentOS7 x86_64
1048576 * 376 + 262144 * 16 = 314572800 (bytes)
即 380MiB
请教一下楼主:
net.netfilter.nf_conntrack_max=1048576写入/etc/sysctl.conf 或/etc/sysctl.d/xxx.conf
这个我也是写入的,但是我重启机器或者防火墙后,就失效了,是为什么呢?
写入方法:
sed -i '/net.nf_conntrack_max/d' /etc/sysctl.conf;echo "net.nf_conntrack_max=2621440" >> /etc/sysctl.conf; >> /etc/sysctl.conf&&sysctl -p
按道理,重启防火墙是不会修改这些内核参数的,不知你是怎么重启防火墙的呢?