nf_conntrack 调优

netfilter

netfilter是linux内在的一个软件框架,用来管理网络数据包。

netfilter提供了5个hook来进行管理网络包。如下图:

netfilter-hooks

  • 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需要保存这些信息在它自己的数据结构中。其数据结构如下:

connection-tracking-structure

它是一个开链的哈希表,链表是一个双向表。每个哈希节点称为一个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表满的提示。

监控图一般会有以下的表现:

qps

dmesg可以看到以下的输出:

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 = 262144nf_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

标签: none

添加新评论