记录一个服务器 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://help.aliyun.com/knowledge_detail/41334.html
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