2019/04/16

nf_conntrack: table full, dropping packet.


记录一个服务器 nf_conntrack: table full, dropping packet. 事故

从 [2019-04-12 20:55:00] 开始 至 [2019-04-12 21:11:34] 在这段时间 sdk服务器出现了用户登录失败,查看程序报错日志发现连接 mysql、redis 超时,使用curl连接失败情况。

查看当时redis和mysql的负载和连接情况都正常,服务器在此段时间有出现并发的连接上升。

翻看 /var/log/message-20190414 发现存在大量 nf_conntrack: table full, dropping packet. 报错。

nf_conntrack 是 connection tracking ,用于追踪 TCP 连接的一个linux内核模块。每一个追踪我们都可以实时在 /proc/net/nf_conntrack 查看到

#cat /proc/net/nf_conntrack |wc -l
55988

# tail -f /proc/net/nf_conntrack

ipv4     2 tcp      6 1 CLOSE src=192.168.1.101 dst=192.168.1.201 sport=48724 dport=80 src=192.168.1.201 dst=192.168.1.101 sport=80 dport=48724 [ASSURED] mark=0 secmark=0 use=2
ipv4     2 tcp      6 38 TIME_WAIT src=192.168.1.201 dst=92.168.1.101 sport=52034 dport=443 src=92.168.1.101 dst=192.168.1.201 sport=443 dport=52034 [ASSURED] mark=0 secmark=0 use=2

可以看到上面的统计,conntrack 跟踪的连接基本状态都已经是 TIME_WAIT ,因为本服务器提供的都是web短连接服务,server端主动断开连接成功后会有个2MLS的 time_wait,这些连接也都被track,该conntrack也会占用2分钟的时间。

比如我们的web每秒并发个100,2MLS的时间2分钟得生成260100=12000,基本上这一分钟的并发会生成1万多的time_wait,conntrack记录。

通过 sysctl net.netfilter.nf_conntrack_count 可以实时观察当前服务器上nf_conntrack跟踪了多少连接。

# sysctl net.netfilter.nf_conntrack_count
net.netfilter.nf_conntrack_count = 52032

查看 nf_conntrack 相关配置参数

# sysctl -a | grep nf_conntrack
net.netfilter.nf_conntrack_generic_timeout = 600
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 432000 #跟踪一个连接最长5天
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120 #time_wait时间 120秒
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_tcp_loose = 1
net.netfilter.nf_conntrack_tcp_be_liberal = 0
net.netfilter.nf_conntrack_tcp_max_retrans = 3
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_acct = 0
net.netfilter.nf_conntrack_events = 1
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_max = 65536 #最大跟踪连接数
net.netfilter.nf_conntrack_count = 52060
net.netfilter.nf_conntrack_buckets = 16384 #哈希表大小
net.netfilter.nf_conntrack_checksum = 1
net.netfilter.nf_conntrack_log_invalid = 0
net.netfilter.nf_conntrack_expect_max = 256
net.nf_conntrack_max = 65536 #最大跟踪连接数

一般默认的 nf_conntrack_max=65535 ,很快就被塞满了。一旦塞满了就会随机的drop包。就会出现 nf_conntrack: table full, dropping packet. 报错。 在外面看来就是丢包情况非常厉害。至此已经定位到问题的原因了。

现在凡是有那么点用户量的服务器跟踪20万以上连接很正常,所以要对服务器的配置参数进行优化。官方给到最优的调整 nf_conntrack_max 公式为:

CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32)
# ARCH为你机器CPU的架构,64或32
# RAMSIZE 为你的内存大小

本服务器是 64位 8核16G centos6.9,套入公式

nf_conntrack_max = 16*1024*1024*1024/16384/2 = 524288
nf_conntrack_buckets = nf_conntrack_max/4 = 131072

优化后配置参数 vi /etc/sysctl.conf

net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
net.netfilter.nf_conntrack_max = 524288
net.nf_conntrack_max = 524288

重启载入配置

sysctl -p

修改 net.netfilter.nf_conntrack_buckets,此参数不能直接填入 sysctl.conf,否则会报错,正确修改方式:

echo 131072 > /sys/module/nf_conntrack/parameters/hashsize

当然线上环境中,如果遇到此问题,可对iptables进行重启,会自动清空nf_conntrack table。

阅读文章
https://my.oschina.net/u/1024767/blog/757465
http://www.10tiao.com/html/488/201701/2247484116/1.html
https://my.oschina.net/zoker/blog/798245
http://yangxg.com/blog/id/1487750393
https://clodfisher.github.io/2018/09/nf_conntrack/

快捷指令
实时查看tcp每个状态数量分布: netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c
实时查看nf_conntrack跟踪了多少连接: sysctl net.netfilter.nf_conntrack_count