Yang's Blog

for anything.

nf_conntrack_max 设置的持久化方法

在并发较高的服务器譬如 Nginx 或者 Haproxy上,并设置了 iptables 的 NAT 表,如果没有设置好很容易出现丢包的情况。是真的丢,一丢丢一堆,连 ping 都不通,让人抓狂。仔细查看日志会发现特别多这样的错误:

ip_conntrack: table full, dropping packet.

这个问题的解决方法其实很多,google 一下会发现大把。譬如:

echo "6553500" > /proc/sys/net/nf_conntrack_max

喔!你发现重启后会失效,所以大家推荐把这一行写到 /etc/rc.local 下。

细想一下,你会发现它是在 /proc/sys 下的,所以可以直接写到 /etc/sysctl.conf 下。遂小聪明一刷,我写:

echo "net.nf_conntrack_max=6553600" >> /etc/sysctl.conf
// 可能有很多类似的设置,譬如 net.netfilter.nf_conntrack_max,net.ipv4.ip_conntrack_max 等,设置一个就好了。
//这个是历史遗留问题,等 netfliter 模块发展到一定时候就会统一。 

好我重启,卧槽,为什么没有成功!我看看日志

nf_conntrack version 0.5.0 (7997 buckets, 31988 max)
// 不同电脑的 buckets 都不同。

它竟然没有读 /etc/sysctl.conf 的配置!


其实道理很简单,我们可以首先了解一下 Linux 系统启动流程。一共分为以下几个步骤(我这里就不介绍BIOS等硬件模块了的开机了):

  1. 读内核,从 /boot目录下读取 Linux 内核引导系统;
  2. init 进程初始化系统;
  3. init 读取 /etc/inittab 文件作为入口开始运行系统开机脚本;
  4. 读取 /etc/rcN.d 系统启动项;
  5. 用户登录。

我们着重看 3 和 4 阶段。

在第三阶段,我们顺着 /etc/inittab 的脚本顺藤摸瓜,可以发现它最后调用了 /etc/sysctl.conf。执行文件的顺序是 (Ubuntu 系统为例):

/etc/inittab => /etc/init.d/rc => /etc/init.d/.depend.boot => /etc/init.d/procps => /etc/sysctl.conf

即使我们在 /etc/sysctl.conf 中加上了 net.nf_conntrack_max=6553600,但是我们会可以查到 nf_conntrack_max 实际上是 nf_conntrack 模块的一个参数,而在系统开机的时候这个模块根本没有加载。在运行了 iptables -t nat -L 之后才会被加载。

在第四阶段,为了让 iptables 能够在开机自动启动,我们会装一个包,叫做 iptables-persistent,然后它会创建一个开机脚本,为 /etc/rc3.d/S01iptables-persistent。而此时一旦这个脚本被执行,我们的可爱的 nf_conntrack 终于被加载了。

所以你会发现,你的 /etc/sysctl.conf 虽然执行了,但是你的 iptables 在那个时候根本没有生效,所以你的设置是没有任何意义的。


另外提一下,我们在手动执行 iptables -t nat -L 的时候,我们会发现一个这样的错误:

nf_conntrack version 0.5.0 (7997 buckets, 31988 max)

根据 kernel 的 doc https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt

nf_conntrack_buckets - INTEGER (read-only)
    Size of hash table. If not specified as parameter during module
    loading, the default size is calculated by dividing total memory
    by 16384 to determine the number of buckets but the hash table will
    never have fewer than 32 and limited to 16384 buckets. For systems
    with more than 4GB of memory it will be 65536 buckets.

nf_conntrack_max - INTEGER
    Size of connection tracking table.  Default value is
    nf_conntrack_buckets value * 4.

按照官方文档的说法,64 位且内存大于 4G 的系统,最大应该是可以默认开启 262144 大小的 conntrack, 不过我测试了部分机器,最大也就只能是默认 65536, 不知道是不是 nf_conntrack_max 本身有一个最大值。


所以解决办法非常简单,你只需要把 nf_conntrack 加到系统开机模块,就可以用 /etc/sysctl.conf 开机设置它了,以后也不会爆 table 了:

echo "nf_conntrack" >>  /etc/modules
echo "net.nf_conntrack_max=6553600" >> /etc/sysctl.conf